我在图形库中有一个简单的框模糊功能(对于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.put
将ImageData
的内容写回画布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);
};
答案 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循环。