建议避免save()restore()? (JavaScript的)

时间:2016-09-24 10:28:30

标签: javascript performance canvas save restore

我在许多网站上都读到使用save()和restore()是要处理的最多(或接近最多)重函数,所以问题是,最好手动恢复上下文状态而不是使用save ()和restore()?

示例,具有更好的性能?:

ctx.save();
ctx.shadowColor = "black";
ctx.shadowOffsetX = 2; 
ctx.shadowOffsetY = 2; 
ctx.shadowBlur = 3;
ctx.textBaseline = "top";
ctx.drawImage(img, 10, 10); // Any image
ctx.restore();

ctx.shadowColor = "black";
ctx.shadowOffsetX = 2; 
ctx.shadowOffsetY = 2; 
ctx.shadowBlur = 3;
ctx.textBaseline = "top";
ctx.drawImage(img, 10, 10); // Any image
ctx.shadowColor = null;
ctx.shadowOffsetX = 0; 
ctx.shadowOffsetY = 0; 
ctx.shadowBlur = 0;
ctx.textBaseline = "alphabetic";

基本上我想知道手动恢复所有已更改的属性是否优于使用save()和restore(),即使有多个属性需要手动恢复。

感谢您的时间。

2 个答案:

答案 0 :(得分:2)

查看“保存还原性能”

使用保存还原通常比不使用它更慢。但这是高度可变的,取决于硬件,上下文的当前状态以及是否在恢复后立即使用恢复状态,或者在不使用恢复状态的情况下继续。

手动完全恢复上下文状态总是比恢复慢,因为恢复不必重新创建状态,只是移动内存。

如果您有复杂的图案,渐变,字体,阴影设置,那么保存和恢复会对性能产生重大影响。如果使用保存和恢复,则必须知道要保存的内容以及是否需要该状态。例如,如果你设置一个大图案,然后使用存储设置一个新的填充和描边样式,然后渲染,然后恢复,不要使用你将减速的模式,因为该模式必须恢复,即使它不是正在使用,这可能意味着整个模式位图从RAM移动到GPU RAM(非常慢)

高质量分析比较器测试

来自非常精确的性能比较器分析器的一些原始转储。由于画布的性质(共享硬件依赖,我有两个浏览器打开,~12个Tabs加上3个应用程序都使用显示)一些结果被归类为“公平”,这意味着测量时间存在显着差异。 (通常我不会使用这些结果,但我不能让它们稳定下来)

  

注意公平地说我想在Firefox和Chrome上运行测试。最近的FF比Chrome有更强更好的性能。对于这个测试,我无法运行它来运行它。测试功能必须在2ms以下,测试人员才能接受它们。当我缩短循环时间(鼓掌2)时,测试功能甚至不会通过稳定时间,因为它始终以“Aw,Snap!Google Chrome在尝试显示此网页时耗尽内存”结束。我尝试过很多东西,但是当我用2D画布做任何事情时,无法让测试人员在Chrome上运行。

所有测试都是Firefox 50.0b1(测试版),默认情况下都是标记。抱歉,我无法让Chrome工作(Whasup chrome,这些天你看起来不太好?)

机Win10(32位)CPU i7 Q720 @ 1.60GHz x64。 4G RAM。 NVIDIA GeForce GT 330M(使用通用驱动程序)

测试直接转换还原V保存还原

测试代码。 (注意ctx是全局的)

function saveRestore(){  // test function contains shared scope and array of test functions.
                         // Each test function is copied and wrapped in a performance timer (locally scoped)
                         // Times are for the internal context of the test functions 
                         // Use setTestFunctionCount(count) to set the number of unique copies of each test function to run. Default = 4 
// the content of this "sharedFunction" will be be placed in the same scope as the test functions. All test functions will use the same shared scope. The content of "sharedFunction" is not in strict mode. You may add the directive yourself 
  sharedFunction = function(){ 
     var i;
     var xdx = Math.cos(1);
     var xdy = Math.sin(1);
  }
  testFunctions = [{
          func:function(){
              for(i = 0; i < 1000; i ++){
                  ctx.save();
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.restore();
              }            
          },
          name:"Save restore",
      },{
          func:function(){
              for(i = 0; i < 1000; i ++){
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.setTransform(1,0,0,1,0,0);
             }},
          name:"Direct restore",
      }
  ];
  start();
}  

