Scope of application for optimization

Recently IN the game engine performance optimization, about the JS performance of some content to share here.

Two points need to be made clear:

First, the JS performance issues discussed in this article are exposed only when there is a lot of execution. Generally speaking, a game with 60fps will need to be executed more than 2000 times per frame. If the execution frequency does not reach the above order of magnitude, performance will not improve significantly.

Second, these performance bonuses are in some cases worth sacrificing code structure and readability in real projects. Many times, you need to sacrifice a little bit of performance to make your code more maintainable. Don’t over-optimize your project.

What points can be optimized

The following points have been shown to have an impact on project performance when executed in large numbers.

A function call

Design patterns and refactoring principles often tell us:

A function does only one thing. If several functions are doing the same thing, then abstract out an object

Yes, these principles are useful in business logic development, making code easier to maintain while having little impact on performance. However, in some cases it is not without performance overhead. When you make too many function calls, you will find that performance slows down. Every time I reduced the number of function calls (copying the code from the function directly to the current call location), performance improved.

For example, in rendering engines we often have matrix calculations, and we typically abstract out a matrix class:

matrix1.append(matrix2)
Copy the code

To implement matrix multiplication, but if the code is in the main rendering loop, you might want to consider expanding it like this:

matrix1.a = matrix2.a * matrix1.a + matrix2.b * matrix1.c;
// ...
Copy the code

This can really improve performance (in the case of a lot of execution), and if your function call path is too long, some process simplification is recommended.

Except in the most extreme cases, architecturally reduce function calls, and do not unbundle them.

The scope chain

When js is looking for a variable, it will start from the current scope and look up the upper level successively. It follows that access to local variables is always the fastest and access to global variables is always the slowest.

The following cases:

var array = [];
function getName() {
    array[0] = 0;
    array[1] = 0;
    array[2] = 0;
    array[3] = 0;
    // ...
}
Copy the code

Performance will be lower than:

var array = [];
function getName() {
    var array = array;
    array[0] = 0;
    array[1] = 0;
    array[2] = 0;
    array[3] = 0;
    // ...
}
Copy the code

If you call a global or outer variable multiple times in a function, remember to save it to a local variable first.

This keyword

Calling this keyword multiple times will cause your JS to execute slowly. Because when you access an object property, js executes by looking up the prototype chain until the property is found. That’s a lot of overhead.

Here’s an example:

for(var i = 0; i < 100; i++) {
    this.array[i] = i;
}
Copy the code

Performance below:

var array = this.array;
for(var i = 0; i < 100; i++) {
    array[i] = i;
}
Copy the code

If an object property is accessed multiple times (or even looped), it is recommended to save the property in a local variable before using it.

Mathematical operations

Of the four operations in JS, division is the slowest, followed by multiplication. Of the mathematical functions encapsulated in Math, the sine and cosine functions execute slowest.

Here’s an example:

// c = a * b; f = a * e;Copy the code

Performance below:

// A is 0 in most casesif(a == 0) {
    c = 0;
    f = 0;
} else {
    c = a * b;
    f = a * e;
}
Copy the code

Avoid unnecessary multiplication and division, and cache sines and cosines whenever possible. In pixi.js, the rotation of the display object is calculated using trigonometric functions, and the engine is marked with dirty processing. In EGRET, the global trig function calculation method was optimized by table lookup.

If you look closely in the main loop method, there may be many similar optimizations in the project.

Array push and POP operations

A lot of calls to the array push and POP methods, and if those calls are in the loop, unfortunately, it can cause performance degradation.

If you have a structure like this in your render loop:

var matrix = Matrix.create();
// do something
Matrix.release(matrix);
Copy the code

And object pooling is implemented like this:

// Create matrix object matrix.create =function() {
    returnMatrix.pool.pop() || new Matrix(); } // release the matrix object matrix.release =function(matrix) {
    Matrix.pool.push(matrix);
}
Copy the code

Then your main loop will be slowed down by push and pop.

In general, engines use a lot of temporary objects. In certain cases, a global temp object can be used instead of a pool of objects:

For example, in the above example, we could define a matrix object $tempMatrix for temporary use within the engine:

Matrix.$tempMatrix = new Matrix();
Copy the code

You only need to:

var matrix = Matrix.$tempMatrix;
// do something
matrix.identify();
Copy the code

Careful students might say that by using one object repeatedly in this way, if the code logically requires multiple Temp objects, the above implementation will not solve the need. Yeah, well, then we need to do it another way. However, for certain cases, the above optimization is simple and effective.

conclusion

In summary, most of these so-called performance optimization points are weaknesses in THE runtime of THE JS language, which may not be repeated in other languages. For example, the function calls mentioned above do not significantly affect performance in languages such as c++.

In addition, if you are not similar to the engine such a low-level product, these things to understand also got.

Again, never over-optimize!!