简体   繁体   English

如何提高此功能的性能?

[英]How to improve performance in this function?

I have a kind of "bouncing balls" project, where I draw 150 particles at a canvas and, at every redraw, it recalc particles position, and verify if any particles is at a corner, to invert its iterator. 我有一个“弹跳球”项目,我在画布上绘制150个粒子,并且在每次重绘时,它都会重新计算粒子的位置,并验证是否有任何粒子在拐角处,以反转其迭代器。

But thing is that this project has a factor that not all "bouncing balls" projects have. 但事实是,该项目具有并非所有“弹跳球”项目都具有的因素。 The balls need to bounce within the bounds of a map. 球需要在地图范围内反弹。

So, when I create the canvas, I also use a SVG to iterate over the pixels and create a array of every bound in the x axis, left and right, so my particles will know exactly where they need to rebounce. 因此,当我创建画布时,我还使用SVG遍历像素并创建x轴左右两侧的每个边界的数组,因此我的粒子将确切知道它们需要重新反弹的位置。

So good, so well, it is working nice, but my canvas is 500px tall, so it need to iterate 500 times, with a lot of conditionals to prevent weird behavior, this multiplied by 150 particles, and in every redraw. 很好,很好,它工作得很好,但是我的画布是500px高,因此它需要进行500次迭代,并带有很多防止怪异行为的条件,该条件乘以150个粒子,并且每次重绘。

It has became very performance greedy and I need to improve performance, so, here is my collision system code 它变得非常贪婪,我需要提高性能,所以这是我的碰撞系统代码

const colisionSystem = state => {
  for (var b=0, hs=state.bounds.length; b<hs; b++) {
    if(
      state.bounds[b][0]
      && state.x - state.radius < state.bounds[b][0].x
      && state.y + state.radius > state.bounds[b][0].y
      && state.y - state.radius < state.bounds[b][0].y
    ) {
      if (
        state.bounds[b][0].x > 0
        && state.bounds[b][0].x < (state.widgetSize.width * 0.33)
        && state.bounds[b][0].y > (state.widgetSize.height * 0.33)
        && state.bounds[b][0].y < (state.widgetSize.width * 0.45)
      ) {
        // middle left bottom corner at acre
        state.x = state.radius + state.bounds[b][0].x;
        state.vy *= -1;
      } else if (
        state.bounds[b][0].x > 0
        && state.bounds[b][0].x < (state.widgetSize.width * 0.098)
        && state.bounds[b][0].y > (state.widgetSize.height * 0.167)
        && state.bounds[b][0].y < (state.widgetSize.width * 0.206)
      ) {
        // middle left top corner at acre
        state.y = state.radius + state.bounds[b][0].y + 1;
        state.vx *= -1;
        state.vy *= -1;
      } else {
        state.x = state.radius + state.bounds[b][0].x;
        state.vx *= -1;
      }

      if(state.oldAxis === state.x) {
        state.y = state.y - 1;
      } else {
        state.oldAxis = state.x;
      }

      state.antiRebounce = false;
    }
    if(
      state.bounds[b][1]
      && state.x + state.radius > state.bounds[b][1].x
      && state.y + state.radius > state.bounds[b][1].y
      && state.y - state.radius < state.bounds[b][1].y
    ) {
      if (
        state.bounds[b][1].x > (state.widgetSize.width * 0.555)
        && state.bounds[b][1].x < (state.widgetSize.width * 0.983)
        && state.bounds[b][1].y > 0
        && state.bounds[b][1].y < (state.widgetSize.width * 0.2098)
      ) {
        // Top right corner
        if(state.antiRebounce) {
          state.vy *= -1;
          state.antiRebounce = false;
        } else {
          state.antiRebounce = true;
        }
        state.y = state.bounds[b][1].y + state.radius + 1;
        state.vy *= -1;
      }
      if (
        state.bounds[b][1].x > (state.widgetSize.width * 0.604)
        && state.bounds[b][1].x < (state.widgetSize.width * 0.827)
        && state.bounds[b][1].y > (state.widgetSize.width * 0.665)
        && state.bounds[b][1].y < (state.widgetSize.width * 0.778)
      ) {
        // bottom right corner
        state.vy *= -1;
      } else {
        state.vx *= -1;
        state.x = state.bounds[b][1].x - state.radius;
      }

      if(state.oldAxis === state.x) {
        state.y = state.y - 1;
      } else {
        state.oldAxis = state.x;
      }
    }
  }

  if (state.y + state.radius > state.widgetSize.height) {
    state.vy *= -1;
    state.y = state.widgetSize.height - state.radius;
  }
  if (state.y - state.radius < 0) {
    state.vy *= -1;
    state.y = state.radius;
  }

  return state;
}

