2009-05-08 5 views
3

次のコードを検討してください。大きなバッファーと大きいスタティックバッファーの利点はありますか?

1000回の連続実行でDoSomething1()が高速化され、DoSomething2()が高速化されますか? DoSomething1()を1000回呼び出すとDoSomething2()を1000回呼び出したほうが速いと思います。

すべての大きなバッファを静的にすることに不利な点はありますか?

#define MAX_BUFFER_LENGTH 1024*5 
void DoSomething1() 
{ 
    static char buf[MAX_BUFFER_LENGTH] ; 
    memset(buf, 0, MAX_BUFFER_LENGTH); 
} 

void DoSomething2() 
{ 
    char buf[MAX_BUFFER_LENGTH] ; 
    memset(buf, 0, MAX_BUFFER_LENGTH); 
} 

ありがとうございます。静的バッファの

答えて

8

欠点:

  • あなたは、スレッドセーフである必要がある場合は、おそらく静的なバッファを使用することは良いアイデアではありません。
  • メモリはプログラムが終了するまで解放されないため、メモリ消費量が増加します。静的バッファの

利点:

  • 静的バッファを持つ以下の割り当てがあります。そのたびにスタックに割り当てる必要はありません。
  • スタティックバッファを使用すると、割り当てが高すぎることによるスタックオーバーフローの可能性が低くなります。
+1

スタックの割り当てが高速ですので、私はそれについて心配しません。スタックオーバーフローは私が真剣に心配しているものです。 –

4

この2つの間には速度の差はほとんどありません。スタックにバッファを割り当てることは非常に高速です - それはスタックポインタをある値だけデクリメントすることです。ただし、非常に大きなバッファをスタックに割り当てると、スタックがオーバーフローし、セグメンテーション/アクセス違反が発生する可能性があります。逆に、静的バッファがたくさんある場合は、プログラムのワーキングセットサイズを大幅に増やしますが、参照の局所性があれば多少なりとも緩和されます。

もう1つの主な違いは、スタックバッファはスレッドセーフであり、再入可能であることです。静的バッファはスレッドセーフでもリエントラントでもありません。

6

バッファオーバーランのセキュリティチェックを有効にするVC++コンパイラで/ GSを有効にすると、スタック割り当てが少し高額になります(/ GSはデフォルトでオンになっています)。実際には、2つのオプションをプロファイルして、どちらが速いかを確認する必要があります。静的メモリとスタックのキャッシュローカリティのようなものが違いを生む可能性があります。

ここには非静的バージョンがあり、VC++コンパイラには/ O2が含まれています。

_main PROC      ; COMDAT 
; Line 5 
    mov eax, 5124    ; 00001404H 
    call __chkstk 
    mov eax, DWORD PTR ___security_cookie 
    xor eax, esp 
    mov DWORD PTR __$ArrayPad$[esp+5124], eax 
; Line 7 
    push 5120     ; 00001400H 
    lea eax, DWORD PTR _buf$[esp+5128] 
    push 0 
    push eax 
    call _memset 
; Line 9 
    mov ecx, DWORD PTR __$ArrayPad$[esp+5136] 
    movsx eax, BYTE PTR _buf$[esp+5139] 
    add esp, 12     ; 0000000cH 
    xor ecx, esp 
    call @[email protected] 
    add esp, 5124    ; 00001404H 
    ret 0 
_main ENDP 
_TEXT ENDS 

そしてここでは、静的なバージョン

; COMDAT _main 
_TEXT SEGMENT 
_main PROC      ; COMDAT 
; Line 7 
    push 5120     ; 00001400H 
    push 0 
    push OFFSET [email protected][email protected]@[email protected] 
    call _memset 
; Line 8 
    movsx eax, BYTE PTR [email protected][email protected]@[email protected]+3 
    add esp, 12     ; 0000000cH 
; Line 9 
    ret 0 
_main ENDP 
_TEXT ENDS 
END 
+1

+1とキャッシュのローカリティとプロファイリング。 –

2

あなたは、クラスにコードを入れて検討することもできます。例えば。何かのように

const MAX_BUFFER_LENGTH = 1024*5; 
class DoSomethingEngine { 
    private: 
    char *buffer; 
    public: 
    DoSomethingEngine() { 
     buffer = new buffer[MAX_BUFFER_LENGTH]; 
    } 
    virtual ~DoSomethingEngine() { 
     free(buffer); 
    } 
    void DoItNow() { 
     memset(buffer, 0, MAX_BUFFER_LENGTH); 
     ... 
    } 
} 

これは、各トレッドが独自のエンジンを割り当てている場合にのみ安全です。スタック上に大量のメモリを割り当てるのを防ぎます。ヒープ上の割り当ては小さなオーバーヘッドですが、クラスのインスタンスを何度も再利用すれば、これはごくわずかです。

2

私はここで唯一マルチスレッドソフトウェアで作業していますか?静的バッファは、その状況では絶対にno-noです。ただし、ロックやロック解除のパフォーマンスを犠牲にしたくない場合は別です。

0

スタックの割り当てが非常に高速であると言われているように、ArrayListやHashTable(今List <>およびgeneric <>などのより複雑なオブジェクトの場合、コンテナが容量に達するたびに不要な再割り当てが行われ、新しいメモリを割り当てて古いメモリから新しいメモリに内容をコピーする必要がある場合にも、その都度実行するコンストラクションコードが存在します。これは私が頻繁に使用するList <オブジェクトですが、必要なサイズに拡大してClear()を呼び出して再利用します。割り当てられたメモリ/容量はそのまま残ります。しかし、頻繁に起こらないか、または一度だけ発生することのないたくさんのメモリを割り当てるようなルージュコールがある場合は、メモリリークに注意する必要があります。

関連する問題