关于alloca的使用和滥用

Has*_*yed 20 c++ memory-management real-time micro-optimization systems-programming

我正在研究一个软实时事件处理系统.我希望尽可能减少代码中具有非确定性时序的调用次数.我需要构造一个由字符串,数字,时间戳和GUID组成的消息.大概std::vectorboost::variant的.

我一直想用alloca在过去类似性质的代码中.然而,当人们研究系统编程文献时,总是会对这个函数调用提出大量警告.就个人而言,我不能想到过去15年中没有虚拟内存的服务器类机器,而且我知道Windows堆栈一次增长一个虚拟内存页面,所以我假设Unices也是如此.这里没有砖墙(再也没有),堆栈就像堆一样可能耗尽空间,那么是什么给出了?为什么人们没有超过阿洛卡?我可以想到许多负责任地使用alloca的用例(字符串处理任何人?).

无论如何,我决定测试性能差异(见下文),并且alloca和malloc之间存在5倍的速度差异(测试捕获了我将如何使用alloca).那么,有变化吗?我们是否应该谨慎对待风并使用alloca(包裹在a中std::allocator)每当我们完全可以确定物体的使用寿命时?

我厌倦了生活在恐惧中!

编辑:

好吧有限制,对于Windows来说这是一个链接时间限制.对于Unix来说,它似乎是可调的.似乎页面对齐的内存分配器是有序的:D任何人都知道通用的便携式实现:D?

码:

#include <stdlib.h>
#include <time.h>

#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>

using namespace boost::posix_time;

int random_string_size()
{
    return ( (rand() % 1023) +1 );
}

int random_vector_size()
{
    return ( (rand() % 31) +1);
}

void alloca_test()
{
    int vec_sz = random_vector_size();

    void ** vec = (void **) alloca(vec_sz * sizeof(void *));    

    for(int i = 0 ; i < vec_sz ; i++)
    {
        vec[i] = alloca(random_string_size());     
    }
}

void malloc_test()
{
    int vec_sz = random_vector_size();

    void ** vec = (void **) malloc(vec_sz * sizeof(void *));    

    for(int i = 0 ; i < vec_sz ; i++)
    {
        vec[i] = malloc(random_string_size());     
    }

    for(int i = 0 ; i < vec_sz ; i++)
    {
        free(vec[i]); 
    }

    free(vec);
}

int main()
{
    srand( time(NULL) );
    ptime now;
    ptime after; 

    int test_repeat = 100; 
    int times = 100000;


    time_duration alloc_total;
    for(int ii=0; ii < test_repeat; ++ii)
    { 

        now = microsec_clock::local_time();
        for(int i =0 ; i < times ; ++i)
        {
            alloca_test();    
        }
        after = microsec_clock::local_time();

        alloc_total += after -now;
    }

    std::cout << "alloca_time: " << alloc_total/test_repeat << std::endl;

    time_duration malloc_total;
    for(int ii=0; ii < test_repeat; ++ii)
    {
        now = microsec_clock::local_time();
        for(int i =0 ; i < times ; ++i)
        {
            malloc_test();
        }
        after = microsec_clock::local_time();
        malloc_total += after-now;
    }

    std::cout << "malloc_time: " << malloc_total/test_repeat << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

输出:

hassan@hassan-desktop:~/test$ ./a.out 
alloca_time: 00:00:00.056302
malloc_time: 00:00:00.260059
hassan@hassan-desktop:~/test$ ./a.out 
alloca_time: 00:00:00.056229
malloc_time: 00:00:00.256374
hassan@hassan-desktop:~/test$ ./a.out 
alloca_time: 00:00:00.056119
malloc_time: 00:00:00.265731
Run Code Online (Sandbox Code Playgroud)

- 编辑:家用机器,铿锵声和谷歌穿孔机的结果 -

G++ without any optimization flags
alloca_time: 00:00:00.025785
malloc_time: 00:00:00.106345


G++ -O3
alloca_time: 00:00:00.021838
cmalloc_time: 00:00:00.111039


Clang no flags
alloca_time: 00:00:00.025503
malloc_time: 00:00:00.104551

Clang -O3 (alloca become magically faster)
alloca_time: 00:00:00.013028
malloc_time: 00:00:00.101729

g++ -O3 perftools
alloca_time: 00:00:00.021137
malloc_time: 00:00:00.043913

clang++ -O3 perftools (The sweet spot)
alloca_time: 00:00:00.013969
malloc_time: 00:00:00.044468
Run Code Online (Sandbox Code Playgroud)

Chr*_*rle 15

首先,即使存在大量虚拟内存并不意味着您的进程将被允许填充它.在*nix上有堆栈大小限制,而堆更宽容.

如果你只是要分配几百/千字节,那么一定要继续.除此之外的任何事情都取决于任何给定系统上的限制(ulimit),这只是一个灾难的处方.

为什么使用alloca()不被视为良好做法?

在我的工作开发盒(Gentoo)上,我的默认堆栈大小限制为8192 kb.这不是很大,如果alloca溢出堆栈,那么行为是未定义的.


DXM*_*DXM 6

我认为你需要在理解什么是alloca时要小心一点.与进入堆的malloc,搜索各种缓冲区的存储桶和链接列表不同,alloca只需要使用堆栈寄存器(x86上的ESP)并将其移动到线程堆栈上创建一个"漏洞",您可以在其中存储您想要的任何内容.这就是为什么它是超快速的,只有一个(或几个)汇编指令.

正如其他人指出的那样,你需要担心的不是"虚拟内存",而是为堆栈保留的大小.虽然其他人将自己限制在"几百字节",只要你知道你的应用程序并小心它,我们已经分配了高达256kb没有任何问题(默认堆栈大小,至少对于visual studio,是1mb,你可以随时如果你需要增加它).

另外你真的不能将alloca用作通用分配器(即将其包装在另一个函数中),因为无论内存分配给你什么,当弹出当前函数的堆栈帧时(即函数退出时),该内存将会消失.

我也看到有些人说alloca不是完全跨平台兼容的,但是如果你正在为特定平台编写特定的应用程序并且你可以选择使用alloca,有时它是你拥有的最佳选择,只要你了解增加堆栈使用的含义.