โซลูชันที่สมบูรณ์แบบสำหรับการตั้งเวลาเธรดซึ่งควรให้เวลาเท่ากันทุกครั้งในการทดสอบแต่ละครั้งคือการคอมไพล์โปรแกรมของคุณให้เป็นระบบปฏิบัติการที่เป็นอิสระและบูตเครื่องคอมพิวเตอร์ของคุณเพื่อรันโปรแกรมในสภาพแวดล้อมที่ไม่มีระบบปฏิบัติการ อย่างไรก็ตามนี่เป็นสิ่งที่ทำไม่ได้เป็นส่วนใหญ่และจะเป็นเรื่องยากที่ดีที่สุด
สิ่งทดแทนที่ดีในการใช้ OS-free คือการตั้งค่าความสัมพันธ์ของเธรดปัจจุบันเป็น 1 คอร์และลำดับความสำคัญสูงสุด ทางเลือกนี้ควรให้ผลลัพธ์ที่สม่ำเสมอเพียงพอ
นอกจากนี้คุณควรปิดการปรับให้เหมาะสมซึ่งจะรบกวนการดีบักซึ่งสำหรับ g ++ หรือ gcc หมายถึงการเพิ่ม-Og
ลงในบรรทัดคำสั่งเพื่อป้องกันไม่ให้โค้ดที่ทดสอบถูกปรับให้เหมาะสม -O0
ธงไม่ควรใช้เพราะมันแนะนำค่าใช้จ่ายที่ไม่จำเป็นเป็นพิเศษซึ่งจะถูกรวมอยู่ในผลเวลาจึงบิดเบือนความเร็วหมดเวลาของรหัส
ในทางตรงกันข้ามทั้งที่สมมติว่าคุณใช้-Ofast
(หรืออย่างน้อยที่สุด-O3
) ในการสร้างการผลิตขั้นสุดท้ายและเพิกเฉยต่อปัญหาของการกำจัดโค้ดที่ "ตาย" -Og
ทำการเพิ่มประสิทธิภาพน้อยมากเมื่อเทียบกับ-Ofast
; ดังนั้นจึง-Og
สามารถบิดเบือนความจริงความเร็วจริงของรหัสในผลิตภัณฑ์ขั้นสุดท้าย
นอกจากนี้การทดสอบความเร็วทั้งหมด (ในบางส่วน) ให้การเท็จ: ในผลิตภัณฑ์การผลิตขั้นสุดท้ายที่รวบรวมด้วย-Ofast
แต่ละส่วนย่อย / ส่วน / ฟังก์ชันของโค้ดจะไม่แยกออกจากกัน แต่แต่ละส่วนของโค้ดจะไหลไปสู่ส่วนถัดไปอย่างต่อเนื่องดังนั้นจึงทำให้คอมไพเลอร์สามารถรวมผสานและเพิ่มประสิทธิภาพโค้ดร่วมกันได้จากทุกที่
ในขณะเดียวกันหากคุณกำลังเปรียบเทียบข้อมูลโค้ดที่ใช้งานหนักrealloc()
ข้อมูลโค้ดอาจทำงานช้าลงในผลิตภัณฑ์ที่ใช้งานจริงที่มีการกระจายตัวของหน่วยความจำสูงเพียงพอ ดังนั้นนิพจน์ "ทั้งหมดมากกว่าผลรวมของชิ้นส่วน" จึงนำไปใช้กับสถานการณ์นี้เนื่องจากโค้ดในบิลด์การผลิตขั้นสุดท้ายอาจทำงานได้เร็วหรือช้ากว่าข้อมูลโค้ดแต่ละรายการที่คุณกำลังทดสอบความเร็วอย่างเห็นได้ชัด
วิธีแก้ปัญหาบางส่วนที่อาจลดความไม่ลงรอยกันกำลังใช้-Ofast
สำหรับการทดสอบความเร็วด้วยการเพิ่มasm volatile("" :: "r"(var))
ตัวแปรที่เกี่ยวข้องในการทดสอบเพื่อป้องกันการกำจัดรหัสตาย / ลูป
นี่คือตัวอย่างวิธีการเปรียบเทียบฟังก์ชันรากที่สองบนคอมพิวเตอร์ Windows
// set USE_ASM_TO_PREVENT_ELIMINATION to 0 to prevent `asm volatile("" :: "r"(var))`
// set USE_ASM_TO_PREVENT_ELIMINATION to 1 to enforce `asm volatile("" :: "r"(var))`
#define USE_ASM_TO_PREVENT_ELIMINATION 1
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <chrono>
#include <cmath>
#include <windows.h>
#include <intrin.h>
#pragma intrinsic(__rdtsc)
#include <cstdint>
class Timer {
public:
Timer() : beg_(clock_::now()) {}
void reset() { beg_ = clock_::now(); }
double elapsed() const {
return std::chrono::duration_cast<second_>
(clock_::now() - beg_).count(); }
private:
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> beg_;
};
unsigned int guess_sqrt32(register unsigned int n) {
register unsigned int g = 0x8000;
if(g*g > n) {
g ^= 0x8000;
}
g |= 0x4000;
if(g*g > n) {
g ^= 0x4000;
}
g |= 0x2000;
if(g*g > n) {
g ^= 0x2000;
}
g |= 0x1000;
if(g*g > n) {
g ^= 0x1000;
}
g |= 0x0800;
if(g*g > n) {
g ^= 0x0800;
}
g |= 0x0400;
if(g*g > n) {
g ^= 0x0400;
}
g |= 0x0200;
if(g*g > n) {
g ^= 0x0200;
}
g |= 0x0100;
if(g*g > n) {
g ^= 0x0100;
}
g |= 0x0080;
if(g*g > n) {
g ^= 0x0080;
}
g |= 0x0040;
if(g*g > n) {
g ^= 0x0040;
}
g |= 0x0020;
if(g*g > n) {
g ^= 0x0020;
}
g |= 0x0010;
if(g*g > n) {
g ^= 0x0010;
}
g |= 0x0008;
if(g*g > n) {
g ^= 0x0008;
}
g |= 0x0004;
if(g*g > n) {
g ^= 0x0004;
}
g |= 0x0002;
if(g*g > n) {
g ^= 0x0002;
}
g |= 0x0001;
if(g*g > n) {
g ^= 0x0001;
}
return g;
}
unsigned int empty_function( unsigned int _input ) {
return _input;
}
unsigned long long empty_ticks=0;
double empty_seconds=0;
Timer my_time;
template<unsigned int benchmark_repetitions>
void benchmark( char* function_name, auto (*function_to_do)( auto ) ) {
register unsigned int i=benchmark_repetitions;
register unsigned long long start=0;
my_time.reset();
start=__rdtsc();
while ( i-- ) {
auto result = (*function_to_do)( i << 7 );
#if USE_ASM_TO_PREVENT_ELIMINATION == 1
asm volatile("" :: "r"(
// There is no data type in C++ that is smaller than a char, so it will
// not throw a segmentation fault error to reinterpret any arbitrary
// data type as a char. Although, the compiler might not like it.
result
));
#endif
}
if ( function_name == nullptr ) {
empty_ticks = (__rdtsc()-start);
empty_seconds = my_time.elapsed();
std::cout<< "Empty:\n" << empty_ticks
<< " ticks\n" << benchmark_repetitions << " repetitions\n"
<< std::setprecision(15) << empty_seconds
<< " seconds\n\n";
} else {
std::cout<< function_name<<":\n" << (__rdtsc()-start-empty_ticks)
<< " ticks\n" << benchmark_repetitions << " repetitions\n"
<< std::setprecision(15) << (my_time.elapsed()-empty_seconds)
<< " seconds\n\n";
}
}
int main( void ) {
void* Cur_Thread= GetCurrentThread();
void* Cur_Process= GetCurrentProcess();
unsigned long long Current_Affinity;
unsigned long long System_Affinity;
unsigned long long furthest_affinity;
unsigned long long nearest_affinity;
if( ! SetThreadPriority(Cur_Thread,THREAD_PRIORITY_TIME_CRITICAL) ) {
SetThreadPriority( Cur_Thread, THREAD_PRIORITY_HIGHEST );
}
if( ! SetPriorityClass(Cur_Process,REALTIME_PRIORITY_CLASS) ) {
SetPriorityClass( Cur_Process, HIGH_PRIORITY_CLASS );
}
GetProcessAffinityMask( Cur_Process, &Current_Affinity, &System_Affinity );
furthest_affinity = 0x8000000000000000ULL>>__builtin_clzll(Current_Affinity);
nearest_affinity = 0x0000000000000001ULL<<__builtin_ctzll(Current_Affinity);
SetProcessAffinityMask( Cur_Process, furthest_affinity );
SetThreadAffinityMask( Cur_Thread, furthest_affinity );
const int repetitions=524288;
benchmark<repetitions>( nullptr, empty_function );
benchmark<repetitions>( "Standard Square Root", standard_sqrt );
benchmark<repetitions>( "Original Guess Square Root", original_guess_sqrt32 );
benchmark<repetitions>( "New Guess Square Root", new_guess_sqrt32 );
SetThreadPriority( Cur_Thread, THREAD_PRIORITY_IDLE );
SetPriorityClass( Cur_Process, IDLE_PRIORITY_CLASS );
SetProcessAffinityMask( Cur_Process, nearest_affinity );
SetThreadAffinityMask( Cur_Thread, nearest_affinity );
for (;;) { getchar(); }
return 0;
}
นอกจากนี้ให้เครดิตกับ Mike Jarvis สำหรับตัวจับเวลาของเขา
โปรดทราบ (สิ่งนี้สำคัญมาก) ว่าหากคุณกำลังจะเรียกใช้ข้อมูลโค้ดที่ใหญ่ขึ้นคุณจะต้องลดจำนวนการทำซ้ำลงเพื่อป้องกันไม่ให้คอมพิวเตอร์ของคุณค้าง