hi @sigvaldm
I don’t claim to be an expert but I’ve done similar work and I can mention my thought process. Maybe others will chime in and I’ll learn something along the way.
The first thing you should know if that while C++ is primarily object-oriented (although non-OO style is possible), Numba is primarily multiple-dispatch based. OO patterns are possible with @jitclass
and structref
. Both are experimental which means that you will find rough edges, especially on complicated use cases (likely to appear if you have a complex C++ codebase in place).
In my main project at work I have a situation similar to yours (large array of objects, which contain a few member variables). I designed it from the beginning thinking about Numba, and the most efficient structure I came up with was a structured array (https://numpy.org/doc/stable/user/basics.rec.html). That will put the entirety of your data (not just the 3-component vector) in linear memory (in the heap). AFAIK, this is equivalent to a C-style array of struct
.
Doing that, however, will make it harder to keep some OO syntax patterns in your code. Things like individual object methods, eg object_array[2].obj_method()
are possible but not straightforward to implement. It would be easier to write my_method(object_array)
to operate on all elements, or my_method(object_array[2])
to operate one element at a time. In numba, it is easier to implement overloaded functions that object methods.
If you want to stay closer to your current design, you should look into both @jitclass
and structref
. Each has advantages and disadvantages. In any case, and going to your main question, to keep your 3-element vector in linear memory, you should make that element a numpy array. For example
from numba.experimental import jitclass
from numba.typed import List
from numba.types import float64
import numpy as np
@jitclass({'my_vect': float64[::1]})
class MyObject:
def __init__(self, x, y, z):
self.my_vect = np.empty(3, np.float64)
self.my_vect[0] = x
self.my_vect[1] = y
self.my_vect[2] = z
obj1 = MyObject(1,2,3)
obj2 = MyObject(4,5,6)
list_objects = List([obj1, obj2])
That above assumes linear memory is the most important property for you. If you want linear memory and fixed size, you can use Tuple, but in that case you’ll also get immutability, which might or not work for you.
Some of the main constraints of this latter approach are around parallelization. AFAIK (and I’m curious to hear from a core dev about this point), it’s not possible to get automatic parallelization (prange
and parallel=True
) with a list of jitclasses. Also, jitclasses are not pickable, and therefore you cannot use multiprocessing either.
I hope this helps with your decision.
cheers,
Luk