Home > OS >  Problems that may arise when initializing arrays on stack inside a function scope with an N size_t p
Problems that may arise when initializing arrays on stack inside a function scope with an N size_t p

Time:09-14

Say for example I have a function that takes some argument and a size_t length to initialize an array on stack inside a function.

Considering the following:

  • Strictly the length can only be on the range of 1 to 30 (using a fixed max buffer length of 30 is not allowed).
  • The array only stays inside the function and is only used to compute a result.
int foo(/*some argument, ..., ... */ size_t length) {
    uint64_t array[length];
    int some_result = 0;
    // some code that uses the array to compute something ...
    return some_result;
}

In normal cases I would use an std::vector, new or *alloc functions for this but... I'm trying to optimize since this said function is being repeatedly called through out the life time of the program, making the heap allocations a large overhead.

Initially using an array on stack with fixed size is the solution that I have come up with, but I cannot do this, for some reasons that I cannot tell since it would be rude.

Anyway I wonder If I can get away with this approach without encountering any problem in the future?

CodePudding user response:

In the rare cases where I've done some image processing with large fixed sized temp buffers or just wanted to avoid the runtime for redundant alloc/free calls, I've made my own heap.

It doesn't make a lot of sense for small allocations, where you could just use the stack, but you indicated your instructor said not to do this. So you could try something like this:

template<typename T>
struct ArrayHeap {
    
    unordered_map<size_t, list<shared_ptr<T[]>>> available;
    unordered_map<uint64_t*, pair<size_t, shared_ptr<T[]>>> inuse;

    T* Allocate(size_t length) {
        auto &l = available[length];
        shared_ptr<T[]> ptr;
        if (l.size() == 0) {
            ptr.reset(new T[length]);
        } else {
            ptr = l.front();
            l.pop_front();
        }
        inuse[ptr.get()] = {length, ptr};
        return ptr.get();
    }

    void Deallocate(T* allocation) {
        auto itor = inuse.find(allocation);
        if (itor == inuse.end()) {
            // assert
        } else {
            auto &p = itor->second;
            size_t length = p.first;
            shared_ptr<T[]> ptr = p.second;
            inuse.erase(allocation);

            // optional - you can choose not to push the pointer back onto the available list
            // if you have some criteria by which you want to reduce memory usage
            available[length].push_back(ptr);
        }
    }
};

In the above code, you can Allocate a buffer of a specific length. The first time invoked for a given length value, it will incur the overhead of allocating "new". But when the buffer is returned to the heap, the second allocation for the buffer of the same length, it will be fast.

Then your function can be implemented like this:

ArrayHeap<uint64_t> global_heap;

int foo(/*some argument, ..., ... */ size_t length) {
    uint64_t* array = global_heap.Allocate(length);

    int some_result = 0;
    // some code that uses the array to compute something ...

    global_heap.Deallocate(array);
    return some_result;
}

CodePudding user response:

Personally I would use a fixed size array on the stack, but if there are reasons to prohibit that then check if there are any against the alloca() method.

man 3 alloca

  • Related