为什么IE8上的JavaScript访问图像尺寸如此昂贵?

roc*_*hal 13 javascript performance image dimensions

我必须处理大量图像.首先,我需要检查图像的大小是否大于50x60并适当增加坏图像的计数器.

我的问题是,速度n.width/ n.height在Internet Explorer 8极低.我查了一下n.offsetWidth,n.clientWidth但速度方面都是一样的.我不能使用n.style.width,因为这个值并不总是设置在<img />我感兴趣的标签上.

考虑以下代码:

使用Javascript

var Test = {
    processImages: function () {
        var fS = new Date().getTime();

        var minimagew = 50,
            minimageh = 60;
        var imgs = document.getElementsByTagName('img');
        var len = imgs.length,
            isBad = 0,
            i = len;

        while (i--) {
            var n = imgs[i];

            var imgW = n.width;
            var imgH = n.height;

            if (imgW < minimagew || imgH < minimageh) {
                isBad++;
            }
        }

        var fE = new Date().getTime();
        var fD = (fE - fS);

        console.info('Processed ' + imgs.length + ' images in ' 
                     + fD + 'ms.  ' + isBad + ' were marked as bad.');
    }
};
Run Code Online (Sandbox Code Playgroud)

HTML

<img src="http://nsidc.org/images/logo_nasa_42x35.gif" />
   [snip 9998 images]
<img src="http://nsidc.org/images/logo_nasa_42x35.gif" />
Run Code Online (Sandbox Code Playgroud)

代码生成以下输出解析10k图像(3个不同的Ctrl + F5s)

  • FF:在115ms内处理10000张图像.10000被标记为坏.
  • FF:在99ms内处理10000张图像.10000被标记为坏.
  • FF:在87ms内处理10000张图像.10000被标记为坏.
  • IE8:在206ms内处理10000张图像.10000被标记为坏.
  • IE8:在204ms内处理10000张图像.10000被标记为坏.
  • IE8:在208ms内处理10000张图像.10000被标记为坏.

如您所见,FF 3.6中的代码比IE8中执行的代码快两倍.

为了证明问题确实与浏览器维度属性的速度有关,如果我改变:n.widthn.height常量,那么我们将:

 var imgW = 43;
 var imgH = 29;
Run Code Online (Sandbox Code Playgroud)

我得到以下结果:

  • FF:在38ms内处理10000张图像.10000被标记为坏.
  • FF:在34ms内处理10000张图像.10000被标记为坏.
  • FF:在33ms内处理10000张图像.10000被标记为坏.
  • IE8:在18ms内处理10000张图像.10000被标记为坏.
  • IE8:在22ms内处理10000张图像.10000被标记为坏.
  • IE8:在17ms内处理10000张图像.10000被标记为坏.

那就对了!当我们跳过<img />维度检查(调用node.width/ node.clientWidthetc)时,IE8实际上比Firefox更好.

你有什么想法为什么IE需要这么长时间来检查图像的大小,并最终如何提高这项检查的性能?

gbl*_*zex 8

那么你的代码非常基础.您可以优化的唯一方法是检查尺寸:

if (n.width < minimagew || n.height < minimageh) {
  isBad++;
}
Run Code Online (Sandbox Code Playgroud)

这样,如果width图像错误,height将无法访问.对于有坏的图像,它会使代码的速度提高1.5-2倍width.

但我的猜测是,您实际上并不需要10000张图片作为您网站的一部分.在这种情况下,您可以检查Image对象而不是<img>元素.

loop {
  var img = new Image();
  img.src = "http://nsidc.org/images/logo_nasa_42x35.gif";
}
Run Code Online (Sandbox Code Playgroud)

这将使您的代码在IE 8中快2倍,在FF中快10倍.

进行这些更改后,我的计算机(演示)得到了以下改进:

FF: 200 ms ->  7 ms
IE:  80 ms -> 20 ms
Run Code Online (Sandbox Code Playgroud)

  • 您可以采取以下措施来加速这样的原始操作:http://code.google.com/chrome/chromeframe/ (2认同)