结果。请注意,时间是针对该函数的,并且每个函数遍历感兴趣的代码1000次。因此,平均时间(即直接恢复平均值:316)意味着运行for循环1000次的0.316ms(1/1000秒),因此每个 ctx.setTransform(xdx, xdy, -xdy, xdx, 0, 0); ctx.setTransform(1, 0, 0, 1, 0, 0);.需要大约0.000316毫秒才能运行。在这种情况下,循环时间是无关紧要的。

Test complete Results.
Calls per sec, % cycle time, % of best time
Direct restore : 3167 : 72.14% 100%.
Save restore : 1223 : 27.86% 38.61%

Total cycles : 1000
Stable cycles : 899 Total.
Tests per cycle : 65
Testing cycles stable for : 800 of 800 cycles 100.00%
Max test variance 10.468%
Test results are of fair quality. Should be run again.

List of all test function results. Mean times in micro secs
# calls, total time, mean time
--------------------
Test function : Save restore
7018 tests 5839.760ms Mean : 832
7020 tests 5715.160ms Mean : 814
7071 tests 5703.905ms Mean : 807
7028 tests 5748.850ms Mean : 818
Variance : 85.601micro sec. normalised : 10.468%
Ran : 28137 over 23007.675ms Mean : 817.702micro sec
--------------------
Test function : Direct restore
6905 tests 2184.580ms Mean : 316
7038 tests 2216.935ms Mean : 315
7061 tests 2230.390ms Mean : 316
7047 tests 2224.530ms Mean : 316
Variance : 0.246micro sec. normalised : 0.078%
Ran : 28051 over 8856.435ms Mean : 315.726micro sec
Total number of tests run : 56188

测试人员从提供的测试函数

生成的testCode

return (function(){
    var now,timer;
    timer = performance;
 
    var i,r,arr = [];
    var xdx = Math.cos(1);
    var xdy = Math.sin(1);

 

    function func0(){
        now = timer.now();

              for(i = 0; i < 1000; i ++){
                  ctx.save();
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.restore();
              }            
          ;
       return timer.now()-now;
    }
    function func1(){
        now = timer.now();

              for(i = 0; i < 1000; i ++){
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.setTransform(1,0,0,1,0,0);
             };
       return timer.now()-now;
    }
    function func2(){
        now = timer.now();

              for(i = 0; i < 1000; i ++){
                  ctx.save();
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.restore();
              }            
          ;
       return timer.now()-now;
    }
    function func3(){
        now = timer.now();

              for(i = 0; i < 1000; i ++){
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.setTransform(1,0,0,1,0,0);
             };
       return timer.now()-now;
    }
    function func4(){
        now = timer.now();

              for(i = 0; i < 1000; i ++){
                  ctx.save();
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.restore();
              }            
          ;
       return timer.now()-now;
    }
    function func5(){
        now = timer.now();

              for(i = 0; i < 1000; i ++){
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.setTransform(1,0,0,1,0,0);
             };
       return timer.now()-now;
    }
    function func6(){
        now = timer.now();

              for(i = 0; i < 1000; i ++){
                  ctx.save();
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.restore();
              }            
          ;
       return timer.now()-now;
    }
    function func7(){
        now = timer.now();

              for(i = 0; i < 1000; i ++){
                  ctx.setTransform(xdx,xdy,-xdy,xdx,0,0);
                  ctx.setTransform(1,0,0,1,0,0);
             };
       return timer.now()-now;
    }
var tests = [{ func:func0, result : 0, count : 0, id : 0, name : 'Save restore'},{ func:func1, result : 0, count : 0, id : 1, name : 'Direct restore'},{ func:func2, result : 0, count : 0, id : 0, name : 'Save restore'},{ func:func3, result : 0, count : 0, id : 1, name : 'Direct restore'},{ func:func4, result : 0, count : 0, id : 0, name : 'Save restore'},{ func:func5, result : 0, count : 0, id : 1, name : 'Direct restore'},{ func:func6, result : 0, count : 0, id : 0, name : 'Save restore'},{ func:func7, result : 0, count : 0, id : 1, name : 'Direct restore'},];
const clearResults = function(){tests.forEach(test=>{ test.count = test.result = 0;})}
var testData;
const runTest = function(cycles){
    var testIndex,result,time;
    while(cycles > 0){
        testIndex = Math.floor(Math.random()* tests.length);
        tests[testIndex].result += time = tests[testIndex].func();
        tests[testIndex].count += 1;
        if(time < 0.002){ testData.warning = true; }
        cycles -= 1;
    }
};
return testData = {
   run : runTest,
   warning : false,
   clear : clearResults,
   results : tests,
}
})()

  

