HTML and CSS Reference
In-Depth Information
Custom tools represent an opportunity to improve code maintainability while using flat structures in this
way. For example, in the case of flattening an array of structures into a single Array or Float32Array, a simple code
transformation could generate the code in Listing 4-4 from source code of the form, as shown in Listing 4-9.
Listing 4-9. Maintainable Source Code That Could Be Used to Generate Listing 4-4
for (var i = 0 ; i < endIdx ; i = i + PARTICLES_STRIDE)
var velocity_x = particles[i+PARTICLES_VEL_X];
var velocity_y = particles[i+PARTICLES_VEL_Y];
var velocity_z = particles[i+PARTICLES_VEL_Z];
This allows properties to be added and removed with relative ease. More complex tool-based solutions are
conceivable that could generate the same output from even higher level code, particularly where more accurate type
information is available at compile time (as is often the case for languages such as TypeScript, which the Turbulenz
engine has adopted throughout the core engine). Tooling of this kind may also represent a trade-off if it introduces a
build step where one was previously not necessary.
Late Unpacking
Data cannot always easily be flattened, and sometimes the runtime representation is necessarily a memory-hungry
hierarchy of objects. In these cases, it is sometimes possible and preferable to encode data into custom byte-code or
strings, and decode the data on the fly, as required.
There is an obvious trade-off made with CPU resources here. The encoded data must be unpacked into its
equivalent object hierarchy, and potentially this process must be repeated each time the hierarchy is destroyed.
However, since the TypedArray interfaces give full access to raw memory buffers, it is possible to decode (and encode)
even binary compression formats to save memory effectively.
Best Practices
The concepts discussed above are relatively simple, and imply some straightforward best practices. Here we describe
a few of these, as well as some recommendations based on the experience of our engineers developing engine
technology and porting and creating high-performance 3D HTML5 games.
The importance of measurement has already been mentioned above, but this cannot be understated. The
behavior of JavaScript and HTML5 implementations is not just varied, but rapidly changing. The true effectiveness
of a given optimization on execution speed and memory consumption is very rarely quantifiable in terms of a single
number. In the worst case, diversity of HTML5 implementations may mean that developers are required to maintain
several code paths and determine which to enable based on browser version.
The browsers themselves often provide useful tools, including memory and code profilers as well as convenient
debugging environments. In general, these may have some limitations (for example, JavaScript heap profilers may not
take into account raw memory used by ArrayBuffers, or graphics resources such as textures or vertex buffers), but they
form a useful part of the toolbox for measuring code behavior. Keep in mind that different profiling tools may have
different side effects, so it is usually most effective to compare the results from several of them (ideally from two or
three major browsers) before drawing conclusions.
At the code level, we have found that it is important to maintain clear relationships between data structures,
including keeping the responsibility for creation and destruction well defined. As well as making code easier to
understand and maintain, clear ownership policies allow applications to accurately and consistently “null-out”
references to unused objects. This helps to avoid memory leaks, which in JavaScript refers to unnecessary objects on
the heap, wasting both memory and CPU resources, but kept alive by dangling references.
Search WWH ::

Custom Search