cmajor-lang/cmajor

[Feature Request] Custom Allocator Support

Closed this issue · 4 comments

I'm following the directions regarding The Cmajor native API,
It would be great to be able to optionally use classes with a custom allocator.

Reasons:

  • Multiple large projects at different companies I've worked on have used them, for different reasons.
  • They're popular in the game development world
  • Makes it easier for an integrator to track the runtime memory cost

One could argue about just hooking new/delete on the integrator's side, but I haven't checked to see if Cmajor is using new/delete or malloc or something else. Also, one might want to use Allocator A for the audio engine, and Allocator B for video, etc.

That's an interesting suggestion. I can certainly see situations where this would be helpful.

I'll investigate and see how practical it would be.

Thanks. The first place I encountered this was with the Engine API.

struct Engine
{
    /// This creates an empty engine, which is basically a null pointer.
    /// To create a usable Engine, use the Engine::create() function.
    Engine() = default;
    
    /* ... */
   
    //==============================================================================
    /// Tries to create a engine, returning a null object if it fails for some reason.
    ///
    /// For a list of the available strings that you can pass to engineType, see
    /// the getAvailableEngineTypes() function. For a default (LLVM JIT) engine,
    /// just leave this empty.
    ///
    /// The engineCreationOptions parameter is used for supplying obscure
    /// implementation-specific flags to the underlying engine factory, so unlikely
    /// to be needed by most users.
    static Engine create (const std::string& engineType = {},
                          const choc::value::Value* engineCreationOptions = nullptr);

I could've potentially used the placement new mechanism, but the exposed constructor doesn't do much, and the factory create function doesn't allow me to supply pre-allocated memory.

There's a number of ways to write this. One example I know I've used in the past with the Xerces XML library. I'm not necessarily suggesting that as a solution, but just an example. This is also something that could be done iteratively. For example, be able to allocate a lot of the housekeeping classes like Engine and Performer without necessarily making you rewrite the JITed instructions. However, forward-thinking to embedded devices where the housekeeping classes might need to have memory on chip A and the audio samples on a faster-access chip B might be important. I'm not an expert in that area, but I've seen things along those lines.

sletz commented

In case it helps: in Faust, a custom memory manager can be used.

(I'm just revisiting some of the old issues that were never closed...!)

I appreciate the reasons for this FR, but not sure how we could implement it in a practical way. Our codebase has many thousands of places where it allocates, both directly and via our heavy use of std library containers. It'd be very difficult and disruptive to retro-fit a custom allocator system to every place it might be needed, and we'd almost certainly miss a few things. I mean.. how would we avoid std::string allocating from the the heap in the countless places where we use it..?

It's an interesting topic and maybe one day we'll have a compelling reason to do the work, but I'm going to close this FR for now :)