请注意,这些函数是随机运行的,以消除由执行顺序和优化问题引起的任何偏差。这就是为什么所有结果都是时间运行的统计分析。

未使用状态比较

Test complete Results.
Calls per sec, % cycle time, % of best time
Save restore : 8130 : 51.91% 100%.
Direct restore : 7531 : 48.09% 92.63%

Total cycles : 1000
Stable cycles : 899 Total.
Tests per cycle : 292
Testing cycles stable for : 800 of 800 cycles 100.00%
Max test variance 0.030%
Test results are good.

List of all test function results. Mean times in micro secs
# calls, total time, mean time
--------------------
Test function : Save restore
31149 tests 3830.600ms Mean : 123
31079 tests 3815.450ms Mean : 123
31321 tests 3858.660ms Mean : 123
31060 tests 3822.630ms Mean : 123
Variance : 0.025micro sec. normalised : 0.020%
Ran : 124609 over 15327.340ms Mean : 123.003micro sec
--------------------
Test function : Direct restore
31173 tests 4131.320ms Mean : 133
31028 tests 4123.105ms Mean : 133
31327 tests 4156.185ms Mean : 133
30986 tests 4122.735ms Mean : 133
Variance : 0.040micro sec. normalised : 0.030%
Ran : 124514 over 16533.345ms Mean : 132.783micro sec
Total number of tests run : 249123

上述结果的测试代码

function saveRestore(){
 sharedFunction = function(){ 
    var i,r,arr = [];
    var xdx = Math.cos(1);
    var xdy = Math.sin(1);
    var gradient = ctx.createLinearGradient(0,0,canvas.width,canvas.height);
    for(i = 0; i < 10; i ++){            gradient.addColorStop(Math.random(),"hsl("+Math.floor(Math.random()*360)+",100%,50%)");
    }
    var col = "Black";
    var col1 = "White";            
 }
  testFunctions = [{
          func:function(){
              ctx.fillStyle = gradient;
              ctx.strokeStyle = col;
              for(i = 0; i < 40; i ++){
                  ctx.save();
                  ctx.fillStyle = col1;
                  ctx.strokeStyle = col1
                  ctx.restore();  // restor style and fill
              }            
          },
          name:"Save restore",
      },{
          func:function(){
              for(i = 0; i < 40; i ++){
                  ctx.fillStyle = col1;
                  ctx.strokeStyle = col1
                  ctx.fillStyle = gradient;
                  ctx.strokeStyle = col;
             }},
          name:"Direct restore",
      }
  ];
  start();
} 

使用州结果

当您与上述测试进行比较时,显示当您使用结果状态时会发生什么(仅在未使用恢复状态时(下一次测试))

Test complete Results.
Calls per sec, % cycle time, % of best time
Direct restore : 4390 : 53.30% 100%.
Save restore : 3847 : 46.70% 87.63%

Total cycles : 1000
Stable cycles : 899 Total.
Tests per cycle : 156
Testing cycles stable for : 800 of 800 cycles 100.00%
Max test variance 2.257%
Test results are good.

List of all test function results. Mean times in micro secs
# calls, total time, mean time
--------------------
Test function : Save restore
16289 tests 4271.920ms Mean : 262
16286 tests 4202.980ms Mean : 258
16342 tests 4211.510ms Mean : 258
16291 tests 4262.670ms Mean : 262
Variance : 4.194micro sec. normalised : 1.614%
Ran : 65208 over 16949.080ms Mean : 259.923micro sec
--------------------
Test function : Direct restore
16329 tests 3752.165ms Mean : 230
16388 tests 3669.685ms Mean : 224
16316 tests 3729.565ms Mean : 229
16377 tests 3747.045ms Mean : 229
Variance : 5.141micro sec. normalised : 2.257%
Ran : 65410 over 14898.460ms Mean : 227.770micro sec
Total number of tests run : 130618

