0

何百万ものベクトルを使用するアプリケーションがあります。

std::vector のほとんどの実装は 4 つのポインター (_First、_Last、_End、および _Alloc) を使用しているようで、64 ビット マシンでは 32 バイトを消費します。ベクトルのほとんどの「実用的な」使用例では、現在のサイズと割り当てられたサイズをそれぞれ格納するために、1 つのポインターと 2 つの「unsigned int」フィールドを使用して回避できます。カスタマイズされた割り当てをサポートするという潜在的な課題を無視すると (割り当てがグローバルな new & delete 演算子を経由しなければならないと仮定する代わりに)、16 バイト (または最悪でも 24 バイトを使用して_Alloc ポインターをサポートします)。

これをコーディングする前に、1) 注意すべき落とし穴はありますか? 2) オープン ソースの実装は存在しますか?

4

3 に答える 3

5

このようなことを達成することはできますが、それほど多くを得る可能性は低いでしょう。

まず、パフォーマンスの側面があります。あなたはメモリ消費のために時間を交換しています。保存したメモリは、呼び出しごとに加算と乗算を行う必要があるため、相殺されます(乗算を最適化できるendベクトルであれば大丈夫です)。ほとんどの手書きのベクトル ループ コードは、すべてのループ反復でsizeof(vector<t>::value_type) == 1呼び出されることに注意してください。end最新の CPU では、プロセッサがより多くのものをキャッシュに保持できるため、実際には大きなメリットとなります。内部ループ内のこれらの余分な命令のカップルが、プロセッサに命令キャッシュ内のものを頻繁にスワップさせない限り)

さらに、次の理由により、ベクター内の全体的なメモリ使用量に関して、メモリの節約は少ない可能性があります。

  • メモリ マネージャーのオーバーヘッド。ほとんどのメモリ マネージャーの実装では、メモリ マネージャーからの各割り当て (もちろん、このベクトルが必要です) によって、16 ~ 24 バイトのオーバーヘッドが追加されます。dlmalloc( (UNIX / Linux / etc。)またはRtlHeap(Windows)のようなものを想定)
  • 負荷のオーバープロビジョニング。最後に償却された定数の挿入と削除を実現するために、ベクトルのサイズが変更されると、ベクトル内のデータのサイズの倍数にサイズ変更されます。これは、ベクトルが割り当てる一般的なメモリ容量が、ベクトルに実際に格納されている要素数の 1.6 (MSVC++) または 2 (STLPort、libstdc++) 倍で十分であることを意味します。
  • アライメントの制限。これらの多くのベクトルを配列 (または別のベクトル) に入れる場合、そのベクトルの最初のメンバーは、割り当てられたメモリ ブロックへのポインターのままであることに注意してください。このポインターは通常、とにかく 8 バイトでアラインする必要があります。そのため、保存した 4 バイトは、配列内の構造パディングで失われます。

今のところ、vector の単純な実装を使用します。メモリ プロファイラーを使用してコードを実行し、これらの 2 つのポインターを取り除くことで大幅な節約ができることがわかった場合は、組み込みのクラスに依存するのではなく、パフォーマンス特性を満たす独自の最適化されたクラスを実装することをおそらくやめています。ベクトル実装。(このような最適化されたクラスの例はstd::string、小さな文字列の最適化を実装するプラットフォームにあります)

(注:ポインタを最適化することを私が知っている唯一のコンパイラは、AllocまだリリースされていないVC11です。Nimは、libstdc ++の現在のプレリリースバージョンも同様にそれを行うと言っています...)

于 2012-07-02T14:31:19.273 に答える
1

これらのベクトルの内容が非常に小さい場合を除き、内容を保持するための 16 バイトと 32 バイトの差は、それらが消費する総メモリのわずかな割合になります。この車輪を再発明するには多大な労力が必要になるため、そのすべての作業に対して十分な見返りを得られるようにしてください。

ところで、教育にも価値があり、これを行うことで多くのことを学ぶことができます。続行することを選択した場合は、最初にテスト スイートを作成し、現在の実装でそれを実行してから、発明したもので実行することを検討してください。

于 2012-07-02T14:36:09.720 に答える