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

Is recommended to avoid save() restore()? (JavaScript)

本文关键字:restore JavaScript save      更新时间:2023-09-26

我在许多网站上读到,使用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()更好,即使有几个属性需要手动恢复。

感谢您的宝贵时间。

查看Save Restore Performance

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

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

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

高质量性能分析比较器测试

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

说明为了公平起见,我想在Firefox和Chrome上同时运行测试。最近FF的比Chrome的要好得多。对于这个测试,我不能得到chrome运行它。测试函数必须小于2ms,测试人员才能接受它们。当我减少循环时间(到鼓掌2)时,测试函数甚至没有通过稳定时间,因为它总是以"哇,啪!"谷歌浏览器在试图显示此网页时内存不足。"我已经尝试了很多东西,但不能让测试人员在Chrome上运行时,我做任何与2D画布。

所有测试都是Firefox 50.0b1 (beta),默认为所有标志。对不起,我不能得到Chrome工作(Whasup Chrome你不好看这些天吗?)

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


Test Direct transform restore V Save restore

测试代码。(注: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)意味着0.316毫秒(1/1000秒)运行for循环1000次,因此每个
ctx.setTransform(xdx, xdy, -xdy, xdx, 0, 0); ctx.setTransform(1, 0, 0, 1, 0, 0);.的运行时间约为0.000316ms。在这种情况下,循环时间几乎不重要。

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的时间限制下得到。

我的时间不够了,所以我会增加更多的测试,因为我做他们。请务必要求进行测试(如果可以的话,我会显示简短的总结结果)。

我刚刚做了一个快速分析器,似乎手动需要多花4倍的时间来完成…这与我在网上看到的正好相反,有人有什么要贡献的吗?谢谢!