5

私は 2 つの同じサイズの配列を割り当てています。1 つはスタック上に、もう 1 つはヒープ上にあり、それらを単純な代入で繰り返し処理しています。

実行可能ファイルは、メイン スレッド スタックに 40 MB を割り当てるようにコンパイルされます。

このコードは、/STACK:41943040 リンカー タグを使用して vc++ でコンパイルすることのみがテストされています。

#include "stdafx.h"
#include <string>
#include <iostream>
#include <malloc.h>
#include <windows.h>
#include <ctime>

using namespace std;

size_t stackavail()
{
    static unsigned StackPtr;   // top of stack ptr
    __asm mov [StackPtr],esp    // mov pointer to top of stack
    static MEMORY_BASIC_INFORMATION mbi;            // page range
    VirtualQuery((PVOID)StackPtr,&mbi,sizeof(mbi)); // get range
    return StackPtr-(unsigned)mbi.AllocationBase;   // subtract from top (stack grows downward on win)
}

int _tmain(int argc, _TCHAR* argv[])
{
    string input;

    cout << "Allocating 22mb on stack." << endl;
    unsigned int start = clock();
    char eathalfastack[23068672]; // approx 22mb
    auto length = sizeof(eathalfastack)/sizeof(char);
    cout << "Time taken in ms: " << clock()-start << endl;

    cout << "Setting through array." << endl;
    start = clock();
    for( int i = 0; i < length; i++ ){
        eathalfastack[i] = i;
    }
    cout << "Time taken in ms: " << clock()-start << endl;
    cout << "Free stack space: " << stackavail() << endl;


    cout << "Allocating 22mb on heap." << endl;
    start = clock();
    // auto* heaparr = new int[23068672]; // corrected
    auto* heaparr = new byte[23068672];
    cout << "Time taken in ms: " << clock()-start << endl;

    start = clock();
    cout << "Setting through array." << endl;
    for( int i = 0; i < length; i++ ){
        heaparr[i] = i;
    }
    cout << "Time taken in ms: " << clock()-start << endl;

    delete[] heaparr;
    getline(cin, input);
}

出力は次のとおりです。

    Allocating 22mb on stack.
    Time taken in ms: 0
    Setting through array.
    Time taken in ms: 45
    Free stack space: 18872076
    Allocating 22mb on heap.
    Time taken in ms: 20
    Setting through array.
    Time taken in ms: 35

スタック配列の反復がヒープ上の同じものより遅いのはなぜですか?

EDIT:nneonneoは私のエラーを吐き出しました

出力は同じになります。

    Allocating 22mb on stack.
    Time taken in ms: 0
    Setting through array.
    Time taken in ms: 42
    Free stack space: 18871952
    Allocating 22mb on heap.
    Time taken in ms: 4
    Setting through array.
    Time taken in ms: 41

以下のÖöTiibの回答に従ってビルドをリリースします。

    Allocating 22mb on stack.
    Time taken in ms: 0
    Setting through array.
    Time taken in ms: 5
    Free stack space: 18873508
    Allocating 22mb on heap.
    Time taken in ms: 0
    Setting through array.
    Time taken in ms: 10
4

3 に答える 3

9

あなたの配列は同じサイズではありません。sizeof(char[23068672]) != sizeof(int[23068672])、要素はさまざまなタイプです。

于 2012-10-16T04:10:22.103 に答える
1

あなたの PC に何か問題があります。私の古い Pentium 4 では、このようなスタックベースの文字配列を割り当てるのに 15 ミリ秒かかります。デバッグ版か何かで試しましたか?

于 2012-10-16T04:32:27.150 に答える
0

あなたの質問には2つの部分があります:

  1. スタックとヒープのスペースの割り当て
  2. スタック上のメモリ位置へのアクセスとグローバルに表示されるメモリ位置へのアクセス

スペースの割り当て

まず、スタックにスペースを割り当てる方法を見てみましょう。私たちが知っているスタックは、x86 アーキテクチャでは下向きに成長します。したがって、スタックにスペースを割り当てるために必要なことは、スタック ポインターをデクリメントすることだけです。アセンブリ命令は 1 つだけです (dec sp、#amount)。このアセンブリ命令は、関数のプロローグ (関数設定コード) に常に存在します。したがって、私の知る限り、スタックにスペースを割り当てるのに時間がかかることはありません。スタックにスペースを割り当てるコスト = (sp 操作の減分)。最新のスーパースカラー マシンでは、この命令のこの実行は他の命令とオーバーラップします。

一方、ヒープにスペースを割り当てるには、new/malloc へのライブラリ呼び出しが必要です。ライブラリ呼び出しは、最初にヒープにスペースがあるかどうかを確認します。はいの場合、最初に使用可能なアドレスへのポインターを返すだけです。スタックに空きがない場合は、brk システム コールを使用して、追加ページのページ テーブル エントリを変更するようカーネルに要求します。システムコールはコストのかかる操作です。これは、パイプライン フラッシュ、TLB 汚染などを引き起こします。つまり、ヒープにスペースを割り当てるコスト = (関数呼び出し + スペースの計算 + (brk システム コール)?)。確かに、ヒープにスペースを割り当てることは、スタックよりも桁違いに遅いようです。

要素へのアクセス x86 ISA のアドレッシング モードでは、直接アドレッシング モード (temp=mem[addr]) を使用してメモリ オペランドをアドレス指定し、グローバル変数にアクセスできます。一方、スタック上の変数は通常、インデックス付きアドレッシング モードを使用してアクセスします。(temp=mem[スタックポインタ+スタック上のオフセット])。私の推測では、両方のメモリ オペランドにほぼ同じ時間がかかるはずですが、直接アドレッシング モードはインデックス付きアドレッシング モードよりも確実に速いようです。配列のメモリ アクセスに関しては、任意の要素にアクセスするための 2 つのオペランド (配列のベース アドレスとインデックス変数) があります。スタック上の配列にアクセスするときは、もう 1 つのオペランド (スタック ポインター) を追加します。x86 アドレッシング モードには、このようなアドレス ( base+scale*index+offset ) が用意されています。だから、スタック配列要素へのアクセスは大丈夫です:temp=mem[sp+base-address+iterator*element-size] および heap array access : temp=mem[base-address+iterator*element-size]. 明らかに、スタック アクセスは配列アクセスよりもコストが高くなければなりません。

さて、反復の一般的なケースに来て、スタックの反復が遅い場合、それはアドレッシングモードボトルネックである可能性があり(完全にはわかりません)、スペースの割り当てがボトルネックである場合、システムコールがボトルネックである可能性があることを意味します。

于 2012-10-16T04:54:47.137 に答える