为什么使用 `color` 设置像素颜色会产生如此糟糕的性能

Why does setting the pixel color using `color` give such bad performance

我正在编写物理模拟,并希望使用 P5*JS(处理语言的 JS 实现)对结果进行后处理。

我正在使用以下代码来设置每个像素的颜色:

colorMode(RGB,1);
for (var x = 0; x < nX; x++) {
  for (var y = 0; y < nY; y++) {
    var id = getIdx(x,y);
    var v = vArray[id]; //range=[0,1]
    var c = color(v, 0, 1-v);
    set(x, y, c);
  }
}
updatePixels();

非常简单;我从数组 vArray 中获取一个值,该值在 [0,1] 范围内。使用 color() 计算颜色 c,像素 [x,y] 设置为 c

不幸的是,与我的其余代码相比,这具有非常糟糕的性能; color() 大约需要 CPU 时间的 60%

与代码的其余部分相比,这是一个巨大的瓶颈,考虑到它正在做繁重的工作,这应该是瓶颈。如果我将 color() 移出循环,代码将再次高效运行(当然没有为像素着色):

有谁知道为什么 color() 如此低效?如何通过修改 color() 的使用或以其他方式设置像素颜色来避免这种低效率?

编辑:事实证明 color() 包含许多 if 检查。我发现通过将包含颜色数据的数组直接传递给 set() 可以获得更好的性能:

for (var x = 0; x < nX; x++) {
  for (var y = 0; y < nY; y++) {
    var id = getIdx(x,y);
    var v = vArray[id]; //range=[0,1]
    var c = [255*v, 0, 255*(1-v), 255];
    set(x, y, c);
  }
}
updatePixels();

colorMode() 不再需要显式设置。

要了解 color() 函数的作用,最好的办法是转到 the source

这里是 color() 函数的开始:

p.color = function(aValue1, aValue2, aValue3, aValue4) {

      // 4 arguments: (R, G, B, A) or (H, S, B, A)
      if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef && aValue4 !== undef) {
        return color(aValue1, aValue2, aValue3, aValue4);
      }

      // 3 arguments: (R, G, B) or (H, S, B)
      if (aValue1 !== undef && aValue2 !== undef && aValue3 !== undef) {
        return color(aValue1, aValue2, aValue3, colorModeA);
      }

      // 2 arguments: (Color, A) or (Grayscale, A)
      if (aValue1 !== undef && aValue2 !== undef) {
        return color(aValue1, aValue2);
      }

      // 1 argument: (Grayscale) or (Color)
      if (typeof aValue1 === "number") {
        return color(aValue1);
      }

      // Default
      return color(colorModeX, colorModeY, colorModeZ, colorModeA);
    };

然后调用 color() 函数:

 function color(aValue1, aValue2, aValue3, aValue4) {
      var r, g, b, a;

      if (curColorMode === PConstants.HSB) {
        var rgb = p.color.toRGB(aValue1, aValue2, aValue3);
        r = rgb[0];
        g = rgb[1];
        b = rgb[2];
      } else {
        r = Math.round(255 * (aValue1 / colorModeX));
        g = Math.round(255 * (aValue2 / colorModeY));
        b = Math.round(255 * (aValue3 / colorModeZ));
      }

      a = Math.round(255 * (aValue4 / colorModeA));

      // Limit values less than 0 and greater than 255
      r = (r < 0) ? 0 : r;
      g = (g < 0) ? 0 : g;
      b = (b < 0) ? 0 : b;
      a = (a < 0) ? 0 : a;
      r = (r > 255) ? 255 : r;
      g = (g > 255) ? 255 : g;
      b = (b > 255) ? 255 : b;
      a = (a > 255) ? 255 : a;

      // Create color int
      return (a << 24) & PConstants.ALPHA_MASK | (r << 16) & PConstants.RED_MASK | (g << 8) & PConstants.GREEN_MASK | b & PConstants.BLUE_MASK;
    }

此函数有 if 语句、舍入和位移。这比您的 collide() 功能 "more work"。从本质上讲,这就是 "taking more time".

的原因

但这并不是说 color() 函数是 "bottleneck" 或 "inefficient"。您正在为图像中的每个像素调用 color() 函数,这几乎可以保证比其他任何像素占用更多 CPU。

您可能会尝试考虑替代方法:您真的需要对每个像素都这样做吗?你真的需要每一帧都这样做吗?

但实际上,如果您没有注意到帧率有任何问题,那么这不是问题。 color() 函数花费了更多 CPU 时间,这似乎让您感到困扰,但我认为您关注的是错误的事情。过早的优化是万恶之源,等等。