2017-03-02 14 views
12

निम्न उदाहरण कोड आकार N का एक मैट्रिक्स उत्पन्न करता है, और इसे SAMPLES कई बार स्थानांतरित करता है। जब N = 512 पारदर्शिता संचालन का औसत निष्पादन समय 2144 μs (coliru link) है। फर्स्ट लुक पर कुछ भी नहीं विशेष है, है ना? ...इन मैट्रिक्स ट्रांसपोजिशन समय इतने प्रति-सहज क्यों हैं?

ठीक है, यहाँ

  • N = 5131451 μs
  • N = 519600 μs
  • N = 530486 μs
  • N = 540 के लिए परिणाम हैं → 492 μs (आखिरकार! सिद्धांत काम करना शुरू कर देता है :)।

तो अभ्यास में ये सरल गणना सिद्धांत से अलग क्यों हैं? क्या यह व्यवहार सीपीयू कैश समेकन या कैश मिस से संबंधित है? यदि ऐसा है तो कृपया समझाओ।

#include <algorithm> 
#include <iostream> 
#include <chrono> 

constexpr int N  = 512; // Why is 512 specifically slower (as of 2016) 
constexpr int SAMPLES = 1000; 
using us = std::chrono::microseconds; 

int A[N][N]; 

void transpose() 
{ 
    for (int i = 0 ; i < N ; i++) 
    for (int j = 0 ; j < i ; j++) 
     std::swap(A[i][j], A[j][i]); 
} 

int main() 
{ 
    // initialize matrix 
    for (int i = 0 ; i < N ; i++) 
    for (int j = 0 ; j < N ; j++) 
     A[i][j] = i+j; 

    auto t1 = std::chrono::system_clock::now(); 
    for (int i = 0 ; i < SAMPLES ; i++) 
     transpose(); 
    auto t2 = std::chrono::system_clock::now(); 

    std::cout << "Average for size " << N << ": " << std::chrono::duration_cast<us>(t2 - t1).count()/SAMPLES << " (us)"; 
} 
+0

आपने स्निपेट कितनी बार चलाया? रनटाइम रन से चलने में काफी भिन्न हो सकते हैं, इस पर निर्भर करता है कि आपका सिस्टम कितनी अन्य चीजें कर सकता है। क्या ये लगभग 10 या 20 रनों का औसत समय है, या केवल एक रन से समय? – JGroven

+1

शायद 512 एक जादू का आकार है जो कैश को बहुत ही फिट करता है ताकि आपको बहुत सारे कैश मिस मिल जाए। अन्य आकार बेहतर फिट होते हैं ताकि आपको कम याद आती है। – NathanOliver

+0

गलत तरीके से @NathanOliver - 512 513 से बहुत धीमा * धीमा * –

उत्तर

6

यह कारण कैश मिस है। आप मिस की मात्रा देखने के लिए valgrind --tool=cachegrind का उपयोग कर सकते हैं।

Average for size 512: 13052 (us)==21803== 
==21803== I refs:  1,054,721,935 
==21803== I1 misses:   1,640 
==21803== LLi misses:   1,550 
==21803== I1 miss rate:   0.00% 
==21803== LLi miss rate:   0.00% 
==21803== 
==21803== D refs:  524,278,606 (262,185,156 rd + 262,093,450 wr) 
==21803== D1 misses:  139,388,226 (139,369,492 rd +  18,734 wr) 
==21803== LLd misses:   25,828 (  7,959 rd +  17,869 wr) 
==21803== D1 miss rate:   26.6% (  53.2%  +   0.0% ) 
==21803== LLd miss rate:   0.0% (  0.0%  +   0.0% ) 
==21803== 
==21803== LL refs:   139,389,866 (139,371,132 rd +  18,734 wr) 
==21803== LL misses:   27,378 (  9,509 rd +  17,869 wr) 
==21803== LL miss rate:   0.0% (  0.0%  +   0.0% ) 

, वहीं N=530 का उपयोग कर आप निम्नलिखित उत्पादन मिल गया:

Average for size 530: 13264 (us)==22783== 
==22783== I refs:  1,129,929,859 
==22783== I1 misses:   1,640 
==22783== LLi misses:   1,550 
==22783== I1 miss rate:   0.00% 
==22783== LLi miss rate:   0.00% 
==22783== 
==22783== D refs:  561,773,362 (280,923,156 rd + 280,850,206 wr) 
==22783== D1 misses:  32,899,398 (32,879,492 rd +  19,906 wr) 
==22783== LLd misses:   26,999 (  7,958 rd +  19,041 wr) 
==22783== D1 miss rate:   5.9% (  11.7%  +   0.0% ) 
==22783== LLd miss rate:   0.0% (  0.0%  +   0.0% ) 
==22783== 
==22783== LL refs:   32,901,038 (32,881,132 rd +  19,906 wr) 
==22783== LL misses:   28,549 (  9,508 rd +  19,041 wr) 
==22783== LL miss rate:   0.0% (  0.0%  +   0.0% ) 

आप देख सकते हैं, डी 1 512 में याद करते हैं चारों ओर 3.5 गुना 530 में से बड़ा है N = 512 का उपयोग करते हुए आप निम्नलिखित उत्पादन मिला

+0

तो एक समाधान "कैश फ्रेंडली" मैट्रिक्स के लिए अगले बड़े आकार के मैट्रिक्स का उपयोग करना होगा, कुछ अप्रयुक्त कॉलम (और संभावित रूप से पंक्तियां) छोड़कर, लेकिन यह तेज़ होना चाहिए। – rcgldr

+0

यूप, और मिस की उच्च दर इसलिए है क्योंकि सहयोगीता कुछ स्मृति उपयोग पैटर्न के तहत कुल कैश का केवल एक अंश उपयोग करने की अनुमति देती है। –

+1

@ आरसीजील्डर: मेमोरी एक्सेस ऑर्डर बदलने के लिए एक बेहतर समाधान होगा। एकल तत्वों को स्वैप करने के बजाय, 4x4 ब्लॉक को स्वैप करें, इस तरह आप स्वैप के दोनों सिरों के लिए उसी कैश लाइन में सभी तत्वों तक पहुंच सकते हैं। –

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