24

私はソフトリアルタイムイベント処理システムに取り組んでいます。非決定的なタイミングを持つコード内の呼び出しを最小限に抑えたいと考えています。文字列、数字、タイムスタンプ、および GUID で構成されるメッセージを作成する必要があります。おそらくstd::vectorboost::variantの。

私はいつもalloca似たような性質の過去のコードで使いたいと思っていました。ただし、システム プログラミングの文献を調べると、この関数呼び出しに対して常に大きな注意が払われています。個人的には、過去 15 年間、仮想メモリを持たないサーバー クラスのマシンは考えられません。また、Windows スタックが一度に 1 ページずつ仮想メモリを拡張するという事実を知っているので、ユニスも同様です。ここには(もう)レンガの壁はありません。スタックはヒープと同じくらいスペースが不足する可能性があります。人々がアロカよりもガガにならないのはなぜですか? 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;
}

出力:

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

--編集: ホーム マシン、clang、および google perftools での結果--

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
4

5 に答える 5

18

まず第一に、仮想メモリがたくさんあるからといって、プロセスがそれをいっぱいにできるわけではありません。*nix ではスタック サイズに制限がありますが、ヒープはより寛容です。

数百/千バイトしか割り当てない場合は、必ず先に進んでください。それを超えるものは、特定のシステムに設定されている制限 (ulimit) に依存します。これは、災害のレシピにすぎません。

alloca() の使用が良い習慣と見なされないのはなぜですか?

仕事中の開発ボックス (Gentoo) では、デフォルトのスタック サイズ制限が 8192 kb です。これはそれほど大きくなく、alloca がスタックをオーバーフローした場合の動作は未定義です。

于 2011-04-27T16:53:41.450 に答える
6

allocaが実際に何であるかを理解するには、少し注意する必要があると思います. ヒープに移動し、バケットとさまざまなバッファーのリンクされたリストを検索する malloc とは異なり、 alloca は単にスタック レジスタ (x86 の ESP) を取得して移動し、スレッドのスタックに必要なものを格納できる "穴" を作成します。そのため、1 つ (または少数) のアセンブリ命令だけで超高速です。

他の人が指摘したように、心配する必要があるのは「仮想メモリ」ではなく、スタック用に予約されているサイズです。他のものは「数百バイト」に制限されていますが、アプリケーションを知っていて注意している限り、問題なく最大 256kb を割り当てました (少なくともビジュアル スタジオのデフォルトのスタック サイズは 1mb であり、いつでも使用できます)。必要に応じて増やしてください)。

また、 alloca を汎用アロケーターとして使用する (つまり、別の関数内にラップする) ことは実際にはできません。これは、alloca が割り当てたメモリが何であれ、現在の関数のスタック フレームがポップされると (つまり、関数が終了するとき)、そのメモリが失われるためです。

また、alloca は完全なクロスプラットフォーム互換ではないと言う人もいますが、特定のプラットフォーム向けの特定のアプリケーションを作成していて、alloca を使用するオプションがある場合、場合によっては alloca が最良のオプションである場合もあります。スタック使用量の増加の影響を理解しています。

于 2011-04-27T17:22:12.550 に答える
3

認識されていない点の 1 つは、スタックはしばしば連続しているのに対し、ヒープはそうではないということです。一般に、スタックがヒープと同じくらいメモリ不足になる可能性が高いとは言えません。

C++ では、オブジェクト インスタンスがローカルとして宣言されているのを見るのは非常に一般的です。これはalloca、N バイトのブロックではなく、構造化されたメモリのようなものです。スタックベースのメモリを使用することをお勧めします。C++ プログラムでmalloc(または) を使用するよりも、オブジェクト インスタンスを RAII ローカルとして宣言するほうがよいでしょう 。alloca例外を安全にするためのすべてのfree呼び出し...

これは通常、オブジェクトのスコープがこの関数とその呼び出された関数に限定されていることを前提としています。そうでない場合は、通常、スタックベースのメモリを使用することはお勧めできません。

于 2011-04-27T18:46:52.373 に答える
2

Windowsスタックは拡大しません。予約されたサイズはリンク時に設定されますが、このサイズ内のページは必要な場合にのみコミットされます。http://msdn.microsoft.com/en-us/library/ms686774%28v=vs.85%29.aspを参照してください。デフォルトの予約サイズは1Mbであるため、を使用するとこれを簡単に超える可能性がありますalloca()

于 2011-04-27T16:57:14.613 に答える