我已经实现了一个用于检查完美碰撞的像素掩码类。我使用的是 SFML,因此实现非常简单:
循环遍历图像的每个像素,并根据其透明度值判断其真假。这是我使用的代码:
// Create an Image from the given texture
sf::Image image(texture.copyToImage());
// measure the time this function takes
sf::Clock clock;
sf::Time time = sf::Time::Zero;
clock.restart();
// Reserve memory for the pixelMask vector to avoid repeating allocation
pixelMask.reserve(image.getSize().x);
// Loop through every pixel of the texture
for (unsigned int i = 0; i < image.getSize().x; i++)
{
// Create the mask for one line
std::vector<bool> tempMask;
// Reserve memory for the pixelMask vector to avoid repeating allocation
tempMask.reserve(image.getSize().y);
for (unsigned int j = 0; j < image.getSize().y; j++)
{
// If the pixel is not transparrent
if (image.getPixel(i, j).a > 0)
// Some part of the texture is there --> push back true
tempMask.push_back(true);
else
// The user can't see this part of the texture --> push back false
tempMask.push_back(false);
}
pixelMask.push_back(tempMask);
}
time = clock.restart();
std::cout << std::endl << "The creation of the pixel mask took: " << time.asMicroseconds() << " microseconds (" << time.asSeconds() << ")";
我使用了 sf::Clock
的一个实例来测量时间。
我的问题是,对于较大的图像(例如 1280x720),此函数需要很长时间(例如 15 秒)。有趣的是,仅在 Debug模式下。编译发布版本时,相同的纹理/图像只需要 0.1 秒或更短时间。
我曾尝试使用 resize() 方法来减少内存分配,但并没有太大改变。我知道循环遍历近 100 万个像素很慢,但应该不会慢 15 秒吧?
因为我想在 Debug模式下测试我的代码(出于显而易见的原因)并且我不想等到所有像素掩码都创建完毕才等待 5 分钟,所以我正在寻找的基本上是一种方法:
- 要么优化代码/我是否遗漏了一些明显的东西?
- 或者在 Debug模式下得到类似于发布性能的东西
感谢您的帮助!
最佳答案
优化调试
优化调试版本通常是一个非常适得其反的想法。它甚至可以让您以一种不仅使维护代码更加困难,而且甚至可能减慢发布构建的方式来优化调试。调试构建通常运行起来要慢得多。即使使用我编写的最平坦的 C 代码,除了合理的寄存器分配和指令选择之外,优化器没有太多要做的事情,调试构建需要 20 倍的时间来完成操作是正常的。这只是接受而不是改变太多。
也就是说,我能理解有时这样做的诱惑。有时您只想调试某部分代码,而软件中的其他操作需要很长时间,需要您等待很长时间才能找到您有兴趣跟踪的代码。我发现在这些情况下,如果可以的话,将 Debug模式输入大小与 Release模式分开是有帮助的(例如:让 Debug模式仅适用于原始大小的 1/10 的输入)。这确实会导致发布和调试之间的差异成为负面影响,但从生产力的角度来看,正面有时会超过负面影响。另一种策略是在发布时构建部分代码,然后只调试您感兴趣的部分,例如针对发布时的主机应用程序调试构建插件。
后果自负
除此之外,如果您真的想让调试构建运行得更快并接受所有相关风险,那么主要的方法就是减少编译器需要优化的工作量。这将是更扁平的代码,通常具有更普通的旧数据类型、更少的函数调用等。
首先,为了安全起见,您可能会花费大量时间在 Debug模式断言上。查看检查迭代器之类的东西以及如何禁用它们: https://msdn.microsoft.com/en-us/library/aa985965.aspx
对于您的情况,您可以轻松地将嵌套循环展平为单个循环。没有必要为每条扫描线创建具有单独容器的像素掩码,因为您始终可以使用一些基本算法( y*image_width
或 y*image_stride
)获取扫描线数据。所以最初我会展平循环。这甚至可能对 Release模式有一定帮助。我不知道 SFML API,所以我将使用伪代码进行说明。
const int num_pixels = image.w * image.h;
vector<bool> pixelMask(num_pixels);
for (int j=0; j < num_pixels; ++j)
pixelMask[j] = image.pixelAlpha(j) > 0;
仅此一点可能会有很大帮助。希望 SFML 允许您使用单个索引访问像素,而无需指定列和行(x
和 y
)。如果您想走得更远,从 SFML 中获取指向像素数组的指针(也很有可能)并使用它可能会有所帮助:
vector<bool> pixelMask(image.w * image.h);
const unsigned int* pixels = image.getPixels();
for (int j=0; j < num_pixels; ++j)
{
// Assuming 32-bit pixels (should probably use uint32_t).
// Note that no right shift is necessary when you just want
// to check for non-zero values.
const unsigned int alpha = pixels[j] & 0xff000000;
pixelMask[j] = alpha > 0;
}
还有 vector<bool>
将每个 bool 值存储为一个位。这节省了内存,但转化为更多的随机访问指令。有时,您甚至可以通过使用更多内存来加快发布速度。我会仔细测试发布和调试以及时间,但你可以试试这个:
vector<char> pixelMask(image.w * image.h);
const unsigned int* pixels = image.getPixels();
char* pixelUsed = &pixelMask[0];
for (int j=0; j < num_pixels; ++j)
{
const unsigned int alpha = pixels[j] & 0xff000000;
pixelUsed[j] = alpha > 0;
}
关于c++ - 如何为 Debug模式优化大循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47637952/