Programing

문자열에서 가장 최적화 된 연결 방법

lottogame 2020. 11. 24. 07:34
반응형

문자열에서 가장 최적화 된 연결 방법


우리는 코드에서 지루하고 매우 많은 문자열 연산을해야하는 많은 상황을 매일 마주했습니다. 우리 모두는 문자열 조작이 비용이 많이 드는 작업이라는 것을 알고 있습니다. 사용 가능한 버전 중 어느 것이 가장 저렴한 지 알고 싶습니다.

가장 일반적인 작업은 연결입니다 (이것은 우리가 어느 정도 제어 할 수있는 것입니다). C ++에서 std :: strings를 연결하는 가장 좋은 방법과 연결 속도를 높이기위한 다양한 해결 방법은 무엇입니까?

내말은,

std::string l_czTempStr;

1).l_czTempStr = "Test data1" + "Test data2" + "Test data3";

2). l_czTempStr =  "Test data1"; 
    l_czTempStr += "Test data2";
    l_czTempStr += "Test data3";

3). using << operator

4). using append()

또한 std :: string보다 CString을 사용하는 이점이 있습니까?


다음은 작은 테스트 모음입니다.

#include <iostream>
#include <string>
#include <chrono>
#include <sstream>

int main ()
{
    typedef std::chrono::high_resolution_clock clock;
    typedef std::chrono::duration<float, std::milli> mil;
    std::string l_czTempStr;
    std::string s1="Test data1";
    auto t0 = clock::now();
    #if VER==1
    for (int i = 0; i < 100000; ++i)
    {
        l_czTempStr = s1 + "Test data2" + "Test data3";
    }
    #elif VER==2
    for (int i = 0; i < 100000; ++i)
    {
        l_czTempStr =  "Test data1"; 
        l_czTempStr += "Test data2";
        l_czTempStr += "Test data3";
    }
    #elif VER==3
    for (int i = 0; i < 100000; ++i)
    {
        l_czTempStr =  "Test data1"; 
        l_czTempStr.append("Test data2");
        l_czTempStr.append("Test data3");
    }
    #elif VER==4
    for (int i = 0; i < 100000; ++i)
    {
        std::ostringstream oss;
        oss << "Test data1";
        oss << "Test data2";
        oss << "Test data3";
        l_czTempStr = oss.str();
    }
    #endif
    auto t1 = clock::now();
    std::cout << l_czTempStr << '\n';
    std::cout << mil(t1-t0).count() << "ms\n";
}

coliru :

다음으로 컴파일하십시오.

clang ++ -std = c ++ 11 -O3 -DVER = 1 -Wall -pedantic -pthread main.cpp

21.6463ms

-DVER = 2

6.61773ms

-DVER = 3

6.7855ms

-DVER = 4

102.015ms

것 같습니다 2), +=승자입니다.

(또한 유무에 관계없이 컴파일 -pthread하면 타이밍에 영향을 미치는 것 같습니다)


다른 답변 외에도 ...

나는 얼마 전에이 문제에 대해 광범위한 벤치 마크를 만들었고 모든 사용 사례 에서 가장 효율적인 솔루션 (Linux x86 / x64 / ARM의 GCC 4.7 및 4.8)은 모두reserve() 를 담을 수있는 충분한 공간이있는 결과 문자열의 첫 번째 라는 결론에 도달했습니다. 연결된 문자열, 그리고 append()그들 (또는 operator +=()차이가없는을 사용하십시오 ).

불행히도 내가 그 벤치 마크를 삭제 한 것 같아서 당신은 내 말만 가지고 있습니다 (하지만 내 말이 충분하지 않다면 Mats Petersson의 벤치 마크를 직접 확인하여 쉽게 조정할 수 있습니다).

간단히 말해서 :

const string space = " ";
string result;
result.reserve(5 + space.size() + 5);
result += "hello";
result += space;
result += "world";

정확한 사용 사례 (연결된 문자열의 수, 유형 및 크기)에 따라 때로는이 방법이 가장 효율적이고 다른 경우에는 다른 방법과 동등하지만 결코 나쁘지 않습니다.


문제는 특히 문자열 리터럴을 혼합 할 때 필요한 총 크기를 미리 계산하는 것이 정말 고통 스럽다는 것입니다 std::string(위의 예는 그 문제에 대해 충분히 명확합니다). 이러한 코드의 유지 관리 성은 리터럴 중 하나를 수정하거나 연결할 다른 문자열을 추가하자마자 절대적으로 끔찍합니다.

