对于可变长度数组使用alloca()比在堆上使用向量更好吗?

时间:2017-09-28 09:03:23

标签: c++ arrays vector stack heap

我有一些代码使用可变长度数组(VLA),它在gcc和clang中编译得很好,但不适用于MSVC 2015。

class Test {
public:
    Test() {
        P = 5;
    }
    void somemethod() {
        int array[P];
        // do something with the array
    }
private:
    int P;
}

代码中似乎有两个解决方案:

  • 使用alloca(),通过绝对确保不访问数组之外​​的元素来考虑the risks of alloca
  • 使用vector成员变量(假设只要P在构造对象后保持不变,vector和c数组之间的开销不是限制因素)

ector将更具可移植性(少#ifdef测试使用哪个编译器),但我怀疑alloca()更快。

矢量实现如下所示:

class Test {
public:
    Test() {
        P = 5;
        init();
    }
    void init() {
        array.resize(P);
    }
    void somemethod() {
        // do something with the array
    }
private:
    int P;
    vector<int> array;
}

另一个考虑因素:当我只在函数外部更改P时,堆上有一个数组,而不是比堆栈上的VLA更快地重新分配?

最大P约为400。

3 个答案:

答案 0 :(得分:3)

您可以并且可能应该使用一些动态分配的heap内存,例如由std::vector管理(如answered by Peter)。您可以使用智能指针或纯粹的原始指针(newmalloc,....),您不应忘记发布(deletefree,... ..)。请注意,堆分配可能比您所认为的快(实际上,大多数情况下,当前笔记本电脑的分配时间远远小于1微秒)。

有时你可以将分配从一些内循环中移出,或者偶尔增长它(对于realloc类似的东西,更好地使用unsigned newsize=5*oldsize/4+10;而不是unsigned newsize=oldsize+1;即具有一些几何增长)。如果你不能使用向量,请确保保持单独的分配大小和使用的长度(如std::vector在内部)。

另一种策略是针对特殊情况小尺寸和大尺寸。例如对于少于30个元素的数组,使用调用堆栈;对于较大的,请使用堆。

如果您坚持在[{3}}上分配(使用VLA - 他们是标准C ++ 11或alloca的常用扩展名,那么明智地限制您的调用帧到几千字节。总呼叫栈受限于某些实现特定限制(例如,在许多笔记本电脑上经常为大约一兆字节或几个)。在某些操作系统中,您可以提高该限制(另请参阅Linux上的call stack

确保在手动调整代码之前进行基准测试。在进行基准测试之前,不要忘记启用setrlimit(2)(例如g++ -O2 -Wallcompiler optimization)。请记住,GCC通常比堆分配贵得多。不要忘记,开发人员的时间也有一些成本(通常与累积的硬件成本相当)。

请注意,使用caches misses或数据也存在问题(不是static variable,不是reentrant安全,不是async-signal-safe -see thread ... 。)并且可读性较差且不太稳健。

答案 1 :(得分:3)

首先,如果您的代码按原样编译任何C ++编译器,那么您就会很幸运。 VLA不是标准C ++。 Some compilers支持他们作为扩展。

使用alloca()也不是标准的,因此在使用不同的编译器时不能保证可靠(甚至根本不能)工作。

在许多情况下,使用static向量是不可取的。在您的情况下,它提供的行为可能与原始代码不同。

您可能希望考虑的第三个选项是

 // in definition of class Test
void somemethod()
{
    std::vector<int> array(P);      // assume preceding #include <vector>
    // do something with array
}

向量本质上是一个动态分配的数组,但在函数返回时将在上面正确清理。

以上是标准C ++。除非您进行严格的测试和分析,以提供性能问题的证据,否则这应该足够了。

答案 2 :(得分:1)

为什么不将数组作为私有成员?

const template: Electron.MenuItemConstructorOptions[] = [{
        label: 'Edit',
        submenu: [
            { role: 'undo' },
            { role: 'redo' },
            { type: 'separator' },
            { role: 'cut' },
            { role: 'copy' },
            { role: 'paste' },
            { role: 'pasteandmatchstyle' },
            { role: 'delete' },
            { role: 'selectall' }
        ]
    },
    {
        label: 'View',
        submenu: [
            { role: 'reload' },
            { role: 'forcereload' },
            { role: 'toggledevtools' },
            { type: 'separator' },
            { role: 'resetzoom' },
            { role: 'zoomin' },
            { role: 'zoomout' },
            { type: 'separator' },
            { role: 'togglefullscreen' }
        ]
    },
    { role: 'window', submenu: [{ role: 'minimize' }, { role: 'close' }] },
    {
        role: 'help',
        submenu: [{
            label: 'Learn More',
            click() {
                require('electron').shell.openExternal('https://electron.atom.io');
            }
        }]
    }
];

由于您已经指定了数组的最大可能大小,您还可以查看boost::small_vector之类的内容,可以像以下一样使用:

#include <vector>

class Test
{
public:
    Test()
    {
        data_.resize(5);
    }
    void somemethod()
    {
        // do something with data_
    }
private:
    std::vector<int> data_;
}

您应该剖析以确定这是否真的更好,并且请注意这可能会占用更多内存,如果有许多#include <boost/container/small_vector.hpp> class Test { public: Test() { data_.resize(5); } void somemethod() { // do something with data_ } private: using boc = boost::container; constexpr std::size_t preset_capacity_ = 400; boc::small_vector<int, preset_capacity_> data_; } 个实例,这可能是一个问题。