std :: vector(ab)は自動ストレージを使用します


46

次のスニペットを考えてみましょう:

#include <array>
int main() {
  using huge_type = std::array<char, 20*1024*1024>;
  huge_type t;
}

通常、デフォルトのスタックサイズは通常20MB未満であるため、ほとんどのプラットフォームでクラッシュします。

次のコードを考えてみましょう:

#include <array>
#include <vector>

int main() {
  using huge_type = std::array<char, 20*1024*1024>;
  std::vector<huge_type> v(1);
}

意外にもクラッシュします!トレースバック(最近のlibstdc ++バージョンの1つを使用)はinclude/bits/stl_uninitialized.hファイルにつながり、ここに次の行が表示されます。

typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
std::fill(__first, __last, _ValueType());

サイズ変更vectorコンストラクターは、要素をデフォルトで初期化する必要があります。これが実装方法です。明らかに、_ValueType()一時的にスタックがクラッシュします。

問題は、それが適合実装であるかどうかです。はいの場合、それは実際には巨大な型のベクトルの使用がかなり制限されていることを意味しますね?


巨大なオブジェクトを配列型に格納するべきではありません。そのためには、存在しない可能性のある連続したメモリの非常に大きな領域が必要になる可能性があります。代わりに、ポインタのベクトル(通常はstd :: unique_ptr)を用意して、メモリにそれほど高い負荷をかけないようにします。
NathanOliver

2
ただの記憶。仮想メモリを使用しない実行中のC ++実装があります。
NathanOliver

3
ところで、コンパイラは?VS 2019(16.4.2)では再現できません
ChrisMM

3
libstdc ++コードを見ると、この実装が使用されるのは、要素タイプが単純でコピー割り当て可能であり、デフォルトstd::allocatorが使用されている場合のみです。
クルミ

1
@Damon上で述べたように、デフォルトのアロケーターを備えた自明な型にのみ使用されるように見えるため、目に見える違いはないはずです。
クルミ

回答:


19

std APIが使用する自動ストレージの量に制限はありません。

それらはすべて12テラバイトのスタックスペースを必要とする可能性があります。

ただし、そのAPIはのみを必要Cpp17DefaultInsertableとし、実装はコンストラクターが必要とするものを超える追加のインスタンスを作成します。オブジェクトの検出の背後にゲートが設定されていない限り、取るに足りない方法でコピーやコピーが可能であれば、その実装は違法に見えます。


8
libstdc ++コードを見ると、この実装が使用されるのは、要素タイプが単純でコピー割り当て可能であり、デフォルトstd::allocatorが使用されている場合のみです。そもそもなぜこの特別なケースが作られたのか、私にはわかりません。
クルミ

3
@walnutこれは、コンパイラーがその一時オブジェクトを実際に作成しなくても自由であることを意味します。私はそれが作成されない最適化されたビルドにまともなチャンスがあると思いますか?
Yakk-Adam Nevraumont

4
はい、可能だと思いますが、大きな要素ではGCCはそうではないようです。libstdc ++を使用したClangは一時を最適化しますが、コンストラクターに渡されたベクトルサイズがコンパイル時の定数である場合にのみ表示されます。godbolt.org/ z / -2ZDMmを参照してください。
クルミ

1
@walnut特別なケースがあるので、std::fill単純な型にディスパッチしmemcpy、バイトを場所にブラストするために使用します。これは、ループ内で個別のオブジェクトを多数構築するよりもはるかに高速である可能性があります。libstdc ++実装は準拠していると思いますが、巨大なオブジェクトのスタックオーバーフローを引き起こすことは、実装品質(QoI)のバグです。私はそれをgcc.gnu.org/PR94540として報告し、修正します。
Jonathan Wakely

