我能为这个功能做更多优化吗?

时间:2011-03-15 15:32:35

标签: javascript optimization canvas

我在图形库中有一个简单的框模糊功能(对于JavaScript / canvas,使用ImageData)我正在写。

我做了一些优化,以避免成堆的冗余代码,例如循环通过[0..3]代替复制代码,并让每个周围的像素用一条未复制的代码行实现,平均最后的价值观。

这些都是减少冗余代码行的优化。我能做些进一步的优化吗,或者更好的是,我可以改变哪些东西可以改善功能本身的性能?

在带有Core 2 Duo的200x150图像区域上运行此功能,在Firefox 3.6上大约需要450毫秒,在Firefox 4上需要45毫秒,在Chromium 10上需要大约55毫秒。

各种说明

  • expressive.data.get会返回ImageData个对象
  • expressive.data.putImageData的内容写回画布
  • ImageData是一个对象:
    • unsigned long width
    • unsigned long height
    • Array data,格式为r, g, b, a, r, g, b, a ...
    • 的单维数据

代码

expressive.boxBlur = function(canvas, x, y, w, h) {
    // averaging r, g, b, a for now
    var data = expressive.data.get(canvas, x, y, w, h);
    for (var i = 0; i < w; i++)
        for (var j = 0; j < h; j++)
            for (var k = 0; k < 4; k++) {
                var total = 0, values = 0, temp = 0;
                if (!(i == 0 && j == 0)) {
                    temp = data.data[4 * w * (j - 1) + 4 * (i - 1) + k];
                    if (temp !== undefined) values++, total += temp;
                }
                if (!(i == w - 1 && j == 0)) {
                    temp = data.data[4 * w * (j - 1) + 4 * (i + 1) + k];
                    if (temp !== undefined) values++, total += temp;
                }
                if (!(i == 0 && j == h - 1)) {
                    temp = data.data[4 * w * (j + 1) + 4 * (i - 1) + k];
                    if (temp !== undefined) values++, total += temp;
                }
                if (!(i == w - 1 && j == h - 1)) {
                    temp = data.data[4 * w * (j + 1) + 4 * (i + 1) + k];
                    if (temp !== undefined) values++, total += temp;
                }
                if (!(j == 0)) {
                    temp = data.data[4 * w * (j - 1) + 4 * (i + 0) + k];
                    if (temp !== undefined) values++, total += temp;
                }
                if (!(j == h - 1)) {
                    temp = data.data[4 * w * (j + 1) + 4 * (i + 0) + k];
                    if (temp !== undefined) values++, total += temp;
                }
                if (!(i == 0)) {
                    temp = data.data[4 * w * (j + 0) + 4 * (i - 1) + k];
                    if (temp !== undefined) values++, total += temp;
                }
                if (!(i == w - 1)) {
                    temp = data.data[4 * w * (j + 0) + 4 * (i + 1) + k];
                    if (temp !== undefined) values++, total += temp;
                }
                values++, total += data.data[4 * w * j + 4 * i + k];
                total /= values;
                data.data[4 * w * j + 4 * i + k] = total;
            }
    expressive.data.put(canvas, data, x, y);
};

5 个答案:

答案 0 :(得分:1)

如果使用var数据的唯一方法是data.data,则可以更改:

var data = expressive.data.get(canvas, x, y, w, h);

为:

var data = expressive.data.get(canvas, x, y, w, h).data;

并更改每一行,如:

temp = data.data[4 * w * (j - 1) + 4 * (i - 1) + k];

为:

temp = data[4 * w * (j - 1) + 4 * (i - 1) + k];

您将保存一些名称查找。

可能有更好的方法来优化它,但这正是我首先注意到的。

<强>更新

此外,if (i != 0 || j != 0)可能比if (!(i == 0 && j == 0))更快,不仅因为否定,还因为它可以缩短电路。

(使用=====!=!==进行自己的实验,因为我的快速测试显示结果似乎与我相反。)

并且一些测试也进行了多次,并且一些ifs是相互排斥的,但是在没有其他情况下仍然进行了测试。您可以尝试使用更多嵌套ifs和更多其他ifs重构它。

答案 1 :(得分:1)

也许(只是可能)尽可能地移出if支票将是一个优势。让我介绍一些伪代码:

