2017-07-11 15 views
12

कुछ प्रदर्शन प्रयोगों के बाद, ऐसा लगता है कि char16_t arrays का उपयोग कभी-कभी 40-50% तक प्रदर्शन को बढ़ावा दे सकता है, लेकिन ऐसा लगता है कि बिना किसी प्रतिलिपि और आवंटन के std :: u16string का उपयोग सी सरणी के रूप में तेज़ होना चाहिए। हालांकि, बेंचमार्क विपरीत दिखा रहे हैं।क्यों std :: u16string char16_t की सरणी से धीमी है?

यहाँ कोड मैं बेंचमार्क के लिए लिखा है है (यह गूगल बेंचमार्क lib का उपयोग करता है):

#include "benchmark/benchmark.h" 
#include <string> 

static std::u16string str; 
static char16_t *str2; 

static void BM_Strings(benchmark::State &state) { 
    while (state.KeepRunning()) { 
     for (size_t i = 0; i < str.size(); i++){ 
      benchmark::DoNotOptimize(str[i]); 
     } 
    } 
} 

static void BM_CharArray(benchmark::State &state) { 
    while (state.KeepRunning()) { 
     for (size_t i = 0; i < str.size(); i++){ 
      benchmark::DoNotOptimize(str2[i]); 
     } 
    } 
} 

BENCHMARK(BM_Strings); 
BENCHMARK(BM_CharArray); 

static void init(){ 
    str = u"Various applications of randomness have led to the development of several different methods "; 
    str2 = (char16_t *) str.c_str(); 
} 

int main(int argc, char** argv) { 
    init(); 
    ::benchmark::Initialize(&argc, argv); 
    ::benchmark::RunSpecifiedBenchmarks(); 
} 

यह निम्न परिणाम दिखाता है:

Run on (8 X 2200 MHz CPU s) 
2017-07-11 23:05:57 
Benchmark    Time   CPU Iterations 
--------------------------------------------------- 
BM_Strings   1832 ns  1830 ns  365938 
BM_CharArray  928 ns  926 ns  712577 

मैं बजना उपयोग कर रहा हूँ (एप्पल LLVM मैक पर संस्करण 8.1.0 (क्लैंग -802.0.42))। अनुकूलन अंतराल के चालू रहते हुए छोटे, लेकिन अभी भी ध्यान देने योग्य है:

Benchmark    Time   CPU Iterations 
--------------------------------------------------- 
BM_Strings   242 ns  241 ns 2906615 
BM_CharArray  161 ns  161 ns 4552165 

कोई व्याख्या कर सकते हैं यहाँ क्या हो रहा है और यही कारण है कि एक अंतर है?

अपडेट किया गया (कुछ वार्म अप चरणों आदेश मिश्रण और कहा):

Benchmark    Time   CPU Iterations 
--------------------------------------------------- 
BM_CharArray  670 ns  665 ns  903168 
BM_Strings   856 ns  854 ns  817776 
BM_CharArray  166 ns  166 ns 4369997 
BM_Strings   225 ns  225 ns 3149521 

इसके अलावा सहित झंडे मैं उपयोग कर रहा हूँ संकलन:

/usr/bin/clang++ -I{some includes here} -O3 -std=c++14 -stdlib=libc++ -Wall -Wextra -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -O3 -fsanitize=address -Werror -o CMakeFiles/BenchmarkString.dir/BenchmarkString.cpp.o -c test/benchmarks/BenchmarkString.cpp 
+0

कौन सा संकलक झंडे आप के लिए उपयोग कर रहे हैं अनुकूलित निर्माण? –

+0

-O0 के साथ पहला परिणाम - कोई अनुकूलन नहीं, दूसरा - -फास्ट –

+5

यदि आप बेंचमार्क का ऑर्डर बदलते हैं तो क्या आपको संगत परिणाम मिलते हैं? साथ ही, आपको '-O0' के साथ किसी भी बेंचमार्क को अनदेखा करना चाहिए। – juanchopanza

उत्तर

27
जिस तरह से libC++ के

