Programming/Modern c++

[Benchmark] Passing std::string as function parameter

BiniU 2022. 6. 12. 18:03

 

어떤 class의 멤버변수로 string을 넘겨주어야 할 경우, 크게 두 가지 옵션이 있다.

 

1. const reference로 받은 뒤 멤버변수에 복사한다.

2. value로 받은 뒤 std::move를 사용해서 멤버변수를 초기화 시킨다.

 

이론상 2번 방법이 좀 더 효과적이라고 생각했는데, 

실제  benchmark 결과 1번이 더 빠른 것을 확인했다.

 

Function parameter로 받아은 값을 class member variable로 설정해야 하는 경우

 

이 경우에는 외부의 값을 받아서 class member variable로 설정해야 한다는 점에서 특수성이 생긴다.

(주로 constructor에서 이런 작업들이 일어난다)

(class member variable이 어떤 데이터의 reference variable이 아닌 경우)

 

이 경우에는 const reference 보다 std::move를 사용하는 편이 더 합리적일 수 있다.

▶ const reference로 받아서 멤버에 복사하게 될 경우
SomeConstructor(const T& param) : param_(param) {}
  1. lvalue를 받았을 경우: lvalue가 argument(reference)로 바인드 되고, 이 값이 멤버변수로 copy 된다.
  2. rvalue를 받았을 경우: rvalue가 argument로 바인드 되고, 이 값이 멤버변수로 copy 된다.

 

▶ std::move를 사용할 경우
SomeConstructor(T param) : param_(std::move(param)) {}
  1. lvalue를 받았을 경우: lvalue가 argument로 copy 되고, 이 값이 멤버변수로 move 된다.
  2. rvalue를 받았을 경우: rvalue가 argument로 move 되고, 이 값이 멤버변수로 다시 move 된다.

 

결국 const reference의 (1), (2)번 경우와 std::move의 (1)번 경우는 모두 복사가 1회 일어나는데,
std::move의 (2)번의 경우 copy가 일어나지 않는다는 장점이 있다.
 
String과 같이 rvalue를 직접 넣어주는 경우가 많을 때는 위와 같은 방식이 더 효율적이라고 할 수 있다.
(Clang tidy에서도 위와 같은 방법을 권장한다.)

 

 

실제 Benchmark 결과

#include <string>
#include <utility>


class A
{
  public:
    A(const std::string& str) : str_(str) {};
  private:
    std::string str_;  
};

class B
{
  public:
    B(std::string str) : str_(std::move(str)) {};
  private:
    std::string str_;  
};


static void ReferenceLValue(benchmark::State& state) 
{
  for (auto _ : state) 
  {
    std::string str("LValue");
    A a(str);
    benchmark::DoNotOptimize(str);
    benchmark::DoNotOptimize(a);
  }
}
BENCHMARK(ReferenceLValue);

static void ReferenceRValue(benchmark::State& state)
{
  for (auto _ : state) 
  {
    A a("RValue");
    benchmark::DoNotOptimize(a);
  }
}
BENCHMARK(ReferenceRValue);

static void MoveLValue(benchmark::State& state) 
{
  for (auto _ : state) 
  {
    std::string str("LValue");
    B b(str);
    benchmark::DoNotOptimize(str);
    benchmark::DoNotOptimize(b);
  }
}
BENCHMARK(MoveLValue);

static void MoveRValue(benchmark::State& state)
{
  for (auto _ : state) 
  {
    B b("RValue");
    benchmark::DoNotOptimize(b);
  }
}
BENCHMARK(MoveRValue);

LLVM
GNU

반응형

'Programming > Modern c++' 카테고리의 다른 글

string literal은 어디에 저장될까?  (0) 2022.06.13
std::string_view  (0) 2022.06.13
C++ 표준 난수 라이브러리 <random>  (0) 2022.04.07