2015-09-21 6 views
5

एक हालिया चर्चा ने मुझे आश्चर्यचकित कर दिया कि एक नियमित पूर्णांक वृद्धि की तुलना में परमाणु वृद्धि कितनी महंगा है।मापना धीमी परमाणु वृद्धि नियमित इंटीजर वृद्धि की तुलना में

मैं यह कोशिश करने के लिए कुछ कोड और बेंचमार्क लिखा है:

#include <iostream> 
#include <atomic> 
#include <chrono> 

static const int NUM_TEST_RUNS = 100000; 
static const int ARRAY_SIZE = 500; 

void runBenchmark(std::atomic<int>& atomic_count, int* count_array, int array_size, bool do_atomic_increment){  
    for(int i = 0; i < array_size; ++i){ 
     ++count_array[i];   
    } 

    if(do_atomic_increment){ 
     ++atomic_count; 
    } 
} 

int main(int argc, char* argv[]){ 

    int num_test_runs = NUM_TEST_RUNS; 
    int array_size = ARRAY_SIZE; 

    if(argc == 3){ 
     num_test_runs = atoi(argv[1]); 
     array_size = atoi(argv[2]);   
    } 

    if(num_test_runs == 0 || array_size == 0){ 
     std::cout << "Usage: atomic_operation_overhead <num_test_runs> <num_integers_in_array>" << std::endl; 
     return 1; 
    } 

    // Instantiate atomic counter 
    std::atomic<int> atomic_count; 

    // Allocate the integer buffer that will be updated every time 
    int* count_array = new int[array_size]; 

    // Track the time elapsed in case of incrmeenting with mutex locking 
    auto start = std::chrono::steady_clock::now(); 
    for(int i = 0; i < num_test_runs; ++i){ 
     runBenchmark(atomic_count, count_array, array_size, true);   
    } 
    auto end = std::chrono::steady_clock::now(); 

    // Calculate time elapsed for incrementing without mutex locking 
    auto diff_with_lock = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start); 
    std::cout << "Elapsed time with atomic increment for " 
       << num_test_runs << " test runs: " 
       << diff_with_lock.count() << " ns" << std::endl; 

    // Track the time elapsed in case of incrementing without a mutex locking 
    start = std::chrono::steady_clock::now(); 
    for(unsigned int i = 0; i < num_test_runs; ++i){ 
     runBenchmark(atomic_count, count_array, array_size, false); 
    } 
    end = std::chrono::steady_clock::now(); 

    // Calculate time elapsed for incrementing without mutex locking 
    auto diff_without_lock = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start); 
    std::cout << "Elapsed time without atomic increment for " 
       << num_test_runs << " test runs: " 
       << diff_without_lock.count() << " ns" << std::endl; 

    auto difference_running_times = diff_with_lock - diff_without_lock; 
    auto proportion = difference_running_times.count()/(double)diff_without_lock.count();   
    std::cout << "How much slower was locking: " << proportion * 100.0 << " %" << std::endl;   

    // We loop over all entries in the array and print their sum 
    // We do this mainly to prevent the compiler from optimizing out 
    // the loop where we increment all the values in the array 
    int array_sum = 0; 
    for(int i = 0; i < array_size; ++i){ 
     array_sum += count_array[i]; 
    } 
    std::cout << "Array sum (just to prevent loop getting optimized out): " << array_sum << std::endl; 

    delete [] count_array; 

    return 0; 
} 

मुसीबत मैं कर रहा हूँ कि इस कार्यक्रम प्रत्येक समय में व्यापक रूप से अलग-अलग परिणाम का उत्पादन होता है:

[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 1000 500 
Elapsed time with atomic increment for 1000 test runs: 99852 ns 
Elapsed time without atomic increment for 1000 test runs: 96396 ns 
How much slower was locking: 3.58521 % 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 1000 500 
Elapsed time with atomic increment for 1000 test runs: 182769 ns 
Elapsed time without atomic increment for 1000 test runs: 138319 ns 
How much slower was locking: 32.1359 % 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 1000 500 
Elapsed time with atomic increment for 1000 test runs: 98858 ns 
Elapsed time without atomic increment for 1000 test runs: 96404 ns 
How much slower was locking: 2.54554 % 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 1000 500 
Elapsed time with atomic increment for 1000 test runs: 107848 ns 
Elapsed time without atomic increment for 1000 test runs: 105174 ns 
How much slower was locking: 2.54245 % 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 1000 500 
Elapsed time with atomic increment for 1000 test runs: 113865 ns 
Elapsed time without atomic increment for 1000 test runs: 100559 ns 
How much slower was locking: 13.232 % 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 1000 500 
Elapsed time with atomic increment for 1000 test runs: 98956 ns 
Elapsed time without atomic increment for 1000 test runs: 106639 ns 
How much slower was locking: -7.20468 % 

यह विश्वास करने के लिए मुझे का कारण बनता है कि बेंचमार्किंग कोड में एक बग हो सकता है। क्या मुझे कुछ गलती है? क्या बेंचमार्किंग के लिए std :: chrono का मेरा उपयोग गलत है? या परमाणु परिचालन से संबंधित ओएस में सिग्नल हैंडलिंग के ओवरहेड के कारण समय अंतर है?

मैं गलत क्या कर सकता था?

टेस्ट बिस्तर:

Intel® Core™ i7-4700MQ CPU @ 2.40GHz × 8 
8GB RAM 
GNU/Linux:Ubuntu LTS 14.04 (64 bit) 
GCC version: 4.8.4  
Compilation: g++ -std=c++11 -O3 atomic_operation_overhead.cpp -o atomic_operation_overhead 

संपादित करें: -O3 अनुकूलन के साथ संकलन के बाद परीक्षण चालन उत्पादन अपडेट किया गया।

संपादित करें:

[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 99999999 500 
Elapsed time with atomic increment for 99999999 test runs: 7111974931 ns 
Elapsed time without atomic increment for 99999999 test runs: 6938317779 ns 
How much slower was locking: 2.50287 % 
Array sum (just to prevent loop getting optimized out): 1215751192 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 99999999 500 
Elapsed time with atomic increment for 99999999 test runs: 7424952991 ns 
Elapsed time without atomic increment for 99999999 test runs: 7262721866 ns 
How much slower was locking: 2.23375 % 
Array sum (just to prevent loop getting optimized out): 1215751192 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 99999999 500 
Elapsed time with atomic increment for 99999999 test runs: 7172114343 ns 
Elapsed time without atomic increment for 99999999 test runs: 7030985219 ns 
How much slower was locking: 2.00725 % 
Array sum (just to prevent loop getting optimized out): 1215751192 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 99999999 500 
Elapsed time with atomic increment for 99999999 test runs: 7094552104 ns 
Elapsed time without atomic increment for 99999999 test runs: 6971060941 ns 
How much slower was locking: 1.77148 % 
Array sum (just to prevent loop getting optimized out): 1215751192 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 99999999 500 
Elapsed time with atomic increment for 99999999 test runs: 7099907902 ns 
Elapsed time without atomic increment for 99999999 test runs: 6970289856 ns 
How much slower was locking: 1.85958 % 
Array sum (just to prevent loop getting optimized out): 1215751192 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 99999999 500 
Elapsed time with atomic increment for 99999999 test runs: 7763604675 ns 
Elapsed time without atomic increment for 99999999 test runs: 7229145316 ns 
How much slower was locking: 7.39312 % 
Array sum (just to prevent loop getting optimized out): 1215751192 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 99999999 500 
Elapsed time with atomic increment for 99999999 test runs: 7164534212 ns 
Elapsed time without atomic increment for 99999999 test runs: 6994993609 ns 
How much slower was locking: 2.42374 % 
Array sum (just to prevent loop getting optimized out): 1215751192 
[email protected]:~/Projects/Misc$ ./atomic_operation_overhead 99999999 500 
Elapsed time with atomic increment for 99999999 test runs: 7154697145 ns 
Elapsed time without atomic increment for 99999999 test runs: 6997030700 ns 
How much slower was locking: 2.25333 % 
Array sum (just to prevent loop getting optimized out): 1215751192 
+1

आपने अपना कोड कैसे संकलित किया? क्या अनुकूलन झंडे के साथ? कौन सा कंपाइलर संस्करण? और आपको कई बार दो बार दोहराना चाहिए! –

+0

@ बेसिलस्टारनकेविच इसे इंगित करने के लिए धन्यवाद। मैंने कंपाइलर जानकारी के साथ पोस्ट को अपडेट करने का संपादन किया है। कई बार चलने के लिए, ध्यान दें कि run_benchmark पहले से ही कई बार चलाया गया है (कमांडलाइन तर्क का उपयोग करके निर्दिष्ट)। – balajeerc

+1

आप अपने संकलन कमांड में कम से कम '-O1' या' -O2' (या यहां तक ​​कि '-O3') भूल गए हैं। अनुकूलन के बिना संकलित बेंचमार्किंग कोड बेकार है। –

उत्तर

3

कुछ विचार: पुनरावृत्तियों की संख्या में वृद्धि के लिए परीक्षण चल रहा है और looped वेतन वृद्धि से बाहर अनुकूलन को रोकने के लिए एक पाश राशि जोड़ने के रूप में एडम ने सुझाव दिया करने के बाद, मैं और अधिक संसृत परिणाम प्राप्त:

  • अधिक पुनरावृत्तियों को चलाएं, कम से कम पर्याप्त कुछ सेकंड लगते हैं। आपके रन मिलीसेकंड लेते हैं, इसलिए एक आईओ इंटरप्ट आपके परिणामों को छूने के लिए पर्याप्त हो सकता है।
  • अंत में राशि को प्रिंट करें। कंपाइलर आपके लूप को आपके विचार से कहीं अधिक अनुकूलित करने के लिए पर्याप्त स्मार्ट हो सकता है, इसलिए आपका कोड आपके विचार से कम काम कर सकता है। यदि संकलक देखता है कि मान कभी नहीं पढ़ा जाता है, तो यह आपके लूप को पूरी तरह हटा सकता है।
  • एक लूप में पुनरावृत्ति करें, जैसा कि एक लूप का विरोध करता है जो फ़ंक्शन को कॉल करता है। जबकि कंपाइलर शायद आपके फ़ंक्शन कॉल को रेखांकित करेगा, शोर का एक और संभावित स्रोत पेश नहीं करना सबसे अच्छा है।
  • मुझे यकीन है कि आप इसे अगले करेंगे, लेकिन एक थ्रेडेड टेस्ट जोड़ें। दोनों के लिए भी यह कर सकते हैं; दौड़ के कारण आपको गैर-परमाणु चर में गलत राशि मिल जाएगी, लेकिन कम से कम आप प्रदर्शन के लिए भुगतान दंड देखेंगे जो आप स्थिरता के लिए भुगतान करते हैं।
+0

सरणी योग की छपाई के साथ-साथ पुनरावृत्तियों की संख्या में वृद्धि हुई ताकि यह कुछ सेकंड तक चल सके। अब मुझे और अधिक उचित परिणाम मिलते हैं। मैंने इन परिवर्तनों को दर्शाने के लिए पोस्ट को अपडेट किया है। अनेक अनेक धन्यवाद! – balajeerc

+0

मैं जल्द ही एक बहु थ्रेड परीक्षण जोड़ दूंगा। – balajeerc

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