2009-05-01 19 views
24

दोनों को ओ (एन लॉग एन) में चलाना चाहिए, लेकिन सामान्य रूप से स्थिर_सॉर्ट से तेज़ है। अभ्यास में प्रदर्शन अंतर कितना बड़ा है? क्या आपके पास इसके बारे में कुछ अनुभव है?अभ्यास में std :: sort और std :: stable_sort के बीच प्रदर्शन अंतर कितना बड़ा है?

मैं लगभग 20 बाइट्स के आकार वाले बहुत से ढांचे को सॉर्ट करना चाहता हूं। परिणाम की स्थिरता मेरे मामले में अच्छी होगी, लेकिन यह जरूरी नहीं है। फिलहाल अंतर्निहित कंटेनर एक सादा सरणी है, शायद इसे बाद में एक std :: डेक में बदला जा सकता है।

उत्तर

15

सैद्धांतिक रूप से एल्गोरिदम की तुलना में अच्छे उत्तर हैं। मैंने जिज्ञासा के लिए google/benchmark के साथ std::sort और std::stable_sort को बेंचमार्क किया।

यह समय से पहले इंगित करना उपयोगी है;

  • बेंचमार्क मशीन 1 X 2500 MHz CPU और 1 GB RAM
  • बेंचमार्क ओएस Arch Linux 2015.08 x86-64
  • बेंचमार्क g++ 5.3.0 और clang++ 3.7.0 (-std=c++11, -O3 और -pthread) के साथ संकलित
  • BM_Base* बेंचमार्क समय को आबाद करने std::vector<> को मापने की कोशिश करता है है। उस समय बेहतर तुलना के लिए सॉर्टिंग परिणामों से घटाया जाना चाहिए।

पहले बेंचमार्क 512k आकार के साथ std::vector<int> क्रमबद्ध करता है।

[ g++ ]# benchmark_sorts --benchmark_repetitions=10 
Run on (1 X 2500 MHz CPU) 
2016-01-08 01:37:43 
Benchmark       Time(ns) CPU(ns) Iterations 
---------------------------------------------------------------- 
... 
BM_BaseInt/512k_mean    24730499 24726189   28 
BM_BaseInt/512k_stddev    293107  310668   0 
... 
BM_SortInt/512k_mean    70967679 70799990   10 
BM_SortInt/512k_stddev    1300811 1301295   0 
... 
BM_StableSortInt/512k_mean  73487904 73481467   9 
BM_StableSortInt/512k_stddev  979966  925172   0 
[ clang++ ]# benchmark_sorts --benchmark_repetitions=10 
Run on (1 X 2500 MHz CPU) 
2016-01-08 01:39:07 
Benchmark       Time(ns) CPU(ns) Iterations 
---------------------------------------------------------------- 
... 
BM_BaseInt/512k_mean    26198558 26197526   27 
BM_BaseInt/512k_stddev    320971  348314   0 
... 
BM_SortInt/512k_mean    70648019 70666660   10 
BM_SortInt/512k_stddev    2030727 2033062   0 
... 
BM_StableSortInt/512k_mean  82004375 81999989   9 
BM_StableSortInt/512k_stddev  197309  181453   0 

दूसरा बेंचमार्क 512k आकार (sizeof(Struct S) = 20) के साथ std::vector<S> क्रमबद्ध करता है।

[ g++ ]# benchmark_sorts --benchmark_repetitions=10 
Run on (1 X 2500 MHz CPU) 
2016-01-08 01:49:32 
Benchmark       Time(ns) CPU(ns) Iterations 
---------------------------------------------------------------- 
... 
BM_BaseStruct/512k_mean   26485063 26410254   26 
BM_BaseStruct/512k_stddev   270355  128200   0 
... 
BM_SortStruct/512k_mean   81844178 81833325   8 
BM_SortStruct/512k_stddev   240868  204088   0 
... 
BM_StableSortStruct/512k_mean 106945879 106857114   7 
BM_StableSortStruct/512k_stddev 10446119 10341548   0 
[ clang++ ]# benchmark_sorts --benchmark_repetitions=10 
Run on (1 X 2500 MHz CPU) 
2016-01-08 01:53:01 
Benchmark       Time(ns) CPU(ns) Iterations 
---------------------------------------------------------------- 
... 
BM_BaseStruct/512k_mean   27327329 27280000   25 
BM_BaseStruct/512k_stddev   488318  333059   0 
... 
BM_SortStruct/512k_mean   78611207 78407400   9 
BM_SortStruct/512k_stddev   690207  372230   0 
... 
BM_StableSortStruct/512k_mean 109477231 109333325   8 
BM_StableSortStruct/512k_stddev 11697084 11506626   0 

