const std :: string &보다 std :: string_view가 얼마나 빠릅니까?


221

std::string_viewC ++ 17로 만들었으며 대신 대신 사용하는 것이 좋습니다 const std::string&.

이유 중 하나는 성능입니다.

누군가 매개 변수 유형으로 사용할 때보 다 정확히 std::string_view / 빠른 속도를 설명 할 수 있습니까 const std::string&? (수취인의 사본이 작성되지 않았다고 가정)


7
std::string_view(char * begin, char * end) 쌍의 추상화입니다. std::string불필요한 사본을 만들 때 사용합니다 .
QuestionC

제 생각에는 문제는 정확히 어느 것이 더 빠르지는 않지만 언제 사용해야하는지입니다. 문자열에 대한 조작이 필요하고 영구적이지 않거나 원래 값을 유지하면 string_view는 문자열 복사본을 만들 필요가 없으므로 완벽합니다. 그러나 예를 들어 string :: find를 사용하여 문자열에서 무언가를 확인 해야하는 경우 참조가 더 좋습니다.
TheArquitect

원하지 않는 경우에 당신이 그것을 사용 @QuestionC 당신의 API는로 제한하는 std::string(원시 배열, 벡터, 받아 들일 수 string_view std::basic_string<>비 기본 할당 자 등 등 등 아, 그리고 다른 string_views 분명히 포함)
sehe

답변:


213

std::string_view 경우에 따라 더 빠릅니다.

먼저 std::string const&데이터가 std::string원시 C 배열이 아닌, char const*C API에 의해 반환, std::vector<char>일부 역 직렬화 엔진에 의해 생성됨 등이 아니어야합니다. 피하지 않는 형식 변환은 바이트 복사를 피하고 (문자열이 특정 std::string구현에 대한 SBO¹ )는 메모리 할당을 피합니다.

void foo( std::string_view bob ) {
  std::cout << bob << "\n";
}
int main(int argc, char const*const* argv) {
  foo( "This is a string long enough to avoid the std::string SBO" );
  if (argc > 1)
    foo( argv[1] );
}

어떤 할당이 이루어없는 string_view경우지만, 경우가있을 것입니다 foo했다 std::string const&대신의string_view .

두 번째로 큰 이유는 복사본없이 부분 문자열로 작업 할 수 있기 때문입니다. 2 기가 바이트 JSON 문자열 (!) ²을 구문 분석한다고 가정하십시오. 로 구문 분석하면 std::string노드의 이름 또는 값을 저장하는 각 구문 분석 노드가 복사됩니다. 는 원래 데이터를 2GB 문자열에서 로컬 노드로 합니다.

대신 std::string_views로 구문 분석 하면 노드 는 원래 데이터를 합니다. 이를 통해 구문 분석 중에 수백만 개의 할당을 줄이고 메모리 요구 사항을 절반으로 줄일 수 있습니다.

당신이 얻을 수있는 속도 향상은 단순히 말도 안됩니다.

이것은 극단적 인 경우이지만 다른 "하위 문자열을 가져와 함께 사용"하는 경우에도 string_view .

결정에있어 중요한 부분은 std::string_view 입니다. 많지는 않지만 무언가입니다.

암시적인 null 종료를 잃어버린 것입니다. 따라서 동일한 문자열이 3 개의 함수에 전달되고 모두 null 종료자가 필요한 경우 std::string한 번으로 변환하는 것이 현명 할 수 있습니다. 따라서 코드에 null 종결자가 필요한 것으로 알려져 있고 C 스타일 소스 버퍼 등에서 공급 된 문자열을 기대하지 않는 경우을 사용할 수 있습니다 std::string const&. 그렇지 않으면std::string_view .

만약 std::string_view널 (null)로 종료되었다고 명시한 플래그가 있다면 (혹은 더 환상적인)std::string const& .

