49

可変長配列を使用するオーバーヘッドはありますか? 実行時にコマンドライン引数を介して配列のサイズを渡すことはできますか? 配列の自動および動的割り当てと比較して、なぜそれが導入されたのですか?

4

4 に答える 4

47

VLA には多少のオーバーヘッドがあります (「通常の」名前付きコンパイル時サイズの配列と比較して)。

まず、実行時の長さがありますが、言語は実行時に配列の実際のサイズを取得する手段を提供します ( を使用sizeof)。これは、配列の実際のサイズをどこかに格納する必要があることを即座に意味します。これにより、アレイごとのわずかなメモリ オーバーヘッドが発生します。ただし、VLA は自動オブジェクトとしてのみ宣言できるため、このメモリ オーバーヘッドは誰も気付かないものです。これは、整数型の追加のローカル変数を宣言するようなものです。

第 2 に、VLA は通常、スタックに割り当てられますが、可変サイズであるため、一般にメモリ内の正確な位置はコンパイル時にわかりません。このため、基本的な実装では通常、メモリ ブロックへのポインタとして実装する必要があります。これにより、追加のメモリ オーバーヘッド (ポインター用) が導入されますが、上記の理由により、これもまったく重要ではありません。これにより、実際の配列を見つけるためにポインター値を読み取る必要があるため、わずかなパフォーマンス オーバーヘッドが発生します。これは、-ed 配列にアクセスするときに発生するオーバーヘッドと同じですmalloc(名前付きのコンパイル時サイズの配列では発生しません)。

VLA のサイズは実行時の整数値であるため、もちろんコマンドライン引数として渡すことができます。VLA は、そのサイズがどこから来るかは気にしません。

VLA は、割り当て/割り当て解除のコストが低いランタイム サイズの配列として導入されました。それらは、「通常の」名前付きのコンパイル時サイズの配列 (割り当てと解放のコストは実質的にゼロですが、サイズは固定) とmalloc-ed 配列 (実行時のサイズですが、割り当てと解放のコストは比較的高い) の間に収まります。

VLA は、[ほぼ] 自動 (つまりローカル) オブジェクトと同じスコープ依存の有効期間規則に従います。つまり、一般に、VLA はmalloc-ed 配列を置き換えることはできません。それらの適用性は、典型的な自動有効期間を備えたランタイムサイズのアレイが必要な場合に限定されます。

于 2010-01-09T22:56:10.460 に答える
29

可変長配列には実行時のオーバーヘッドがありますが、それを測定するにはかなりの労力を費やす必要があります。が可変長配列のsizeof(vla)場合、はコンパイル時定数ではないことに注意してください。vla

配列のサイズは、実行時に関数に渡すことができます。コマンドライン引数からサイズを取得し、それを整数に変換して実行時に関数に渡すことを選択した場合は、それで問題ありません。

変数は自動的に正しいサイズに割り当てられ、関数の終了時に自動的に解放されるため、可変長配列が使用されます。これにより、スペースの過剰な割り当て(ほとんどの場合、最小サイズで作業する場合に可能な最大サイズに十分なスペースを割り当てる)が回避され、メモリのクリーンアップに関する問題が回避されます。

さらに、多次元配列では、AFAIKはFortranのように動作します。配列の先頭の次元を除くすべての次元で固定サイズにとらわれるのではなく、すべての次元を動的に構成できます。


VLAの実行時オーバーヘッドの具体的な証拠-少なくともSPARC(Solaris 10)のGCC4.4.2では。

以下の2つのファイルについて考えてみます。

vla.c-可変長配列を使用

#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);

size_t identity_matrix(int n, int m)
{
    int vla[n][m];
    int i, j;
    assert(n > 0 && n <= 32);
    assert(m > 0 && m <= 32);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            vla[i][j] = 0;
        }
        vla[i][i] = 1;
    }
    return(sizeof(vla));
}

fla.c-固定長配列を使用

#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);

size_t identity_matrix(int n, int m)
{
    int fla[32][32];
    int i, j;
    assert(n > 0 && n <= 32);
    assert(m > 0 && m <= 32);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            fla[i][j] = 0;
        }
        fla[i][i] = 1;
    }
    return(sizeof(fla));
}

コンパイルとオブジェクトファイルのサイズ

比較のために、ローカル配列の名前は異なり(vlavs fla)、宣言されたときの配列の次元は異なります。それ以外の場合、ファイルは同じです。

私は以下を使用してコンパイルしました:

$ gcc -O2 -c -std=c99 fla.c vla.c

オブジェクトファイルのサイズは多少異なります。「ls」と「size」の両方で測定されます。

$ ls -l fla.o vla.o
-rw-r--r--   1 jleffler rd          1036 Jan  9 12:13 fla.o
-rw-r--r--   1 jleffler rd          1176 Jan  9 12:13 vla.o
$ size fla.o vla.o
fla.o: 530 + 0 + 0 = 530
vla.o: 670 + 0 + 0 = 670

オーバーヘッドのどれだけが固定され、どれだけが可変であるかを確認するための広範なテストは行っていませんが、VLAの使用にはオーバーヘッドがあります。

于 2010-01-09T20:02:28.683 に答える
4

可変長配列を使用することでオーバーヘッドが発生するのではないかと思います。

いいえ

配列のサイズは、実行時にコマンドライン引数を介して渡すことができますか?

はい。

アレイを自動的かつ動的に割り当てるのと比較して、なぜそれが導入されたのですか?

自動割り当てでは、コンパイル時に既知の固定サイズのみが許可されます。

()を動的に割り当てるmallocと、配列はヒープに格納されます。ヒープには大きなメモリスペースがありますが、アクセスに時間がかかります。

VLAは、アレイをスタックに配置することで機能します。これにより、割り当てとアクセスが非常に高速になります、スタックは通常(数KB)小さく、VLAがスタックをオーバーフローすると、無限再帰と区別できなくなります。

于 2010-01-09T20:02:28.700 に答える
1

VLAのオーバーヘッドはごくわずかであるはずです(せいぜいスタックポインターに追加されるはずです)。動的割り当てには手動のメモリ管理が必要であり、VLAのスタックベースの割り当てよりも低速です。また、配列の「自動」宣言には、配列サイズのコンパイル時の式が必要です。ただし、スタックオーバーフローが発生すると、未定義の動作が発生するため、VLAを比較的小さくしてください。

コマンドライン引数を使用して配列のサイズを渡すこともできますが、それを処理するコードを自分で作成する必要があります。

于 2010-01-09T20:03:15.997 に答える