한 가지 접근 방식은 sizeof리터럴의 크기를 계산하는 데 사용 하는 것이지만 IMHO는 해결하는 것보다 많은 엉망을 만들고 유지 관리 가능성은 여전히 ​​끔찍합니다.

#define STR_HELLO "hello"
#define STR_WORLD "world"

const string space = " ";
string result;
result.reserve(sizeof(STR_HELLO)-1 + space.size() + sizeof(STR_WORLD)-1);
result += STR_HELLO;
result += space;
result += STR_WORLD;

사용 가능한 솔루션 (C ++ 11, 가변 템플릿)

필자는 필요에 따라 문자열 크기 (예 : 문자열 리터럴의 크기가 컴파일 시간에 결정됨)를 효율적으로 계산하는 일련의 가변 템플릿을 reserve()선택한 다음 모든 것을 연결했습니다.

이것이 유용하기를 바랍니다.

namespace detail {

  template<typename>
  struct string_size_impl;

  template<size_t N>
  struct string_size_impl<const char[N]> {
    static constexpr size_t size(const char (&) [N]) { return N - 1; }
  };

  template<size_t N>
  struct string_size_impl<char[N]> {
    static size_t size(char (&s) [N]) { return N ? strlen(s) : 0; }
  };

  template<>
  struct string_size_impl<const char*> {
    static size_t size(const char* s) { return s ? strlen(s) : 0; }
  };

  template<>
  struct string_size_impl<char*> {
    static size_t size(char* s) { return s ? strlen(s) : 0; }
  };

  template<>
  struct string_size_impl<std::string> {
    static size_t size(const std::string& s) { return s.size(); }
  };

  template<typename String> size_t string_size(String&& s) {
    using noref_t = typename std::remove_reference<String>::type;
    using string_t = typename std::conditional<std::is_array<noref_t>::value,
                                              noref_t,
                                              typename std::remove_cv<noref_t>::type
                                              >::type;
    return string_size_impl<string_t>::size(s);
  }

  template<typename...>
  struct concatenate_impl;

  template<typename String>
  struct concatenate_impl<String> {
    static size_t size(String&& s) { return string_size(s); }
    static void concatenate(std::string& result, String&& s) { result += s; }
  };

  template<typename String, typename... Rest>
  struct concatenate_impl<String, Rest...> {
    static size_t size(String&& s, Rest&&... rest) {
      return string_size(s)
           + concatenate_impl<Rest...>::size(std::forward<Rest>(rest)...);
    }
    static void concatenate(std::string& result, String&& s, Rest&&... rest) {
      result += s;
      concatenate_impl<Rest...>::concatenate(result, std::forward<Rest>(rest)...);
    }
  };

} // namespace detail

template<typename... Strings>
std::string concatenate(Strings&&... strings) {
  std::string result;
  result.reserve(detail::concatenate_impl<Strings...>::size(std::forward<Strings>(strings)...));
  detail::concatenate_impl<Strings...>::concatenate(result, std::forward<Strings>(strings)...);
  return result;
}

공개 인터페이스에 관한 한 흥미로운 부분은 마지막 template<typename... Strings> std::string concatenate(Strings&&... strings)템플릿입니다. 사용법은 간단합니다.

int main() {
  const string space = " ";
  std::string result = concatenate("hello", space, "world");
  std::cout << result << std::endl;
}

최적화가 켜져 있으면 괜찮은 컴파일러 concatenate는 모든 것을 수동으로 작성한 첫 번째 예제와 동일한 코드로 호출을 확장 할 수 있습니다 . GCC 4.7 및 4.8과 관련하여 생성 된 코드는 성능뿐만 아니라 거의 동일합니다.


WORST 가능한 시나리오는 C 문자열을 사용하므로 끝을 찾기 위해 "계산"되어야하기 때문에 일반 이전 strcat(또는 sprintf)을 사용 strcat하는 것입니다. 긴 문자열의 경우 실제 성능 문제입니다. C ++ 스타일 문자열이 훨씬 낫고 성능 문제는 길이를 계산하는 것이 아니라 메모리 할당에있을 가능성이 높습니다. 그러나 다시, 현은 기하학적으로 성장합니다 (성장해야 할 때마다 두 배가 됨). 그리 끔찍하지 않습니다.