std::string사용하지 않는 const&것이 a보다 최적 인 경우가 std::string_view있습니다. 호출 후 문자열 사본을 무기한으로 소유해야하는 경우 값을 사용하는 것이 효율적입니다. SBO 케이스에 있거나 (할당되지 않고 문자 사본을 복제하기 위해 단지 몇 개의 문자 사본 만) 힙 할당 버퍼를 local로 이동할 수 있습니다 std::string. 과부하 가 두 번 std::string&&이고 std::string_view빠를 수 있지만 약간만있을 수 있으며, 약간의 코드 팽창이 발생할 수 있습니다 (이로 인해 모든 속도 향상 비용이 발생할 수 있음).


¹ 작은 버퍼 최적화

² 실제 사용 사례.


8
또한 소유권을 잃습니다. 어떤 문자열을 반환하고 경우에만 관심의 만큼 오래 살아 남기 위해 보장되는 버퍼의 하위 문자열 외에 아무것도해야합니다. 실제로, 소유권 상실은 매우 양날의 무기입니다.
중복 제거기

SBO가 이상하게 들린다. 난 항상 SSO (작은 문자열 최적화) 들었어요
phuclv

@ 푸 물론; 그러나 트릭을 사용하는 유일한 것은 문자열이 아닙니다.
Yakk-Adam Nevraumont

@phuclv SSO는 작은 버퍼 최적화 를 나타내는 SBO의 특정 사례입니다 . 다른 용어는 작은 데이터 선택입니다. , 작은 물체 옵트. 또는 작은 크기를 선택하십시오. .
Daniel Langr

59

string_view가 성능을 향상시키는 한 가지 방법은 접두사와 접미사를 쉽게 제거 할 수 있다는 것입니다. 후드 아래에서 string_view는 문자열 버퍼에 대한 포인터에 접두사 크기를 추가하거나 바이트 카운터에서 접미사 크기를 뺄 수 있습니다. 일반적으로 빠릅니다. 반면 std :: string은 substr과 같은 작업을 수행 할 때 바이트를 복사해야합니다 (이 방법으로 버퍼를 소유하는 새 문자열을 얻을 수 있지만 많은 경우 복사하지 않고 원래 문자열의 일부를 가져 오려고합니다). 예:

std::string str{"foobar"};
auto bar = str.substr(3);
assert(bar == "bar");

std :: string_view로 :

std::string str{"foobar"};
std::string_view bar{str.c_str(), str.size()};
bar.remove_prefix(3);
assert(bar == "bar");

최신 정보:

실제 숫자를 추가하기 위해 매우 간단한 벤치 마크를 작성했습니다. 멋진 Google 벤치 마크 라이브러리를 사용했습니다 . 벤치 마크 된 기능은 다음과 같습니다.

string remove_prefix(const string &str) {
  return str.substr(3);
}
string_view remove_prefix(string_view str) {
  str.remove_prefix(3);
  return str;
}
static void BM_remove_prefix_string(benchmark::State& state) {                
  std::string example{"asfaghdfgsghasfasg3423rfgasdg"};
  while (state.KeepRunning()) {
    auto res = remove_prefix(example);
    // auto res = remove_prefix(string_view(example)); for string_view
    if (res != "aghdfgsghasfasg3423rfgasdg") {
      throw std::runtime_error("bad op");
    }
  }
}
// BM_remove_prefix_string_view is similar, I skipped it to keep the post short

결과

(x86_64 Linux, gcc 6.2, " -O3 -DNDEBUG") :

Benchmark                             Time           CPU Iterations
-------------------------------------------------------------------
BM_remove_prefix_string              90 ns         90 ns    7740626
BM_remove_prefix_string_view          6 ns          6 ns  120468514

2
실제 벤치 마크를 제공 한 것이 좋습니다. 이것은 실제로 관련 사용 사례에서 얻을 수있는 것을 보여줍니다.
Daniel Kamil Kozar

1
@DanielKamilKozar 피드백에 감사드립니다. 또한 벤치 마크가 가치 있다고 생각하며 때로는 모든 것이 바뀝니다.
Pavel Davydov

