在函数作用域内使用 N size_t参数初始化堆栈上的数组时可能会出现问题?

Problems that may arise when initializing arrays on stack inside a function scope with an N size_t parameter?

提问人:himynameisjm 提问时间:9/14/2022 更新时间:9/14/2022 访问量:77

问:

例如,假设我有一个函数,它接受一些参数和 a 来初始化函数内堆栈上的数组。size_t length

考虑到以下几点:

  • 严格来说,只能在 1 到 30 的范围内(不允许使用固定的最大缓冲区长度 30)。length
  • only 保留在函数内部,仅用于计算结果。array
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;
}

在正常情况下,我会为此使用 ,或函数,但是......我正在尝试优化,因为该函数在程序的整个生命周期中被反复调用,使堆分配产生很大的开销。std::vectornew*alloc

最初在堆栈上使用固定大小的数组是我提出的解决方案,但是由于某些原因,我无法做到这一点,因为这是不礼貌的。

无论如何,我想知道我是否可以摆脱这种方法而不会在未来遇到任何问题?

C++ 数组函数 堆栈 堆栈分配

评论

2赞 Avi Berger 9/14/2022
好吧,第一个问题是它可能无法编译。它是非标准的,尽管一些编译器确实接受它作为语言扩展。
2赞 Artemy Vysotsky 9/14/2022
您可以使用 std::p mr::vector
1赞 Daniel Langr 9/14/2022
你能使用静态数组吗?或者,静态向量?
1赞 Avi Berger 9/14/2022
我将对 @DanielLangr 的建议添加一个注释,即这将使函数不是线程安全的。 可能会通过未公开的粗鲁测试,并且只有在达到峰值大小之前才会重新分配。static std::vector<uint64_t> array; array.resize(length);
1赞 user4581301 9/14/2022
不允许使用固定的最大缓冲区长度 30,这很不幸,因为除非函数是递归的,否则它可能是最好的解决方案。

答:

1赞 selbie 9/14/2022 #1

在极少数情况下,我使用大型固定大小的临时缓冲区进行了一些图像处理,或者只是想避免冗余分配/空闲调用的运行时,我创建了自己的堆。

对于小额分配来说,这没有多大意义,你可以只使用堆栈,但你表示你的教练说不要这样做。所以你可以尝试这样的事情:

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);
        }
    }
};

在上面的代码中,您可以使用特定长度的缓冲区。第一次为给定的长度值调用时,将产生分配“new”的开销。但是当缓冲区返回到堆时,对相同长度的缓冲区进行第二次分配时,它会很快。Allocate

那么你的函数可以这样实现:

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;
}
1赞 Outtruder 9/14/2022 #2

就我个人而言,我会在堆栈上使用固定大小的数组,但如果有理由禁止它,请检查是否有任何针对 alloca() 方法的数组。

人 3 阿洛卡