छोटे स्ट्रिंग को लागू करता है ऑप्टिमाइज़ेशन, प्रत्येक अव्यवस्था पर इसे जांचने की आवश्यकता होती है कि स्ट्रिंग सामग्री स्ट्रिंग ऑब्जेक्ट में या ढेर पर संग्रहीत होती है या नहीं। चूंकि अनुक्रमण benchmark::DoNotOptimize में लपेटा गया है, इसलिए जब भी चरित्र का उपयोग किया जाता है, तो उसे इस चेक को करने की आवश्यकता होती है। पॉइंटर के माध्यम से स्ट्रिंग डेटा तक पहुंचने पर डेटा हमेशा बाहरी होता है, और इसलिए कोई चेक की आवश्यकता नहीं होती है।

0

शुद्ध char16_t में आप पहुँच सरणी सीधे, जबकि स्ट्रिंग में आप अतिभारित है ऑपरेटर []

reference 
operator[](size_type __pos) 
{ 
    #ifdef _GLIBCXX_DEBUG_PEDANTIC 
    __glibcxx_check_subscript(__pos); 
#else 
    // as an extension v3 allows s[s.size()] when s is non-const. 
    _GLIBCXX_DEBUG_VERIFY(__pos <= this->size(), 
     _M_message(__gnu_debug::__msg_subscript_oob) 
     ._M_sequence(*this, "this") 
     ._M_integer(__pos, "__pos") 
     ._M_integer(this->size(), "size")); 
#endif 
    return _M_base()[__pos]; 
} 

और _M_base() है:

_Base& _M_base() { return *this; } 

अब, मेरे अनुमान हैं कि या तो:

  1. _M_base() शायद रेखांकित नहीं हो सकता है और आपको प्रत्येक पठन के कारण प्रदर्शन हिट करने की बजाय कार्य को पढ़ने के लिए अतिरिक्त ऑपरेशन होता है पता।

या

  1. उन सबस्क्रिप्ट चेकों में से एक होता है।
0

दिलचस्प बात यह है कि मैं आपके परिणामों को पुन: उत्पन्न करने में असमर्थ हूं। मैं मुश्किल से दोनों के बीच एक अंतर का पता लगा सकते हैं।

(अधूरा) कोड मैं इस्तेमाल किया यहाँ दिखाया गया है:

hol::StdTimer timer; 

using index_type = std::size_t; 

index_type const N = 100'000'000; 
index_type const SIZE = 1024; 

static std::u16string s16; 
static char16_t const* p16; 

int main(int, char** argv) 
{ 
    std::generate_n(std::back_inserter(s16), SIZE, 
     []{ return (char)hol::random_number((int)'A', (int)'Z'); }); 

    p16 = s16.c_str(); 
    unsigned sum; 

    { 
     sum = 0; 

     timer.start(); 
     for(index_type n = 0; n < N; ++n) 
      for(index_type i = 0; i < SIZE; ++i) 
       sum += s16[i]; 
     timer.stop(); 

     RESULT("string", sum, timer); 
    } 

    { 
     sum = 0; 

     timer.start(); 
     for(std::size_t n = 0; n < N; ++n) 
      for(std::size_t i = 0; i < SIZE; ++i) 
       sum += p16[i]; 
     timer.stop(); 

     RESULT("array ", sum, timer); 
    } 
} 

आउटपुट:

string: (670240768) 17.575232 secs 
array : (670240768) 17.546145 secs 

संकलक:

GCC 7.1 
g++ -std=c++14 -march=native -O3 -D NDEBUG 
+4

आश्चर्य की बात नहीं है। libstdC++ एसएसओ libC++ से एक अलग डिज़ाइन का उपयोग करता है। –

+1

libstdC++ में, स्ट्रिंग के लिए हमेशा एक सूचक होता है। क्लैंग के संस्करण में, सूचक केवल तभी मौजूद होता है जब यह एक लंबी स्ट्रिंग है। इसलिए पॉइंटर प्राप्त करने के लिए 'if' की आवश्यकता होती है। – geza

+0

@ टी.सी. मैं सोच रहा था कि भविष्यवाणी शाखा अनुकूलन 'एसएसओ' चेक (एक तंग पाश में) का ख्याल रखेगा। लेकिन स्रोत कोड को देखते हुए मुझे यह भी यकीन नहीं है कि 'जीसीसी' 'एसएसओ 'लागू करता है, ऐसा लगता है कि यह सिर्फ एक प्रत्यक्ष सूचक है ... – Galik

संबंधित मुद्दे