जो कोई बेंचमार्क चलाने के लिए पसंद करती है यहाँ कोड है,

#include <vector> 
#include <random> 
#include <algorithm> 

#include "benchmark/benchmark_api.h" 

#define SIZE 1024 << 9 

static void BM_BaseInt(benchmark::State &state) { 
    std::random_device rd; 
    std::mt19937 mt(rd()); 
    std::uniform_int_distribution<int> dist; 

    while (state.KeepRunning()) { 
    std::vector<int> v; 
    v.reserve(state.range_x()); 
    for (int i = 0; i < state.range_x(); i++) { 
     v.push_back(dist(mt)); 
    } 
    } 
} 
BENCHMARK(BM_BaseInt)->Arg(SIZE); 

static void BM_SortInt(benchmark::State &state) { 
    std::random_device rd; 
    std::mt19937 mt(rd()); 
    std::uniform_int_distribution<int> dist; 

    while (state.KeepRunning()) { 
    std::vector<int> v; 
    v.reserve(state.range_x()); 
    for (int i = 0; i < state.range_x(); i++) { 
     v.push_back(dist(mt)); 
    } 

    std::sort(v.begin(), v.end()); 
    } 
} 
BENCHMARK(BM_SortInt)->Arg(SIZE); 

static void BM_StableSortInt(benchmark::State &state) { 
    std::random_device rd; 
    std::mt19937 mt(rd()); 
    std::uniform_int_distribution<int> dist; 

    while (state.KeepRunning()) { 
    std::vector<int> v; 
    v.reserve(state.range_x()); 
    for (int i = 0; i < state.range_x(); i++) { 
     v.push_back(dist(mt)); 
    } 

    std::stable_sort(v.begin(), v.end()); 
    } 
} 
BENCHMARK(BM_StableSortInt)->Arg(SIZE); 


struct S { 
    int key; 
    int arr[4]; 
}; 

static void BM_BaseStruct(benchmark::State &state) { 
    std::random_device rd; 
    std::mt19937 mt(rd()); 
    std::uniform_int_distribution<int> dist; 

    while (state.KeepRunning()) { 
    std::vector<S> v; 
    v.reserve(state.range_x()); 
    for (int i = 0; i < state.range_x(); i++) { 
     v.push_back({dist(mt)}); 
    } 
    } 
} 
BENCHMARK(BM_BaseStruct)->Arg(SIZE); 

static void BM_SortStruct(benchmark::State &state) { 
    std::random_device rd; 
    std::mt19937 mt(rd()); 
    std::uniform_int_distribution<int> dist; 

    while (state.KeepRunning()) { 
    std::vector<S> v; 
    v.reserve(state.range_x()); 
    for (int i = 0; i < state.range_x(); i++) { 
     v.push_back({dist(mt)}); 
    } 

    std::sort(v.begin(), v.end(), 
       [](const S &a, const S &b) { return a.key < b.key; }); 
    } 
} 
BENCHMARK(BM_SortStruct)->Arg(SIZE); 

static void BM_StableSortStruct(benchmark::State &state) { 
    std::random_device rd; 
    std::mt19937 mt(rd()); 
    std::uniform_int_distribution<int> dist; 

    while (state.KeepRunning()) { 
    std::vector<S> v; 
    v.reserve(state.range_x()); 
    for (int i = 0; i < state.range_x(); i++) { 
     v.push_back({dist(mt)}); 
    } 

    std::stable_sort(v.begin(), v.end(), 
        [](const S &a, const S &b) { return a.key < b.key; }); 
    } 
} 
BENCHMARK(BM_StableSortStruct)->Arg(SIZE); 


BENCHMARK_MAIN(); 
+9

