【问题标题】:How to optimise large loops for debug mode如何优化调试模式的大循环
【发布时间】:2018-05-18 04:32:43
【问题描述】:

我已经实现了一个用于检查完美碰撞的像素掩码类。我使用的是 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 秒)。有趣的是,仅在调试模式下。编译发布版本时,相同的纹理/图像只需 0.1 秒或更短。

我尝试通过使用 resize() 方法来减少内存分配,但并没有太大变化。我知道循环通过近 100 万像素很慢,但应该不会慢 15 秒吧?

由于我想在调试模式下测试我的代码(出于显而易见的原因)并且我不想等待 5 分钟直到所有像素掩码都创建完毕,所以我正在寻找的基本上是一种方法:

  • 优化代码/我是否遗漏了一些明显的东西?
  • 或者在调试模式下获得类似于发布性能的东西

感谢您的帮助!

【问题讨论】:

  • 调试模式总是很慢;不要为此进行优化。
  • 你用的是什么编译器?如果是 Visual Studio,只需更改发布设置即可生成调试信息。另外,关闭发布模式的优化。 很多 VS 的缓慢是由于迭代器检查 - 去掉它,即使是未优化的发布程序运行速度也明显更快。
  • 您可以使用单个向量来代替 向量的向量。这将节省大量分配(通常非常慢)。然后使用数学公式mask[(x * width) + y] 访问x y
  • 另外,pixel by pixel 检查碰撞可能比检查游戏实体占据的一般形状慢很多
  • @AdrianKoch 我通常有 3 种配置。发布、调试、ReleaseWIthDebugInfo。后者和release完全一样,只是开启了调试信息的生成,禁用了优化。

标签: c++ loops c++11 optimization sfml


【解决方案1】:

调试优化

针对调试构建进行优化通常是一种适得其反的想法。它甚至可以让您以一种不仅使维护代码更加困难,而且甚至可能减慢发布构建的方式优化调试。一般来说,调试版本运行起来会慢得多。即使使用我编写的最平坦的 C 代码,除了合理的寄存器分配和指令选择之外,优化器不会做太多事情,调试构建需要 20 倍的时间才能完成操作是正常的。这只是接受而不是改变太多。

也就是说,我可以理解有时这样做的诱惑。有时您只想调试某部分代码,只为软件中的其他操作需要很长时间,这需要您等待很长时间才能找到您有兴趣跟踪的代码。我发现在这些情况下,如果可以的话,将调试模式输入大小与发布模式分开是有帮助的(例如:调试模式仅适用于原始大小的 1/10 的输入)。这确实会导致发布和调试之间的差异作为负面因素,但从生产力的角度来看,正面因素有时会超过负面因素。另一种策略是在发布时构建部分代码并仅调试您感兴趣的部分,例如在发布时针对宿主应用程序构建一个调试插件。

后果自负

除此之外,如果您真的想让您的调试版本运行得更快并接受所有相关的风险,那么主要的方法就是减少编译器的优化工作量。这将是更扁平的代码,通常具有更普通的旧数据类型、更少的函数调用等等。

首先,为了安全起见,您可能会在调试模式断言上花费大量时间。查看检查迭代器以及如何禁用它们之类的内容: https://msdn.microsoft.com/en-us/library/aa985965.aspx

对于您的情况,您可以轻松地将嵌套循环展平为单个循环。无需为每个扫描线创建带有单独容器的这些像素掩码,因为您始终可以通过一些基本算法(y*image_widthy*image_stride)获得扫描线数据。所以最初我会把循环弄平。这甚至可能对发布模式有所帮助。我不知道 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 允许您使用单个索引访问像素,而无需指定列和行(xy)。如果你想走得更远,它可能有助于从 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&lt;bool&gt; 将每个布尔值存储为一个位。这可以节省内存,但会转化为更多用于随机访问的指令。有时你甚至可以通过使用更多内存来提高速度。我会仔细测试发布、调试和时间,但你可以试试这个:

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;
}

【讨论】:

    【解决方案2】:

    如果使用成本变量,循环会更快: 1. for (unsigned int i = 0; i

    【讨论】:

      猜你喜欢
      • 2012-10-28
      • 1970-01-01
      • 1970-01-01
      • 2011-06-15
      • 1970-01-01
      • 2022-11-03
      • 2020-10-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多