위의 모든 방법이 동일하거나 적어도 매우 유사한 성능으로 끝날 것이라고 생각합니다. stringstream포맷을 지원하는 오버 헤드로 인해 속도가 더 느릴 것으로 예상 되지만 한계가 있다고 생각합니다.

이런 일이 "재미 있기 때문에"벤치 마크로 돌아갈 게요 ...

편집하다:

이 결과는 g ++ 4.6.3으로 컴파일 된 x86-64 Linux를 실행하는 내 컴퓨터에 적용됩니다. 다른 OS, 컴파일러 및 C ++ 런타임 라이브러리 구현은 다를 수 있습니다. 애플리케이션에 성능이 중요한 경우 사용하는 컴파일러를 사용하여 중요한 시스템을 벤치마킹하십시오.

이것을 테스트하기 위해 작성한 코드는 다음과 같습니다. 실제 시나리오를 완벽하게 표현하지 못할 수도 있지만 대표적인 시나리오라고 생각합니다.

#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <cstring>

using namespace std;

static __inline__ unsigned long long rdtsc(void)
{
    unsigned hi, lo;
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}

string build_string_1(const string &a, const string &b, const string &c)
{
    string out = a + b + c;
    return out;
}

string build_string_1a(const string &a, const string &b, const string &c)
{
    string out;
    out.resize(a.length()*3);
    out = a + b + c;
    return out;
}

string build_string_2(const string &a, const string &b, const string &c)
{
    string out = a;
    out += b;
    out += c;
    return out;
}

string build_string_3(const string &a, const string &b, const string &c)
{
    string out;
    out = a;
    out.append(b);
    out.append(c);
    return out;
}


string build_string_4(const string &a, const string &b, const string &c)
{
    stringstream ss;

    ss << a << b << c;
    return ss.str();
}


char *build_string_5(const char *a, const char *b, const char *c)
{
    char* out = new char[strlen(a) * 3+1];
    strcpy(out, a);
    strcat(out, b);
    strcat(out, c);
    return out;
}



template<typename T>
size_t len(T s)
{
    return s.length();
}

template<>
size_t len(char *s)
{
    return strlen(s);
}

template<>
size_t len(const char *s)
{
    return strlen(s);
}



void result(const char *name, unsigned long long t, const string& out)
{
    cout << left << setw(22) << name << " time:" << right << setw(10) <<  t;
    cout << "   (per character: " 
         << fixed << right << setw(8) << setprecision(2) << (double)t / len(out) << ")" << endl;
}

template<typename T>
void benchmark(const char name[], T (Func)(const T& a, const T& b, const T& c), const char *strings[])
{
    unsigned long long t;

    const T s1 = strings[0];
    const T s2 = strings[1];
    const T s3 = strings[2];
    t = rdtsc();
    T out = Func(s1, s2, s3);
    t = rdtsc() - t; 

    if (len(out) != len(s1) + len(s2) + len(s3))
    {
        cout << "Error: out is different length from inputs" << endl;
        cout << "Got `" << out << "` from `" << s1 << "` + `" << s2 << "` + `" << s3 << "`";
    }
    result(name, t, out);
}


void benchmark(const char name[], char* (Func)(const char* a, const char* b, const char* c), 
               const char *strings[])
{
    unsigned long long t;

    const char* s1 = strings[0];
    const char* s2 = strings[1];
    const char* s3 = strings[2];
    t = rdtsc();
    char *out = Func(s1, s2, s3);
    t = rdtsc() - t; 

    if (len(out) != len(s1) + len(s2) + len(s3))
    {
        cout << "Error: out is different length from inputs" << endl;
        cout << "Got `" << out << "` from `" << s1 << "` + `" << s2 << "` + `" << s3 << "`";
    }
    result(name, t, out);
    delete [] out;
}


#define BM(func, size) benchmark(#func " " #size, func, strings ## _ ## size)


#define BM_LOT(size) BM(build_string_1, size); \
    BM(build_string_1a, size); \
    BM(build_string_2, size); \
    BM(build_string_3, size); \
    BM(build_string_4, size); \
    BM(build_string_5, size);

