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 ++ 버전 중 하나 포함)은 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
어떤 컴파일러, btw? 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 (Quality of Implementation) 버그입니다. 나는 그것을 gcc.gnu.org/PR94540으로 보고하고 고칠 것이다.
Jonathan Wakely

@JonathanWakely 그렇습니다. 내 의견을 쓸 때 왜 그렇게 생각하지 않았는지 기억이 나지 않습니다. 나는 첫 번째 기본 구성 요소가 제자리에 직접 구성되고 그로부터 복사 할 수 있다고 생각했을 것이므로 요소 유형의 추가 객체는 구성되지 않습니다. 그러나 물론 이것을 실제로 자세히 생각하지 않았으며 표준 라이브러리 구현의 시작과 끝을 알지 못합니다. (버그 보고서에서 귀하의 제안이기도합니다.)
월넛

9
huge_type t;

분명히 그것은 대부분의 플랫폼에서 충돌 할 것입니다 ...

나는 "가장"이라는 가정에 이의를 제기한다. 거대한 객체의 메모리는 절대 사용되지 않기 때문에 컴파일러는이를 완전히 무시하고 메모리를 할당 할 수 없으며이 경우 충돌이 발생하지 않습니다.

문제는 그것이 적합한 구현인지 여부입니다.

C ++ 표준은 스택 사용을 제한하거나 스택의 존재를 인정하지 않습니다. 예, 표준을 준수합니다. 그러나 이것을 구현 품질 문제라고 생각할 수 있습니다.

그것은 실제로 거대한 유형의 벡터의 사용이 상당히 제한적이라는 것을 의미합니다.

libstdc ++의 경우처럼 보입니다. 충돌은 libc ++ (clang 사용)로 재현되지 않았으므로 언어의 제한이 아니라 특정 구현에서만 발생하는 것으로 보입니다.


6
"할당 된 메모리가 프로그램에 의해 액세스되지 않기 때문에 스택의 오버 플로우에도 불구하고 반드시 충돌하지는 않습니다." -스택이 이후에 어떤 식 으로든 사용되면 (예 : 함수 호출) 오버 커밋 플랫폼에서도 충돌이 발생합니다. .
Ruslan

객체가 성공적으로 할당되지 않았다고 가정하면 충돌이 발생하지 않는 플랫폼은 스택 충돌에 취약합니다.
user253751

@ user253751 대부분의 플랫폼 / 프로그램이 취약하지 않다고 가정하는 것이 낙관적입니다.
eerorika

오버 커밋은 스택이 아닌 힙에만 적용된다고 생각합니다. 스택의 크기에는 고정 된 상한이 있습니다.
Jonathan Wakely

@JonathanWakely 당신이 맞아요. 충돌하지 않는 이유는 컴파일러가 사용되지 않은 객체를 할당하지 않기 때문입니다.
eerorika

5

저는 언어 변호사 나 C ++ 표준 전문가는 아니지만 cppreference.com은 말합니다.

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

기본적으로 삽입 된 개수가 T 인 컨테이너를 구성합니다. 사본이 없습니다.

아마도 "default-inserted"를 오해하고 있지만 다음과 같이 기대합니다.

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

동등하다

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

후자의 버전은 스택 복사본을 생성하지 말고 벡터의 동적 메모리에 직접 huge_type을 구성해야합니다.

나는 당신이보고있는 것이 준수하지 않는다고 권위적으로 말할 수는 없지만 품질 구현에서 기대하는 것은 아닙니다.


4
질문에 대한 주석에서 언급했듯이 libstdc ++는 copy 할당이있는 사소한 유형에 대해서만이 구현을 사용 std::allocator하므로 벡터 메모리에 직접 삽입하는 것과 중간 복사본을 만드는 것 사이에는 눈에 띄는 차이가 없어야합니다.
호두

@walnut : 맞습니다. 그러나 대량 스택 할당과 init 및 copy의 성능 영향은 여전히 ​​고품질 구현에서 기대하지 않는 것입니다.
Adrian McCarthy

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&)DefaultInsertable
dyp
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.