gpt4 book ai didi

c# - 不安全的指针迭代和位图——为什么 UInt64 更快?

转载 作者:太空狗 更新时间:2023-10-29 23:03:01 24 4
gpt4 key购买 nike

我一直在做一些不安全的位图操作,并且发现增加指针的次数更少可以带来一些大的性能改进。我不确定为什么会这样,即使您在循环中执行了更多的按位运算,但对指针执行的迭代次数越少越好。

因此,例如,不是使用 UInt32 迭代 32 位像素,而是使用 UInt64 迭代两个像素,并在一个周期内执行两次操作。

下面是通过读取两个像素并修改它们来实现的(当然对于奇数宽度的图像它会失败,但它只是为了测试)。

    private void removeBlueWithTwoPixelIteration()
{
// think of a big image with data
Bitmap bmp = new Bitmap(15000, 15000, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
TimeSpan startTime, endTime;

unsafe {

UInt64 doublePixel;
UInt32 pixel1;
UInt32 pixel2;

const int readSize = sizeof(UInt64);
const UInt64 rightHalf = UInt32.MaxValue;

PerformanceCounter pf = new PerformanceCounter("System", "System Up Time"); pf.NextValue();

BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
byte* image = (byte*)bd.Scan0.ToPointer();

startTime = TimeSpan.FromSeconds(pf.NextValue());

for (byte* line = image; line < image + bd.Stride * bd.Height; line += bd.Stride)
{
for (var pointer = line; pointer < line + bd.Stride; pointer += readSize)
{
doublePixel = *((UInt64*)pointer);
pixel1 = (UInt32)(doublePixel >> (readSize * 8 / 2)) >> 8; // loose last 8 bits (Blue color)
pixel2 = (UInt32)(doublePixel & rightHalf) >> 8; // loose last 8 bits (Blue color)
*((UInt32*)pointer) = pixel1 << 8; // putback but shift so A R G get back to original positions
*((UInt32*)pointer + 1) = pixel2 << 8; // putback but shift so A R G get back to original positions
}
}

endTime = TimeSpan.FromSeconds(pf.NextValue());

bmp.UnlockBits(bd);
bmp.Dispose();

}

MessageBox.Show((endTime - startTime).TotalMilliseconds.ToString());

}

以下代码逐个像素地执行,并且比之前的代码慢 70% 左右:

    private void removeBlueWithSinglePixelIteration()
{
// think of a big image with data
Bitmap bmp = new Bitmap(15000, 15000, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
TimeSpan startTime, endTime;

unsafe
{

UInt32 singlePixel;

const int readSize = sizeof(UInt32);

PerformanceCounter pf = new PerformanceCounter("System", "System Up Time"); pf.NextValue();

BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
byte* image = (byte*)bd.Scan0.ToPointer();

startTime = TimeSpan.FromSeconds(pf.NextValue());

for (byte* line = image; line < image + bd.Stride * bd.Height; line += bd.Stride)
{
for (var pointer = line; pointer < line + bd.Stride; pointer += readSize)
{
singlePixel = *((UInt32*)pointer) >> 8; // loose B
*((UInt32*)pointer) = singlePixel << 8; // adjust A R G back
}
}

endTime = TimeSpan.FromSeconds(pf.NextValue());

bmp.UnlockBits(bd);
bmp.Dispose();

}

MessageBox.Show((endTime - startTime).TotalMilliseconds.ToString());
}

有人可以澄清为什么递增指针的操作比执行一些按位操作的成本更高吗?

我正在使用 .NET 4 框架。

对于 C++,这样的事情会成立吗?

注意。 32 位与 64 位两种方法的比率相等,但是在 64 位和 32 位上这两种方法都慢了 20%?

编辑:正如 Porges 和 arul 所建议的,这可能是因为减少了内存读取次数和分支开销。

编辑2:

经过一些测试,答案似乎是从内存中读取更少的时间:

使用这段代码,假设图像宽度可以被 5 整除,你的速度会提高 400%:

[StructLayout(LayoutKind.Sequential,Pack = 1)]
struct PixelContainer {
public UInt32 pixel1;
public UInt32 pixel2;
public UInt32 pixel3;
public UInt32 pixel4;
public UInt32 pixel5;
}

然后使用这个:

            int readSize = sizeof(PixelContainer);

// .....

for (var pointer = line; pointer < line + bd.Stride; pointer += readSize)
{
multiPixel = *((PixelContainer*)pointer);
multiPixel.pixel1 &= 0xFFFFFF00u;
multiPixel.pixel2 &= 0xFFFFFF00u;
multiPixel.pixel3 &= 0xFFFFFF00u;
multiPixel.pixel4 &= 0xFFFFFF00u;
multiPixel.pixel5 &= 0xFFFFFF00u;
*((PixelContainer*)pointer) = multiPixel;
}

最佳答案

这是一种称为 loop unrolling. 的技术主要的性能优势应该来自于减少分支开销。

作为旁注,您可以通过使用位掩码来加快速度:

*((UInt64 *)pointer) &= 0xFFFFFF00FFFFFF00ul;

关于c# - 不安全的指针迭代和位图——为什么 UInt64 更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5812990/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com