int main()
{
    const char *strings_small[]  = { "Abc", "Def", "Ghi" };
    const char *strings_medium[] = { "abcdefghijklmnopqrstuvwxyz", 
                                     "defghijklmnopqrstuvwxyzabc", 
                                     "ghijklmnopqrstuvwxyzabcdef" };
    const char *strings_large[]   = 
        { "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
          "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", 

          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" 
          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" 
          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" 
          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" 
          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc"

          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" 
          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" 
          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" 
          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" 
          "defghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc", 

          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
          "ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"
        };

    for(int i = 0; i < 5; i++)
    {
        BM_LOT(small);
        BM_LOT(medium);
        BM_LOT(large);
        cout << "---------------------------------------------" << endl;
    }
}

대표적인 결과는 다음과 같습니다.

build_string_1 small   time:      4075   (per character:   452.78)
build_string_1a small  time:      5384   (per character:   598.22)
build_string_2 small   time:      2669   (per character:   296.56)
build_string_3 small   time:      2427   (per character:   269.67)
build_string_4 small   time:     19380   (per character:  2153.33)
build_string_5 small   time:      6299   (per character:   699.89)
build_string_1 medium  time:      3983   (per character:    51.06)
build_string_1a medium time:      6970   (per character:    89.36)
build_string_2 medium  time:      4072   (per character:    52.21)
build_string_3 medium  time:      4000   (per character:    51.28)
build_string_4 medium  time:     19614   (per character:   251.46)
build_string_5 medium  time:      6304   (per character:    80.82)
build_string_1 large   time:      8491   (per character:     3.63)
build_string_1a large  time:      9563   (per character:     4.09)
build_string_2 large   time:      6154   (per character:     2.63)
build_string_3 large   time:      5992   (per character:     2.56)
build_string_4 large   time:     32450   (per character:    13.87)
build_string_5 large   time:     15768   (per character:     6.74)

동일한 코드, 32 비트로 실행 :

build_string_1 small   time:      4289   (per character:   476.56)
build_string_1a small  time:      5967   (per character:   663.00)
build_string_2 small   time:      3329   (per character:   369.89)
build_string_3 small   time:      3047   (per character:   338.56)
build_string_4 small   time:     22018   (per character:  2446.44)
build_string_5 small   time:      3026   (per character:   336.22)
build_string_1 medium  time:      4089   (per character:    52.42)
build_string_1a medium time:      8075   (per character:   103.53)
build_string_2 medium  time:      4569   (per character:    58.58)
build_string_3 medium  time:      4326   (per character:    55.46)
build_string_4 medium  time:     22751   (per character:   291.68)
build_string_5 medium  time:      2252   (per character:    28.87)
build_string_1 large   time:      8695   (per character:     3.72)
build_string_1a large  time:     12818   (per character:     5.48)
build_string_2 large   time:      8202   (per character:     3.51)
build_string_3 large   time:      8351   (per character:     3.57)
build_string_4 large   time:     38250   (per character:    16.35)
build_string_5 large   time:      8143   (per character:     3.48)

이것으로부터 우리는 결론을 내릴 수 있습니다.

  1. 가장 좋은 방법은 "체인"접근 방식을 합리적으로 가깝게 사용하여 한 번에 조금씩 ( out.append()또는 out +=) 추가하는 것 입니다.

  2. 문자열을 미리 할당하는 것은 도움이되지 않습니다.

  3. 사용 stringstream은 매우 좋지 않은 생각입니다 (2-4 배 느림).

  4. char *사용 new char[]. 호출 함수에서 지역 변수를 사용하면 가장 빠르지 만 비교하기에는 약간 불공평합니다.

  5. 짧은 문자열을 결합하는 데 상당한 오버 헤드가 있습니다. 데이터 복사는 바이트 당 최대 1 사이클이어야합니다 (데이터가 캐시에 맞지 않는 경우).

edit2

댓글에 따라 추가됨 :

string build_string_1b(const string &a, const string &b, const string &c)
{
    return a + b + c;
}

string build_string_2a(const string &a, const string &b, const string &c)
{
    string out;
    out.reserve(a.length() * 3);
    out += a;
    out += b;
    out += c;
    return out;
}

결과는 다음과 같습니다.

build_string_1 small   time:      3845   (per character:   427.22)
build_string_1b small  time:      3165   (per character:   351.67)
build_string_2 small   time:      3176   (per character:   352.89)
build_string_2a small  time:      1904   (per character:   211.56)