47

두 가지 주요 이유가 있습니다.

  • string_view 기존 버퍼의 슬라이스이므로 메모리 할당이 필요하지 않습니다.
  • string_view 참조가 아닌 값으로 전달됩니다.

슬라이스를 사용하면 다음과 같은 장점이 있습니다.

  • 새 버퍼를 할당 char const*하거나 char[]할당하지 않고 사용할 수 있습니다
  • 할당하지 않고 여러 슬라이스와 하위 슬라이스를 기존 버퍼에 넣을 수 있습니다
  • 부분 문자열은 O (N)이 아닌 O (1)입니다.
  • ...

더 우수하고 일관된 성능.


앨리어싱 (aliasing) 때문에 값으로 전달하면 참조로 전달하는 것보다 장점이 있습니다.

특히, 당신이 std::string const& 매개 변수 경우 참조 문자열이 수정되지 않을 것이라는 보장은 없습니다. 결과적으로 컴파일러는 각 호출 후 opaque 메소드 (데이터, 길이, ...에 대한 포인터)를 호출 한 후 문자열의 컨텐츠를 다시 가져와야합니다.

반면, string_viewby 값을 전달할 때 컴파일러는 다른 코드가 스택 (또는 레지스터)에서 길이와 데이터 포인터를 수정할 수 없음을 정적으로 확인할 수 있습니다. 결과적으로 함수 호출을 통해 "캐시"할 수 있습니다.


36

std::stringnull로 끝나는 문자열에서 암시 적으로 변환하는 경우 객체를 생성하지 않는 것이 가능 합니다.

void foo(const std::string& s);

...

foo("hello, world!"); // std::string object created, possible dynamic allocation.
char msg[] = "good morning!";
foo(msg); // std::string object created, possible dynamic allocation.

12
그것은 말하는 가치가있을 수도 있습니다 const std::string str{"goodbye!"}; foo(str);아마 되지 않습니다 문자열 및보다 빨리 string_view와 수
마틴 보너 모니카 지원

1
습관 string_view이 하나의 포인터와 달리 두 개의 포인터를 복사 할 수 있습니다으로 느린 const string&?
balki

9

std::string_view기본적으로 단지 래퍼 const char*입니다. 그리고 전달 const char*은 다음과 같은 것을 의미 하기 때문에 전달 const string*(또는 const string&) 과 비교할 때 시스템에 하나의 포인터가 적다는 string*것을 의미합니다.

string* -> char* -> char[]
           |   string    |

const 인수를 전달할 목적으로 첫 번째 포인터는 불필요합니다.

PS 하나 사이 substancial 차이 std::string_viewconst char*, 그럼에도 불구하고 string_views이 (가 내장되어 크기) 널 (null) 종료 할 필요가 없습니다 것입니다, 이것은 긴 문자열의 임의의 위치에서 접합이 가능합니다.


4
downvotes와 함께 무엇입니까? std::string_view의 단지 멋진 const char*, 기간입니다. GCC는이를 다음과 같이 구현합니다.class basic_string_view {const _CharT* _M_str; size_t _M_len;}
n.caillou

4
65K 담당자에게 연락하십시오 (현재 65 세부터). 이것은 대답이 될 것입니다 (화물 숭배 군중에게 파동) :)
mlvljr

7
@mlvljr 아무도 통과하지 못합니다 std::string const*. 그리고 그 도표는 이해할 수 없습니다. @ n.caillou : 자신의 의견은 이미 답변보다 정확합니다. 그것은 string_view"멋진 것"이상을 만듭니다 char const*. 정말 분명합니다.
sehe

@sehe 나는 아무도, 문제가 될 수 없다 (즉, const 문자열에 포인터 (또는 참조)를 전달하는 이유는 무엇입니까?) :)
mlvljr

2
@sehe 당신은 최적화 또는 실행 관점에서 것을 이해한다, std::string const*그리고 std::string const&당신은 동일하지 않습니다입니까?
n.caillou
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.