IT story

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

hot-time 2020. 5. 14. 08:05
반응형

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


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

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

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


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미미한 빠를 수 있지만, 수도, 그것은 (당신에게 속도 향상의 모든 비용 수) 겸손 코드 팽창을 일으킬 것입니다.


¹ 작은 버퍼 최적화

² 실제 사용 사례.


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

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

  • string_view 기존 버퍼의 슬라이스이므로 메모리 할당이 필요하지 않습니다.
  • string_view is passed by value, not by reference

The advantages of having a slice are multiple:

  • you can use it with char const* or char[] without allocating a new buffer
  • you can take multiple slices and subslices into an existing buffer without allocating
  • substring is O(1), not O(N)
  • ...

Better and more consistent performance all over.


Passing by value also has advantages over passing by reference, because aliasing.

Specifically, when you have a std::string const& parameter, there is no guarantee that the reference string will not be modified. As a result, the compiler must re-fetch the content of the string after each call into an opaque method (pointer to data, length, ...).

On the other hand, when passing a string_view by value, the compiler can statically determine that no other code can modify the length and data pointers now on the stack (or in registers). As a result, it can "cache" them across function calls.


One thing it can do is avoid constructing an std::string object in the case of an implicit conversion from a null terminated string:

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.

std::string_view is basically just a wrapper around a const char*. And passing const char* means that there will be one less pointer in the system in comparison with passing const string* (or const string&), because string* implies something like:

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

Clearly for the purpose of passing const arguments the first pointer is superfluous.

p.s. One substancial difference between std::string_view and const char*, nevertheless, is that the string_views are not required to be null-terminated (they have built-in size), and this allows for random in-place splicing of longer strings.

참고URL : https://stackoverflow.com/questions/40127965/how-exactly-is-stdstring-view-faster-than-const-stdstring

반응형