build_string_1 large   time:      9056   (per character:     3.87)
build_string_1b large  time:      6414   (per character:     2.74)
build_string_2 large   time:      6417   (per character:     2.74)
build_string_2a large  time:      4179   (per character:     1.79)

(32 비트 실행이지만 64 비트는 이와 매우 유사한 결과를 보여줍니다.)


대부분의 마이크로 최적화와 마찬가지로 각 옵션의 효과를 측정해야합니다. 먼저 측정을 통해 이것이 실제로 최적화 할 가치가있는 병목 현상임을 확인했습니다. 확실한 답은 없습니다.

append그리고 +=정확히 같은 일을해야한다.

+임시를 만들고 파괴하기 때문에 개념적으로 덜 효율적입니다. 컴파일러는이를 추가하는 것만 큼 빠르게 최적화 할 수도 있고 그렇지 않을 수도 있습니다.

reserve총 크기로 호출 하면 필요한 메모리 할당 수를 줄일 수 있습니다. 아마도 가장 큰 병목 현상이 될 것입니다.

<<(아마에서 stringstream) 더 빠르거나 빠르지 않을 수 있습니다. 이를 측정해야합니다. 문자열이 아닌 유형의 형식을 지정해야하는 경우 유용하지만 문자열을 처리하는 데 특히 좋거나 나쁘지는 않을 것입니다.

CString 이식성이 없다는 단점이 있으며 저와 같은 유닉스 해커가 그 장점이 무엇인지 아닌지 말할 수 없다는 단점이 있습니다.


사용자 Jesse Good이 제공 한 코드로 테스트를 실행하기로 결정했습니다. Rapptz 관찰 , 특히 ostringstream이 루프의 각 단일 반복에서 구성되었다는 사실을 고려하여 약간 수정되었습니다 . 따라서 몇 가지 경우를 추가했습니다. 그 중 몇 가지는 " oss.str (" "); oss.clear () " 시퀀스로 지워진 ostringstream입니다 .

다음은 코드입니다.

#include <iostream>
#include <string>
#include <chrono>
#include <sstream>
#include <functional>


template <typename F> void time_measurement(F f, const std::string& comment)
{
    typedef std::chrono::high_resolution_clock clock;
    typedef std::chrono::duration<float, std::milli> mil;
    std::string r;
    auto t0 = clock::now();
    f(r);
    auto t1 = clock::now();
    std::cout << "\n-------------------------" << comment << "-------------------\n" <<r << '\n';
    std::cout << mil(t1-t0).count() << "ms\n";
    std::cout << "---------------------------------------------------------------------------\n";

}

inline void clear(std::ostringstream& x)
{
    x.str("");
    x.clear();
}

void test()
{
    std:: cout << std::endl << "----------------String Comparison---------------- " << std::endl;
    const int n=100000;
    {
        auto f=[](std::string& l_czTempStr)
        {
            std::string s1="Test data1";
            for (int i = 0; i < n; ++i)
            {
                l_czTempStr = s1 + "Test data2" + "Test data3";
            }
        };
        time_measurement(f, "string, plain addition");
   }

   {
        auto f=[](std::string& l_czTempStr)
        {
            for (int i = 0; i < n; ++i)
            {
                l_czTempStr =  "Test data1";
                l_czTempStr += "Test data2";
                l_czTempStr += "Test data3";
            }
        };
        time_measurement(f, "string, incremental");
    }

    {
         auto f=[](std::string& l_czTempStr)
         {
            for (int i = 0; i < n; ++i)
            {
                l_czTempStr =  "Test data1";
                l_czTempStr.append("Test data2");
                l_czTempStr.append("Test data3");
            }
         };
         time_measurement(f, "string, append");
     }

    {
         auto f=[](std::string& l_czTempStr)
         {
            for (int i = 0; i < n; ++i)
            {
                std::ostringstream oss;
                oss << "Test data1";
                oss << "Test data2";
                oss << "Test data3";
                l_czTempStr = oss.str();
            }
         };
         time_measurement(f, "oss, creation in each loop, incremental");
     }

    {
         auto f=[](std::string& l_czTempStr)
         {
            std::ostringstream oss;
            for (int i = 0; i < n; ++i)
            {
                oss.str("");
                oss.clear();
                oss << "Test data1";
                oss << "Test data2";
                oss << "Test data3";
            }
            l_czTempStr = oss.str();
         };
         time_measurement(f, "oss, 1 creation, incremental");
     }

    {
         auto f=[](std::string& l_czTempStr)
         {
            std::ostringstream oss;
            for (int i = 0; i < n; ++i)
            {
                oss.str("");
                oss.clear();
                oss << "Test data1" << "Test data2" << "Test data3";
            }
            l_czTempStr = oss.str();
         };
         time_measurement(f, "oss, 1 creation, plain addition");
     }

    {
         auto f=[](std::string& l_czTempStr)
         {
            std::ostringstream oss;
            for (int i = 0; i < n; ++i)
            {
                clear(oss);
                oss << "Test data1" << "Test data2" << "Test data3";
            }
            l_czTempStr = oss.str();
         };
         time_measurement(f, "oss, 1 creation, clearing calling inline function, plain addition");
     }


    {
         auto f=[](std::string& l_czTempStr)
         {
            for (int i = 0; i < n; ++i)
            {
                std::string x;
                x =  "Test data1";
                x.append("Test data2");
                x.append("Test data3");
                l_czTempStr=x;
            }
         };
         time_measurement(f, "string, creation in each loop");
     }

}

