2015-09-21 31 views
5

Son zamanlarda yapılan bir tartışma, atomik artışın normal tamsayı artışına kıyasla ne kadar pahalı olduğunu merak etmemi sağladı. Atomik artışların düzenli tamsayı artışlarla karşılaştırıldığında ne kadar yavaş olduğunu ölçmek

Bazı denemek için kod ve kriter bu yazmış:

#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; 
} 

ben yaşıyorum sorun bu program her vadede oldukça farklı sonuçlar üretir olmasıdır:

[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 % 

Bu inanmamı neden olur Kıyaslama kodunda bir hata olabilir. Eksik olduğum bir hata var mı? Kıyaslama için std :: chrono kullanımım yanlış mı? Ya da OS'de atomik işlemlerle ilgili sinyal işlemenin tepesindeki yükün farkı nedir?

Neyi yanlış yapıyorum?

test yatağı:

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 

DÜZENLEME: -O3 optimizasyonu ile derleme sonra test sürüşü çıkışını güncellendi.

DÜZENLEME:

[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

Kodunuzu nasıl derlediniz? Hangi optimizasyon bayrakları ile? Hangi derleyici sürümü? Ve her koşuyu birkaç kez tekrar etmelisiniz! –

+0

@BasileStarynkevitch Bunu işaretlediğiniz için teşekkürler. Derleyicinin bilgileriyle güncellemeyi güncellemeyi yaptım. Birkaç kez çalıştırmak için, run_benchmark'ın birkaç kez çalıştırıldığını unutmayın (bir komut satırı argümanı kullanarak belirtilir). – balajeerc

+1

Derleme komutunuzda en azından '-O1' veya' -O2' (veya hatta '-O3') olduğunu unutmuşsunuzdur. Optimizasyonlar olmadan derlenen kıyaslama kodu işe yaramaz. –

cevap

3

Bazı düşünceler: Adam tarafından önerildiği gibi tekrarlamalar sayısının artması test yapmaktan ve ilmekli artım dışına optimizasyonu önlemek için bir döngü toplamı ekledikten sonra, ben daha yakınsak sonuçlar elde:

  • Daha fazla yineleme yapın, en azından birkaç saniye sürün. Çalışmalarınız milisaniye alıyor, bu yüzden sonuçlarınızı çarpmak için IO kesintisi yeterli olabilir.
  • Sonunda toplamı yazdırın. Derleyici, döngülerinizi düşündüğünüzden çok daha fazla optimize edebilecek kadar akıllı olabilir, bu nedenle kodunuz düşündüğünüzden daha az iş yapıyor olabilir. Derleyici değeri görürse, döngülerinizi tamamen silebilir.
  • Yinelemeyi, bir işlevi çağıran döngüye karşılık, tek bir döngüde yapın. Derleyici muhtemelen fonksiyon çağrınızı sıraya koyarken, başka bir potansiyel gürültü kaynağını tanıtmamak en iyisidir.
  • Bundan sonra yapacağınızdan eminim, ancak dişli bir sınama ekleyin. Her ikisi için de yapabilir; ırklar nedeniyle atomik olmayan değişkende yanlış bir miktar alırsınız, ancak en azından tutarlılık için ödediğiniz performans cezasını görürsünüz.
+0

Dizi toplamının yazdırılması ve iterasyonların sayısının artırılmasıyla birkaç saniye sürdü. Şimdi daha makul sonuçlar elde ediyorum. Bu değişiklikleri yansıtacak olan gönderiyi güncelledim. Teşekkürler bir ton! – balajeerc

+0

Yakında çok iş parçacıklı bir test ekleyeceğim. – balajeerc