हमारे पास एक साधारण मेमोरी थ्रूपुट बेंचमार्क है। यह सब स्मृति के एक बड़े ब्लॉक के लिए बार-बार memcpy है।एकल-थ्रेडेड मेमोरी थ्रूपुट के लिए ब्रॉडवेल-ई की तुलना में स्काइलेक इतना बेहतर क्यों है?
कुछ अलग मशीनों पर परिणाम (64-बिट के लिए संकलित) को देखते हुए, स्काइलेक मशीन ओएस (विन 10-64), प्रोसेसर की गति और रैम स्पीड (डीडीआर 4-2133) रखते हुए ब्रॉडवेल-ई की तुलना में काफी बेहतर होती है। वही। हम कुछ प्रतिशत अंक, पर बात नहीं कर रहे हैं बल्कि लगभग 2 के कारक हैं। स्काइलेक को दोहरी चैनल कॉन्फ़िगर किया गया है, और ब्रॉडवेल-ई के परिणाम दोहरी/ट्रिपल/क्वाड-चैनल के लिए भिन्न नहीं हैं।
कोई विचार यह क्यों हो रहा है? कोड इस प्रकार है कि VS2015 में रिलीज में संकलित किया गया है, और रिपोर्ट औसत समय में प्रत्येक memcpy पूरा करने के लिए:
64-बिट: Skylake के लिए 2.2ms Broadwell ई
32-बिट के लिए 4.5ms बनाम : ब्रॉडवेल-ई के लिए स्काइलेक बनाम 3.5 एमएमएस के लिए 2.2 एमएमएस।
हम एक ट्रैक्टर-चैनल Broadwell-ई से अधिक थ्रेड का उपयोग करके निर्माण पर अधिक से अधिक स्मृति throughput प्राप्त कर सकते हैं, और कहा कि अच्छा है, लेकिन देखने के लिए एकल पिरोया स्मृति पहुँच के लिए इस तरह के एक कठोर अंतर निराशा होती है। कोई विचार क्यों अंतर इतना स्पष्ट है?
हमने विभिन्न बेंचमार्किंग सॉफ़्टवेयर का भी उपयोग किया है, और वे यह सरल उदाहरण दिखाते हैं कि एकल-थ्रेडेड मेमोरी थ्रूपुट स्किलेक पर बेहतर तरीका है।
#include <memory>
#include <Windows.h>
#include <iostream>
//Prevent the memcpy from being optimized out of the for loop
_declspec(noinline) void MemoryCopy(void *destinationMemoryBlock, void *sourceMemoryBlock, size_t size)
{
memcpy(destinationMemoryBlock, sourceMemoryBlock, size);
}
int main()
{
const int SIZE_OF_BLOCKS = 25000000;
const int NUMBER_ITERATIONS = 100;
void* sourceMemoryBlock = malloc(SIZE_OF_BLOCKS);
void* destinationMemoryBlock = malloc(SIZE_OF_BLOCKS);
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
while (true)
{
LONGLONG total = 0;
LONGLONG max = 0;
LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
for (int i = 0; i < NUMBER_ITERATIONS; ++i)
{
QueryPerformanceCounter(&StartingTime);
MemoryCopy(destinationMemoryBlock, sourceMemoryBlock, SIZE_OF_BLOCKS);
QueryPerformanceCounter(&EndingTime);
ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
ElapsedMicroseconds.QuadPart *= 1000000;
ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
total += ElapsedMicroseconds.QuadPart;
max = max(ElapsedMicroseconds.QuadPart, max);
}
std::cout << "Average is " << total*1.0/NUMBER_ITERATIONS/1000.0 << "ms" << std::endl;
std::cout << "Max is " << max/1000.0 << "ms" << std::endl;
}
getchar();
}
MSVC के memcpy पुस्तकालय समारोह CPUID या कुछ भी पर आधारित एक रणनीति चुनाव करता है? जैसे AVX पाश बनाम 'rep movsb'? क्या आप सुनिश्चित करते हैं कि दोनों बफर कम से कम 64 बी-सभी परीक्षणों के लिए गठबंधन हैं? क्या आपने पेर्फ काउंटरों को यह देखने के लिए जांच की है कि क्या आपको कोई टीएलबी याद आ रही है, या सिर्फ एल 3 कैश याद आती है? (स्काइलेक समानांतर में दो टीएलबी चल सकता है)। क्या आपका ब्रॉडवेल-ई एक बहु-सॉकेट सिस्टम (NUMA) है? –
क्या आपने यह सुनिश्चित करने के लिए अपने ब्रॉडवेल सिस्टम पर BIOS की जांच की है कि इसमें प्रीफेचिंग अक्षम या कुछ भी नहीं है? क्या आप अन्य ब्रॉडवेल या हैसवेल डेस्कटॉप सिस्टम की तुलना करने में सक्षम थे? (जिस विशिष्ट ब्रॉडवेल मशीन पर आप परीक्षण कर रहे हैं उस पर कुछ अजीब बात करें)। 23.8MiB कॉपी करने के लिए –
2.2ms मिश्रित पढ़ने + लिखने के लिए, के बारे में 10.6GiB/s पढ़ने की और लिखने प्रत्येक है। इंटेल का कहना है [Skylake i5-6600] (http://ark.intel.com/products/88188) (और अन्य एसकेएल मॉडल DDR4-2133 का प्रयोग करके) एक सैद्धांतिक अधिकतम स्मृति बैंडविड्थ 34.1 जीबी/s (या 31.8 के GiB/है रों)। इसलिए यदि प्रत्येक भार और स्टोर एल 3 में चूक जाता है और उसे मुख्य स्मृति में जाना पड़ता है, तो यह सैद्धांतिक अधिकतम के लगभग 2/3s है। हालांकि, एक धागे के लिए यह सामान्य हो सकता है। –