결과는 다음과 같습니다.

/*

g++ "qtcreator debug mode"
----------------String Comparison---------------- 

-------------------------string, plain addition-------------------
Test data1Test data2Test data3
11.8496ms
---------------------------------------------------------------------------

-------------------------string, incremental-------------------
Test data1Test data2Test data3
3.55597ms
---------------------------------------------------------------------------

-------------------------string, append-------------------
Test data1Test data2Test data3
3.53099ms
---------------------------------------------------------------------------

-------------------------oss, creation in each loop, incremental-------------------
Test data1Test data2Test data3
58.1577ms
---------------------------------------------------------------------------

-------------------------oss, 1 creation, incremental-------------------
Test data1Test data2Test data3
11.1069ms
---------------------------------------------------------------------------

-------------------------oss, 1 creation, plain addition-------------------
Test data1Test data2Test data3
10.9946ms
---------------------------------------------------------------------------

-------------------------oss, 1 creation, clearing calling inline function, plain addition-------------------
Test data1Test data2Test data3
10.9502ms
---------------------------------------------------------------------------

-------------------------string, creation in each loop-------------------
Test data1Test data2Test data3
9.97495ms
---------------------------------------------------------------------------


g++ "qtcreator release mode" (optimized)
----------------String Comparison----------------

-------------------------string, plain addition-------------------
Test data1Test data2Test data3
8.41622ms
---------------------------------------------------------------------------

-------------------------string, incremental-------------------
Test data1Test data2Test data3
2.55462ms
---------------------------------------------------------------------------

-------------------------string, append-------------------
Test data1Test data2Test data3
2.5154ms
---------------------------------------------------------------------------

-------------------------oss, creation in each loop, incremental-------------------
Test data1Test data2Test data3
54.3232ms
---------------------------------------------------------------------------

-------------------------oss, 1 creation, incremental-------------------
Test data1Test data2Test data3
8.71854ms
---------------------------------------------------------------------------

-------------------------oss, 1 creation, plain addition-------------------
Test data1Test data2Test data3
8.80526ms
---------------------------------------------------------------------------

-------------------------oss, 1 creation, clearing calling inline function, plain addition-------------------
Test data1Test data2Test data3
8.78186ms
---------------------------------------------------------------------------

-------------------------string, creation in each loop-------------------
Test data1Test data2Test data3
8.4034ms
---------------------------------------------------------------------------
*/

이제 std :: string을 사용하는 것이 더 빠르며 추가는 여전히 가장 빠른 연결 방법이지만 ostringstream은 이전처럼 더 이상 그렇게 끔찍하지 않습니다.


There are some significant parameters, which has potential impact on deciding the "most optimized way". Some of these are - string/content size, number of operations, compiler optimization, etc.

In most of the cases, string::operator+= seems to be working best. However at times, on some compilers, it is also observed that ostringstream::operator<< works best [like - MingW g++ 3.2.3, 1.8 GHz single processor Dell PC]. When compiler context comes, then it is majorly the optimizations at compiler which would impact. Also to mention, that stringstreams are complex objects as compared to simple strings, and therefore adds to the overhead.

For more info - discussion, article.

참고URL : https://stackoverflow.com/questions/18892281/most-optimized-way-of-concatenation-in-strings

반응형