export default colisionSystem;

So, question is, is there any practical advice to improve this code itself? 因此,问题是,是否有任何实用建议来改进此代码本身?

You have 500 * 150 particles (750000) that is a lot and frankly too much for a JS app to handle. 您有500 * 150粒子(750000),这对于JS应用程序来说实在太多了,坦白说太多了。 (BUT you say 150 particles so I am somewhat confused as to what you are doing) (但是您说了150个粒子,所以我对您的工作感到困惑)

To increase the performance of the function provided you can use a few simple rules of thumb. 为了提高所提供功能的性能,您可以使用一些简单的经验法则。

An array lookup is slower than a direct referance. 数组查找比直接引用慢。

ie

// a compound referance
state.bounds[b][1].x // find state, then bounds, then index b, then 1,then x
// if you have the common parts used frequently
var b1 = state.bounds[b][1]; // does all the lookups
b1.x; // but now only needs one lookup to get x

Same is true for item properties. 项目属性也是如此。 Each . 每个. means an additional lookup. 表示额外的查询。 You can get a lot of extra performance by creating temporary variables to hold the result of all the lookups. 通过创建临时变量来保存所有查找的结果,您可以获得很多额外的性能。

Applying this to your code and you get 将其应用到您的代码中,您将获得

const colisionSystem = state => {
    var w = state.widgetSize.width;  // used many times in the loop
    var h = state.widgetSize.height;
    var x = state.x;
    var y = state.y;
    var r = state.radius;
    for (var b = 0, hs = state.bounds.length; b < hs; b++) {
        var bounds = state.bounds[b];        
        if (bounds[0]){
            var b0 = bounds[0];
            if( x - r < b0.x && y + r > b0.y && y - r < b0.y) {
                if ( b0.x > 0 && b0.x < (w * 0.33) && b0.y > (h * 0.33) && b0.y < (w * 0.45)) {                    
                    x = r + b0.x; // middle left bottom corner at acre
                    state.vy *= -1;
                } else if ( b0.x > 0 && b0.x < (w * 0.098) && b0.y > (h * 0.167) && b0.y < (w * 0.206)) {                   
                    y = r + b0.y + 1; // middle left top corner at acre
                    state.vx *= -1;
                    state.vy *= -1;
                } else {
                    x = r + b0.x;
                    state.vx *= -1;
                }
                if (state.oldAxis === x) {
                    y -= 1;
                } else {
                    state.oldAxis = x;
                }
                state.antiRebounce = false;
            }
        }
        if (bounds[1]){
            var b1 = bounds[1];
            if( x + r > b1.x && y + r > b1.y && y - r < b1.y) {
                if ( b1.x > (w * 0.555) && b1.x < (w * 0.983) && b1.y > 0 && b1.y < (w * 0.2098)) {                        
                    if (state.antiRebounce) { // Top right corner
                        state.vy *= -1;
                        state.antiRebounce = false;
                    } else {
                        state.antiRebounce = true;
                    }
                    y = b1.y + r + 1;
                    state.vy *= -1;
                }
                if (b1.x > (w * 0.604) && b1.x < (w * 0.827) && b1.y > (w * 0.665) && b1.y < (w * 0.778)) {                    
                    state.vy *= -1; // bottom right corner
                } else {
                    state.vx *= -1;
                    x = b1.x - r;
                }
                if (state.oldAxis === x) {
                    y = y - 1;
                } else {
                    state.oldAxis = x;
                }
            }
        }
    }
    if (y + r > h) {
        state.vy *= -1;
        y = h - r;
    } else if (y - r < 0) {  // added else. Cant both happen at the same time?????
        state.vy *= -1;
        y = r;
    }
    state.y = y; // set state x,y to reflect any changes.
    state.x = x; 

    return state;
}