上面的测试代码

function saveRestore(){
 sharedFunction = function(){ 
    var i,r,arr = [];
    var xdx = Math.cos(1);
    var xdy = Math.sin(1);
    var gradient = ctx.createLinearGradient(0,0,canvas.width,canvas.height);
    for(i = 0; i < 10; i ++){
        gradient.addColorStop(Math.random(),"hsl("+Math.floor(Math.random()*360)+",100%,50%)");
    }
    var col = "Black";
    var col1 = "White";
    

 }
  testFunctions = [{
          func:function(){
              ctx.fillStyle = gradient;
              ctx.strokeStyle = col;
              for(i = 0; i < 40; i ++){
                  ctx.save();
                  ctx.fillStyle = col1;
                  ctx.strokeStyle = col1;
                  ctx.fillRect(0,0,1,1);
                  ctx.restore();  // restor style and fill
              }            
          },
          name:"Save restore",
      },{
          func:function(){
              for(i = 0; i < 40; i ++){
                  ctx.fillStyle = col1;
                  ctx.strokeStyle = col1
                  ctx.fillRect(0,0,1,1);
                  ctx.fillStyle = gradient;
                  ctx.strokeStyle = col;
             }},
          name:"Direct restore",
      }
  ];
  start();
} 

使用新状态和恢复状态测试

直接恢复仍然快〜5%

Test complete Results.
Calls per sec, % cycle time, % of best time
Direct restore : 1707 : 51.25% 100%.
Save restore : 1623 : 48.75% 95.11%

Total cycles : 1000
Stable cycles : 899 Total.
Tests per cycle : 60
Testing cycles stable for : 800 of 800 cycles 100.00%
Max test variance 5.881%
Test results are good.

List of all test function results. Mean times in micro secs
# calls, total time, mean time
--------------------
Test function : Save restore
6523 tests 4055.545ms Mean : 622
6660 tests 4043.060ms Mean : 607
6681 tests 4151.470ms Mean : 621
6685 tests 4105.790ms Mean : 614
Variance : 36.231micro sec. normalised : 5.881%
Ran : 26549 over 16355.865ms Mean : 616.063micro sec
--------------------
Test function : Direct restore
6579 tests 3908.045ms Mean : 594
6558 tests 3850.375ms Mean : 587
6547 tests 3815.380ms Mean : 583
6573 tests 3811.335ms Mean : 580
Variance : 28.463micro sec. normalised : 4.858%
Ran : 26257 over 15385.135ms Mean : 585.944micro sec
Total number of tests run : 52806

以上代码

function saveRestore(){
 sharedFunction = function(){ 
    var i,r,arr = [];
    var xdx = Math.cos(1);
    var xdy = Math.sin(1);
    var gradient = ctx.createLinearGradient(0,0,canvas.width,canvas.height);
    for(i = 0; i < 10; i ++){
        gradient.addColorStop(Math.random(),"hsl("+Math.floor(Math.random()*360)+",100%,50%)");
    }
    var col = "Black";
    var col1 = "White";
    

 }
  testFunctions = [{
          func:function(){
              ctx.fillStyle = gradient;
              ctx.strokeStyle = col;
              for(i = 0; i < 40; i ++){
                  ctx.save();
                  ctx.fillStyle = col1;
                  ctx.strokeStyle = col1;
                  ctx.fillRect(0,0,1,1);
                  ctx.restore();  // restor style and fill
                  ctx.fillRect(0,0,1,1);
              }            
          },
          name:"Save restore",
      },{
          func:function(){
              for(i = 0; i < 40; i ++){
                  ctx.fillStyle = col1;
                  ctx.strokeStyle = col1
                  ctx.fillRect(0,0,1,1);
                  ctx.fillStyle = gradient;
                  ctx.strokeStyle = col;
                  ctx.fillRect(0,0,1,1);
             }},
          name:"Direct restore",
      }
  ];
  start();
} 

我尝试在测试中构建渐变,但在2ms的时间限制内无法获得该渐变。

我没时间,所以我会添加更多测试。请做一个测试(如果可以,我会显示简要的摘要结果)。

答案 1 :(得分:0)

我刚刚制作了一个快速的分析器,似乎手册需要x4更多的时间来完成...这与我在互联网上阅读的相反,任何人都有贡献的东西?谢谢!