@JonathanWakelyはい、それは理にかなっています。私がコメントを書いたとき、なぜそれを考えなかったのか覚えていません。最初にデフォルトで構築された要素は直接インプレースで構築され、次にそこからコピーできるので、要素タイプの追加のオブジェクトが構築されないだろうと私は思ったでしょう。しかし、もちろんこれについては詳しく考えていません。標準ライブラリの実装の詳細についてはわかりません。(私はこれがバグレポートであなたの提案でもあることに気付いたので遅すぎました。)
ウォールナット

9
huge_type t;

明らかに、ほとんどのプラットフォームでクラッシュします...

私は「ほとんど」の仮定に異議を唱えます。巨大なオブジェクトのメモリは使用されないため、コンパイラはそれを完全に無視してメモリを割り当てることはできません。この場合、クラッシュは発生しません。

問題は、それが適合実装であるかどうかです。

C ++標準は、スタックの使用を制限したり、スタックの存在を認めたりすることもありません。だから、はい、それは標準に準拠しています。しかし、これは実装の品質の問題であると考えることができます。

それは実際には、巨大な型のベクトルの使用がかなり制限されていることを意味しますね。

libstdc ++の場合はそうです。クラッシュはlibc ++では(clangを使用して)再現されなかったため、これは言語の制限ではなく、特定の実装に限定されているようです。


6
「割り当てられたメモリがプログラムによってアクセスされることはないため、スタックがオーバーフローしてもクラッシュするとは限らない」 —この後にスタックが使用されると(関数の呼び出しなど)、オーバーコミットプラットフォームでもクラッシュします。 。
ルスラン

これがクラッシュしないすべてのプラットフォーム(オブジェクトが正常に割り当てられないと想定)は、スタッククラッシュに対して脆弱です。
user253751

@ user253751ほとんどのプラットフォーム/プログラムは脆弱ではないと仮定することは楽観的です。
eerorika

オーバーコミットは、スタックではなくヒープにのみ適用されると思います。スタックのサイズには上限が固定されています。
Jonathan Wakely

@JonathanWakelyそうですね。クラッシュしない理由は、コンパイラが未使用のオブジェクトを割り当てないためです。
eerorika

5

私は言語弁護士でもC ++標準の専門家でもありませんが、cppreference.comは次のように述べています。

explicit vector( size_type count, const Allocator& alloc = Allocator() );

Tのcount-default-insertedインスタンスでコンテナを構築します。コピーは作成されません。

おそらく「デフォルト挿入」を誤解しているかもしれませんが、私は期待します:

std::vector<huge_type> v(1);

に等しい

std::vector<huge_type> v;
v.emplace_back();

後者のバージョンでは、スタックコピーを作成するのではなく、ベクトルの動的メモリに直接huge_typeを作成します。

あなたが見ているものは非準拠であると私は正式には言えませんが、確かに私が質の高い実装から期待するものではありません。


4
質問のコメントで述べたように、libstdc ++はコピー割り当てとを使用した自明な型に対してのみこの実装を使用するためstd::allocator、ベクトルメモリに直接挿入することと中間コピーを作成することの間に目に見える違いはないはずです。
クルミ

@walnut:そうです、しかし、巨大なスタック割り当てとinitとcopyのパフォーマンスへの影響は、私が高品質の実装から期待できないものです。
エイドリアンマッカーシー

2
はい私は同意する。これは実装の見落としだったと思います。私の要点は、標準への準拠という点では問題ではないということだけでした。
クルミ

IIRCにはemplace_back、ベクトルの作成だけでなく、コピー機能または移動機能も必要です。これはあなたが持つことができることを意味vector<mutex> v(1)ではなく、vector<mutex> v; v.emplace_back();何かのためにhuge_typeあなたはまだ配分を持っている可能性があり、第二のバージョンでより多くの操作を移動します。どちらも一時オブジェクトを作成するべきではありません。
dyp

1
@IgorR。vector::vector(size_type, Allocator const&)(Cpp17)DefaultInsertableが必要
dyp
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.