som*_*ome 5

换句话说,你问为什么浏览器A需要比浏览器B更长的时间来完成相同的操作.

好吧,他们不做同样的事情.你在脚本中写下你想要发生的事情,你选择的浏览器会尝试最好地实现这一点,但是你很少或根本无法控制它是如何做到的.

浏览器由不同的团队编写,具有不同的如何做事的理念.您的20行内容脚本可能需要浏览器A中的10000个CPU周期和浏览器B中的50000个,这取决于浏览器代码的编写方式和浏览器的内部工作方式.

为了详细解答为什么IE8比FF慢,在这种情况下,人们必须看看幕后发生的事情.由于IE8的源代码不公开,我们无法查看,我不知道是否有任何详细的文档可以告诉我们发生了什么.

相反,我举一个例子说明如何做两件不同的哲学如何影响产生相同最终结果的时间.注意:这是一个例子,与现实世界的任何相似之处纯属巧合.

该怎么办:

  • 获取图像的尺寸.

A队:

  1. 从指定的源加载文件
  2. 将图像解码到内存中
  3. 返回图像的宽度和高度

没错,是吗?它确实返回图像的宽度和高度.团队可以做得更好吗?

B队:

  1. 将前1024个字节的文件加载到内存中
  2. 检测图像格式
    • 它是jpeg吗?获取标题FFC0,保存宽度和高度
    • 是吗?找到标题,保存宽度和高度
    • 是gif吗?找到标题,保存宽度和高度
  3. 返回图像的宽度和高度

他们的代码也会返回图像的宽度和高度,但是他们以不同的方式执行它,比团队A编写的代码快几倍:只将文件的开头加载到内存中,并且采用尺寸从头部而不解码图像.它节省了带宽,内存和时间.

那么哪一个最好?A队或B队的代码?想想这一点,两个团队都会对10万张图片运行代码....可能需要一个......哦,团队B已经完成了!他们说10%的图像小于50x60像素,并且他们无法打开其中3个像素.那么A队怎么样?看起来我们要等一会儿......也许是一杯咖啡?

[10分钟后]

我猜你认为B队写的是最好的代码,我是对的,还是我对的?

A队表示,8%的图像小于50x60像素.奇怪的是,这不是B队所说的.A队还表示他们无法获得20%图像的维度,因为那些文件已损坏.B队没有说什么......

那么您认为哪种代码最好?

我为语言错误道歉,英语不是我的母语.


Inv*_*con 4

好吧,这很可能不是您想要的,但我想我会发布它,以防它对其他人有帮助。由于无法提高浏览器基本功能的速度,因此您可以防止循环在执行时冻结浏览器。您可以通过按块执行循环并使用时间为 0 的 setTimeout 启动下一个块来完成此操作。这基本上允许浏览器在调用下一个块之前重新绘制并执行其他操作。这是脚本的修改版本:

var Test = {
    processImages: function() {
        var fS = new Date().getTime();

        var minimagew = 50,
            minimageh = 60,
            stepSize = 1000;
        var imgs = document.getElementsByTagName('img');
        var len = imgs.length,
            isBad = 0,
            i = len,
            stopAt = len;

        var doStep = function() {
            stopAt -= stepSize;
            while (i >= stopAt && i--) {
                var n = imgs[i];

                var imgW = n.width;
                var imgH = n.height;

                if (imgW < minimagew || imgH < minimageh) {
                    isBad++;
                }
            }

            if (i > 0)
                setTimeout(doStep, 0);
            else {
                var fE = new Date().getTime();
                var fD = (fE - fS);

                console.info('Processed ' + imgs.length + ' images in '
                     + fD + 'ms.  ' + isBad + ' were marked as bad.');
            }
        }
        doStep();
    }
};
Run Code Online (Sandbox Code Playgroud)

当然,这会使总执行时间更长,但也许您可以使用它,以便您的页面在工作时可用。