Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Contiguous Memory

So i'm performing the same function on a large set of objects. To my understanding, it's better to keep this set of objects in contiguous memory space for better time.

std::vector<MyObject> myObjects;
// Fill vector with thousands of objects
for( auto const& obj listMyObjects ) { obj.doThing(); }

Note: myObjects lives for the lifetime of the application and is changing constantly.

In other parts of my code, I want to work directly with a single instance of MyObject. Normally I'd wrap my vector with a class like MyObjectManager and make a getter:

MyObject * obj = MyObjectManager::getObject( "OBJECT_NAME" );
obj->setName( "NEW_NAME" );

But this is error-prone as the pointer may be invalidated if the vector is resized. This leaves me to using an index or id instead, and effect all changes through the the manager

int objId = MyObjectManager::getObject( "OBJECT_NAME" );
MyObjectManager::setObjectName( objId, "NEW_NAME" );

But I don't feel this is the best way as it tightly couples my manager and object class. So I thought about creating an interface for objects that is a friend of the manager:

class MyObject
{
    std::string myName;
}

class MyObjectInterface
{
    int myId;

    MyObjectInterface( int id ) { myId = id; }
    void setName( std::string name ) { MyObjectManager::myObjects.at( myId ).name = name; }
}

class MyObjectManager
{
    friend class MyObjectInterface;
    static std::vector<MyObject> myObjects;

    static MyObjectInterface getObject( std::string name ) { return MyObjectInterface( myObjects.find( name ) ); // Psuedo code }
}

MyObjectInterface obj = MyObjectManager::getObject( "OBJECT1" );
obj.setName( "OBJECT2" );

Note: Excuse issues in the code. This is more about the idea then it is about the code.

While this seems like it'll work, I'm curious if I've reasoned about the issue correctly and whether this solution is over-complicating things? Would love some insight, thanks.

like image 759
jakedipity Avatar asked Jan 28 '26 02:01

jakedipity


1 Answers

I think it is reasonable to use an index or an id instead of a pointer for long-lived references to objects in a vector for the reasons you give. I think it is also reasonable to wrap that id in a class for more type safety.

But I would normally stop short of making that class a full proxy for the real object. If the vector is really changing "constantly" from multiple threads then even something like

MyObjectManager::myObjects.at( myId ).name = name;

is not safe. Between getting the reference to the object using at and setting the name, the vector could have been resized and your reference could be invalidated.

You normally need to have some local guarantee in performance critical code that the vector is not going to be resized and it is safe to use references or pointers. The cost of looking up objects by id in a tight loop will probably outweigh any benefits you are getting from data locality anyway.

Overuse of MyObjectInterface is probably going to be the enemy of data locality given that unlike MyObject they are not guaranteed to be contiguous in memory.

If MyObjectInterface and/or MyObjectManager are actually required to provide complicated synchronization machinery then I suggest you post a separate question that includes that.

like image 167
Chris Drew Avatar answered Jan 29 '26 14:01

Chris Drew



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!