Several techniques and good practices can help to improve the Structure builder module performance and memory use. The research and the measures taken are described below.
[related to Three.js] This has to do with the optimization of the 3d scene elements representation (mainly atoms and bonds due to their number) and the scene rendering
Free objects not being used
(memory opt.) The Three.js library requires to explicitly free the resources not being used longer. In our case it's specially interesting to free the meshes making up a structure when we don't want to work on it longer (when we load other structure or reset the viewer). This prevents from meaningful memory leaks that could lead the application to crash in long use sessions.
We can use the WebGLRenderer.info attribute to check the resources being help by the application. It seems that the geometries are being freed properly. I'm not so sure (
PENDING) for the textures (this is less important because they take up far less memory)
Using more efficient Threejs data structures
(memory and performance opt.) Meaningful geometries like the ones representing atoms and bonds are switched to BufferGeometry versions: SphereBufferGeometry and CylinderBufferGeometry repectively.
For more info: BufferGeometry
Adapting the detail level of the atom geometry to the context
(memory and performance opt.) Up to now the atom geometry wasn't optimized at all. The number of segments was fixed at 24 (widthSegments and heightSegments) meaning around 554 vertices per atom.
The first testing was applying the next formula to the segments calculation: segment = 5 + 12*atom_radius. This way you are guaranteeing a decent minimun number of segments even for small atoms. The test was done with 3 structures (different sizes):
|Structure file||24 segments||5 + 12*atom_radius segments||5 + 5*atom_radius segments|
|CdSrTa.in (12 atoms)||6648 (total) 554 (fixed)||2576 (total) 214.6 (average) / 39%|
|BiAgBrH.in (312 atoms)||172848 (total) 554 (fixed)||26000 (total) 83.3 (average) / 15%|
|Supercell (1296 atoms)||717984 (total) 554 (fixed)||125712 (total) 97 (average) / 17%||62208 (total) 48 (average) / 9%|
The last column is a more aggressive segment reduction that can make sense for huge structures as the camera should keep further.
The percentages represent the final number of vertices relating to the initial one.
I've also reduce the radialSegments of bonds geometry but in this case the gain is small (the number of vertices per bond is smaller and fixed).
At the beginning it seemed the vertices optimization will play an important role in order to reduce the memory use and I designed this optimization expression (good trade-off between geometry quality and number of vertices):
nSegments = 5 + Math.ceil(15*(1 - numAtoms/2500)*radius)
This adds a new variable (the size of the structure, number of atoms) to determine the segments number of the atoms geometry.
But later once using the BufferGeometry versions of the primitives I realized the vertices optimization is hardly worth it. Some figures (testing on the supercell - 1300 atoms):
Reusing 3D objects attributes (geometry, materials, etc) for atoms and bonds
All the bonds meshes need the same material configuration, so one only Material object can be shared for them. Specially interesting for structures with high number of bonds per atom.
Example of memory reduction:
CdSrTa.in structure (12 atoms, 62 bonds) -> supercell 5x5x5 (1500 atoms, 7870 bonds).
Memory use change: 300M/200M -> 240M/140M (after the optimization)
PENDING for atoms
Optimizing the operations with single or few structure elements
Examples: change the position of an atom, remove an atom, etc.
This optimization consists on modifying as fewer scene elements as possible and specially preventing from removal and new creation of lots of elements.
This requires a more fine-grained structure change event. The event needs some parameters in order to communicate the specific change (atom-change, atom-removal, atom-creation...).
All the single operations with atoms (change, removal and creation) could be done in a very efficient way if it wasn't for the required recalculation of bonds. I've been refactoring and testing the atom change operation and it's the bonds recalculation what does the operation slow and memory demanding (temporarily, eventually the memory is freed).
Finally, we will wait until the bonds refactoring (
PENDING): the bonds will be part of the Structure model (instead of mere graphical objects), to fix this performance issue. That way, the solution will be easy and natural: the atoms and bonds will be referenced and will be easy to manipulate them.
Structure representation and algorithms
The structure representation (data model) and the algorithms working on it can be optimized to work with huge structures efficiently
Idea: use Typed array instead of regular arrays
Create efficient math functions/operators
Idea: get rid of mathjs library
Idea: implement our own math operators
The regular UI can be optimized to work with huge structures efficiently
Study the behaviour of non-responsive UI components
e.g: atoms list when you remove one