By replacing the many compound references with direct references you can free up a lot of CPU time, but this will depend on the data. 通过用直接引用替换许多复合引用,您可以节省大量CPU时间,但这取决于数据。 If the code above spends more time rejecting the conditional statements early you will not get much of a benefit, and if the bounds[1],bounds[0] conditions are only passed once a call to colisionSystem you will get no benefit, and maybe a slight decrease in performance. 如果上面的代码花了更多时间尽早拒绝条件语句,那么您将不会获得太多好处,并且如果仅对一次colisionSystem的调用传递了bounds [1],bounds [0]条件,您将不会获得任何好处,也许性能略有下降。 Same holds true for the number of items in the loop, if the loop is iterating many items you will see an improvement, if the loop is only 1 or 2 items you will see no benefit and for one item you will see a decrease in performance. 对于循环中的项目数也是如此,如果循环迭代许多项目,您将看到改善;如果循环仅1或2个项目,您将看不到任何好处;对于一项,您将看到性能下降。

Note, I was not careful refactoring, there may be some typos in above code and is only an example. 注意,我不小心重构,上面的代码中可能有一些错别字,仅是示例。

You say you use SVG to iterate??? 您说您使用SVG进行迭代???

I also use a SVG to iterate over the pixels 我还使用SVG遍历像素

Rule of thumb #2. 经验法则#2。 The DOM is slow, SVG is part of the DOM and in my book SVG is really VSG (Very Slow Graphics) and I would say that the main part of the slow down is in whatever you do with the SVG. DOM很慢,SVG是DOM的一部分,在我的书中SVG实际上是VSG(非常慢的图形),我想说,减速的主要部分在于您对SVG所做的一切。

Recommendations: 建议:

  • This function is doing way too much, consider breaking this up so that expressions like this 这个函数做的太多了,考虑将其分解,以便像这样的表达式

      state.bounds[b][1].x > (state.widgetSize.width * 0.604) && state.bounds[b][1].x < (state.widgetSize.width * 0.827) && state.bounds[b][1].y > (state.widgetSize.width * 0.665) && state.bounds[b][1].y < (state.widgetSize.width * 0.778) 

    Are their own function 是自己的功能

  • Consider using _.map instead of the for loop, you are return state anyway, so _.map would be more semantic. 考虑使用_.map而不是for循环,无论如何您都处于返回state ,因此_.map更具语义。

  • Consider breaking up the nesting of if s with functions. 考虑将if s与函数的嵌套分开。 Every time you use a code comment, consider making it a function. 每次使用代码注释时,请考虑使其成为函数。 For example 例如

     // Top right corner if(state.antiRebounce) { state.vy *= -1; state.antiRebounce = false; } else { state.antiRebounce = true; } state.y = state.bounds[b][1].y + state.radius + 1; state.vy *= -1; 

    could be 可能

     const topRightCorner = (state) => { if(state.antiRebounce) { state.vy *= -1; state.antiRebounce = false; } else { state.antiRebounce = true; } state.y = state.bounds[b][1].y + state.radius + 1; state.vy *= -1; return state; } 

Once you broken this giant function into many smaller easier to understand functions. 一旦将这个巨大的功能分解为许多更易于理解的较小功能。 You can use chrome profiling tools to find bottlenecks for performance. 您可以使用chrome性能分析工具来查找性能瓶颈。

https://developers.google.com/web/tools/chrome-devtools/rendering-tools/ https://developers.google.com/web/tools/chrome-devtools/rendering-tools/

With the code broken up, it will be easy to see what part of the script is suffering from performance problems, at that point you can see about how to fix, without doing pre-mature optimization. 随着代码的分解,可以很容易地看到脚本的哪一部分受到性能问题的影响,此时,您可以了解如何解决问题,而无需进行过早的优化。

AS far as I know (but I don't know so much) your situation doesn't give us so much space of manovre because you need to "see" always in the screen all that bouncing objects. 据我所知(但我所不知道的太多),您的情况并没有给我们太多的操作空间,因为您需要始终在屏幕上“看到”所有反弹的物体。 The bound system seems good for me checking for the bounds before a more accurate point collision detection. 边界系统对我来说似乎更好,可以在更精确的点碰撞检测之前检查边界。 How many fps are you trying to render? 您要渲染多少fps? Maybe something could be tuned I guess. 我想也许可以调整一些东西。 Bye 再见

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM