The V8 engine is Google’s open source, high-performance JavaScript engine written in C++. Alongside Google Chrome, it can also be found in MongoDb , Node.js, and many other popular applications.
It’s very interesting to discover what makes V8 so fast and which solutions were used to achieve this goal.
OOP, Design Patterns, and performance.
There is an odd conviction among some C/C++ programmers. They seem to think that using OOP and design patterns decrease their application performance. V8 is a good example to prove that it’s not true. V8 implements many design patterns and it’s well-optimized.
Here are some patterns used:
Factory
When the javascript engine executes a script, it creates the instances of each variable, function or array encountered. JSObject is the parent class of all these kind of objects.
Here’s the list of all classes inheriting from JSObject:
V8 implements a factory class to create the objects needed and the method Factory::NewJsObject is used for this purpose.
Here are all the methods using it:
The factory is not used directly by the V8 classes, but it’s invoked from the Heap class which adds another indirection to the implementation.
Visitor:
As well explained in the visitor wiki page:
The visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to follow the open/closed principle.
Like the factory pattern, this pattern adds also some indirections to the implementation. But it makes the code more readable and maintainable.
V8 source code contains many classes implementing the visitor pattern.
Even if the V8 developers have to optimize its execution, they don’t care if some indirections are added to the code. It’s true that using design patterns and some C++ mechanisms could have an optimization impact due to these added indirections generated by their implementations. But it’s more related to a micro-optimization. Significant macro optimization depends more on your design choices specific to your application needs.
V8 design choices to optimize its execution
1- Hidden Class and Fast Property Access:
JavaScript is a dynamic programming language: properties can be added to, and deleted from, objects on the fly. This means an object’s properties are likely to change.
As specified before the JSObject is the parent class of JSFunction which represents a javascript function, or JSValue which represents a javascript value. However, There’s no class inheriting from JSObject representing a Class like Function or Value. Most JavaScript engines use a dictionary-like data structure as storage for object properties, each property access requires a dynamic lookup to resolve the property’s location in memory.
This approach makes accessing properties in JavaScript typically much slower than accessing instance variables in programming languages like Java and Smalltalk. In these languages, instance variables are located at fixed offsets determined by the compiler due to the fixed object layout defined by the object’s class. Access is simply a matter of a memory load or store, often requiring only a single instruction.
V8 use hidden class concept to reduce the time required to access JavaScript properties. V8 does not use dynamic lookup to access properties. Instead, V8 dynamically creates hidden classes behind the scenes.
2- Dynamic machine code generation
V8 compiles JavaScript source code directly into machine code when it is first executed. There are no intermediate byte codes, no interpreter. Property access is handled by inline cache code that may be patched with other machine instructions as V8 executes.
3- Efficient garbage collector
V8 reclaims memory used by objects that are no longer required in a process known as garbage collection. To ensure fast object allocation, short garbage collection pauses, and no memory fragmentation V8 employs a stop-the-world, generational, accurate, garbage collector. This means that V8:
- stops program execution when performing a garbage collection cycle.
- processes only part of the object heap in most garbage collection cycles. This minimizes the impact of stopping the application.
- always knows exactly where all objects and pointers are in memory. This avoids falsely identifying objects as pointers which can result in memory leaks.
Conclusion:
Decide to not use the OOP and the design patterns because the optimization is your first priority, is maybe a bad idea. You could just gain some microseconds and lose the readability and maintainability of your code.
Significant macro optimization depends more on your design choices specific to your application needs.