为了简单起见,我将调用代码循环遍历k“内部循环”

// do a specialized version of "inner loop" that assumes i==0
for (var i = 1; i < (w-1); i++)
     // do a specialized version of "inner loop" that assumes j==0 && i != 0 && i != (w-1)
     for (var j = 1; j < (h-1); j++)
        // do a general version of "inner loop" that can assume i != 0 && j != 0 && i != (w-1) && j != (h-1)
     }
     // do a specialized version of "inner loop" that assumes j == (h - 1) && i != 0 && i != (w-1)
}
// do a specialized version of "inner loop" that assumes i == (w - 1)

如果if检查,这将大大减少数量,因为大多数操作都不需要它们。

答案 2 :(得分:0)

次要优化:

var imgData = expressive.data.get(canvas, x, y, w, h);
var data = imgData.data;

// in your if statements
temp = data[4 * w * (j - 1) + 4 * (i - 1) + k];

expressive.data.put(canvas, imgData, x, y) 

您还可以在索引中执行一些次要优化,例如:

4 * w * (j - 1) + 4 * (i - 1) + k // is equal to
4 * ((w * (j-1) + (i-1)) + k

var jmin1 = (w * (j-1))
var imin1 = (i-1)
//etc, and then use those indices at the right place

此外,在代码中的每个for语句后面加上{}。另外两个角色不会产生很大的不同。潜在的错误会。

答案 3 :(得分:0)

你可以提出一些常见的表达方式:

for (var i = 0; i < w; i++) {
    for (var j = 0; j < h; j++) {
        var t = 4*w*j+4*i;
        var dt = 4*j;
        for (var k = 0; k < 4; k++) {
            var total = 0, values = 0, temp = 0;
            if (!(i == 0 && j == 0)) {
                temp = data.data[t-dt-4+k];
                if (temp !== undefined) values++, total += temp;
            }
            if (!(i == w - 1 && j == 0)) {
                temp = data.data[t-dt+4+k];
                if (temp !== undefined) values++, total += temp;
            }
            if (!(i == 0 && j == h - 1)) {
                temp = data.data[t+dt-4+k];
                if (temp !== undefined) values++, total += temp;
            }
            if (!(i == w - 1 && j == h - 1)) {
                temp = data.data[t+dt+4+k];
                if (temp !== undefined) values++, total += temp;
            }
            if (!(j == 0)) {
                temp = data.data[t-dt+k];
                if (temp !== undefined) values++, total += temp;
            }
            if (!(j == h - 1)) {
                temp = data.data[t+dt+k];
                if (temp !== undefined) values++, total += temp;
            }
            if (!(i == 0)) {
                temp = data.data[t-4+k];
                if (temp !== undefined) values++, total += temp;
            }
            if (!(i == w - 1)) {
                temp = data.data[t+4+k];
                if (temp !== undefined) values++, total += temp;
            }
            values++, total += data.data[t+k];
            total /= values;
            data.data[t+k] = total;
        }
    }
}

您可以尝试将循环移到k上,使其位于最外层,然后将+k折叠到t的定义中,从而节省更多的重复计算。 (这可能会因内存地点原因而变坏。)

您可以尝试将j上的循环移到i上方的循环之外,这样可以提供更好的内存位置。这对于大型图像更重要;根据您使用的尺寸,它可能无关紧要。

相当痛苦但可能非常有效:你可以通过将你的循环分成{0,1..w-2,w-1}和{0,1..h-2,h-来丢失大量的条件操作1}。

你可以摆脱所有undefined次测试。你是否真的需要它们,因为你正在进行所有那些范围检查?

避免范围检查的另一种方法是:您可以沿着每条边将图像(使用零)填充一个像素。请注意,显而易见的方法是从边缘的现有代码中获得不同的结果;这可能是好事也可能是坏事。如果这是一件坏事,你可以计算出适当的值除以。

答案 4 :(得分:0)

temp = 0的声明不是必需的,只需写var total = 0, values = 0, temp;

接下来就是向后循环。

var length = 100,
    i;

for (i = 0; i < length; i++) {}

慢于

var length = 100;

for (; length != 0; length--) {}

第三个提示是使用Duffy's Device进行巨大的for循环。