वास्तव में आगे जा रहा है और बेंचमार्क कर लिए +1 - – 5gon12eder

9

एक अलग फ़ंक्शन की गारंटी देने के लिए पर्याप्त है जो स्थिर प्रकार करता है और std::sort() यह पारदर्शी रूप से नहीं करता है।

+0

HAHAH। ठीक है, तो शायद सबसे तकनीकी रूप से विस्तृत उत्तर नहीं, लेकिन निश्चित रूप से मेरा पसंदीदा। –

15

std::stable_sort पर्याप्त स्मृति उपलब्ध होने पर NlogN तुलना करता है। जब अपर्याप्त स्मृति उपलब्ध होती है, तो यह एन ((लॉगएन)^2) तुलना में घट जाती है। इसलिए यह लगभग(जो औसत और सबसे खराब मामले में ओ (एनएलओएनएन) तुलना करता है, वही दक्षता के लगभग समान है) जब स्मृति उपलब्ध हो।

रुचि रखने वालों के, प्रकार के लिए() एक introsort (quicksort जो heapsort जब प्रत्यावर्तन एक निश्चित गहराई तक पहुँच जाता स्विच)) और stable_sort (एक merge sort का उपयोग करता है का उपयोग करता है।

+6

लॉगएन^2 ususually का मतलब लॉग (एन^2) नहीं है (लेकिन लॉग एन)^2, और विशेष रूप से यदि यह बड़े-ओ नोटेशन में होता है।यह आमतौर पर एल्गोरिदम कि हे (लॉग एन) चरण के लिए काम करते हैं और इसके विपरीत के साथ हे (एन लॉग ऑन एन) के लिए कदम उठाने के साथ होता है। तो, नहीं, यह नहीं एक निरंतर 2. – MSalters

+0

आप सही हैं, एक परिणाम के रूप में काफी मेरा उत्तर बदल दिया है। – marcog

+0

आप कहते हैं कि 'std :: प्रकार (जो दोनों औसत में ओ (NlogN) की तुलना करता है और सबसे ज्यादा मामले)'। लेकिन std :: sort का लिंक कहता है: ओ (एन · लॉग (एन)) सभी मामलों में केवल सी ++ 11 के बाद से। सी ++ 11 से पहले सबसे खराब मामले के लिए ओ (एनएलओएल (एन)) की कोई आवश्यकता नहीं थी। –

2

अभ्यास में प्रदर्शन अंतर कितना बड़ा है? क्या आपके पास इसके बारे में कुछ अनुभव है?

हां, लेकिन यह आपके द्वारा अपेक्षित तरीके से नहीं चला था।

मैंने Burrows-Wheeler Transform और C++ के सी कार्यान्वयन को लिया - इसे लागू किया गया। सी कोड से बहुत धीमा हो गया (हालांकि कोड क्लीनर था)। तो मैंने वहां समय सारिणी लगाई और ऐसा प्रतीत होता है कि qsort std :: sort से तेज प्रदर्शन कर रहा था। यह वीसी 6 में चल रहा था। इसके बाद स्थिर_सॉर्ट के साथ पुन: संकलित किया गया और परीक्षण सी संस्करण की तुलना में तेज़ी से भाग गया। अन्य अनुकूलन सी ++ संस्करण ~ सी संस्करण की तुलना में 25% तेज करने में कामयाब रहे। मुझे लगता है कि अधिक गति प्राप्त करना संभव था लेकिन कोड की स्पष्टता गायब हो रही थी।

+0

मूल रूप से मैं क्या कहने की कोशिश कर रहा हूँ दोनों तरीकों से कोशिश है। –

9

कभी-कभी std :: stable_sort() की आवश्यकता होती है क्योंकि यह बराबर तत्वों के क्रम को बनाए रखता है।

और पारंपरिक सलाह यह है कि, यदि आदेश बनाए रखना महत्वपूर्ण नहीं है, तो आपको इसके बजाय std :: sort() का उपयोग करना चाहिए।

हालांकि, इसका संदर्भ निर्भर है। बहुत सारे डेटा हैं जो स्थिर प्रकार के साथ सबसे अच्छे क्रमबद्ध होते हैं, भले ही आपको ऑर्डर को बनाए रखने की आवश्यकता न हो:

क्विक्सोर्ट तेजी से खराब स्थिति के मामले में सबसे खराब स्थिति प्रदर्शन बन जाता है।

Burrows-Wheeler Transform डेटा संपीड़न के हिस्से के रूप में उपयोग किया जाने वाला एक एल्गोरिदम है, उदा। bzip2। इसे पाठ के सभी घूर्णन को क्रमबद्ध करने की आवश्यकता है। अधिकांश टेक्स्ट डेटा के लिए, सॉर्ट मर्ज करें (जैसा कि अक्सर std :: stable_sort() द्वारा उपयोग किया जाता है) Quicksort (आमतौर पर std :: sort() द्वारा उपयोग किया जाता है) से काफी तेज है।

bbb एक बीडब्ल्यूटी कार्यान्वयन है जो इस एप्लिकेशन के लिए std :: stable_sort() के प्रकार() के फायदे नोट करता है।

1

यदि आप बड़ी संख्या में structs को क्रमबद्ध कर रहे हैं, तो आपकी स्मृति/डिस्क की आईओ गति एसिम्पटोटिक चलने का समय से अधिक महत्वपूर्ण हो जाती है। इसके अलावा, स्मृति उपयोग को भी ध्यान में रखा जाना चाहिए।

मैंने 2 जीबी डेटा (64 बी structs) पर std :: stable_sort की कोशिश की, यह नहीं जानते कि std :: stable_sort डेटा की एक आंतरिक प्रति बनाता है। स्वैप कचरा जो लगभग मेरे पीसी को बंद कर दिया।

अस्थिर std :: सॉर्ट का उपयोग करके 2 के कारक द्वारा स्मृति उपयोग को कम कर देता है, जो बड़े सरणी को सॉर्ट करते समय उपयोगी होता है। मैंने std :: stable_sort को समाप्त कर दिया है, इसलिए मैं यह निर्धारित नहीं कर सकता कि यह कितना धीमा था। हालांकि, अगर स्थिर प्रकार की आवश्यकता नहीं है, तो मुझे लगता है कि अस्थिर std :: sort का उपयोग करना बेहतर है।

+0

हालांकि यह शायद आधुनिक कंप्यूटरों पर बहुत छोटा है, जब आप बहुत बड़े डेटा सेट छँटाई कर रहे हैं, आप एक बाहरी छंटाई एल्गोरिथ्म जो डिस्क का अच्छा इस्तेमाल मध्यवर्ती परिणाम स्टोर करने के लिए बनाता है का उपयोग कर बेहतर कर रहे हैं; जैसे बाहरी विलय सॉर्ट। – Clearer

+0

(, और इसके विपरीत आदि structs के आकार, उपलब्ध रैम, पर निर्भर करता है और कभी कभी संकेत छँटाई structs छँटाई की तुलना में बहुत तेजी से होता है)। आपका काम ध्वनि दिखता है। परिणाम अधिक सुलभ होंगे, हालांकि, यदि आप उन्हें छोटे चित्र को चित्रित करने जैसे फ़ॉर्म को पचाने में आसान बनाते हैं। –

1

कुछ इसी तरह के लिए देख रहा था - लेकिन कोई भी सहायक अंतरिक्ष के बारे में बात आश्चर्य हुआ।

जैसा कि मेरा मानना ​​है - स्थिर_आर्ट और सॉर्ट दोनों के कार्यान्वयन को सभी (सर्वश्रेष्ठ, औसत & सबसे खराब) मामलों के लिए ओ (एनएलओएनएन) की गारंटी देना है।

हालांकि, उपयोग की जाने वाली सहायक जगह में अंतर मौजूद है। स्थिर_सार्ट को ओ (एन) की सहायक जगह की आवश्यकता है।

उस स्थान को प्राप्त करने में प्रदर्शन में अंतर हो सकता है। :)
अन्यथा, सैद्धांतिक रूप से - वे समान w.r.t प्रदर्शन होना चाहिए। > stable_sort बराबर मूल्यों के साथ तत्वों के सापेक्ष आदेश को बरकरार रखता है -

तरह तुम क्या जरूरत है जब तक आप की जरूरत है इस करना चाहिए।

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