Một giải pháp hoàn chỉnh cho việc lập lịch luồng, sẽ mang lại số lần chính xác như nhau cho mỗi lần kiểm tra, là biên dịch chương trình của bạn để độc lập với hệ điều hành và khởi động máy tính của bạn để chạy chương trình trong môi trường không có hệ điều hành. Tuy nhiên, điều này phần lớn là không thực tế và sẽ rất khó khăn.
Một sự thay thế tốt cho việc không sử dụng hệ điều hành chỉ là đặt mối quan hệ của luồng hiện tại thành 1 lõi và mức độ ưu tiên là cao nhất. Sự thay thế này sẽ cung cấp kết quả đủ nhất quán.
Ngoài ra, bạn nên tắt tính năng tối ưu hóa sẽ cản trở việc gỡ lỗi, điều này đối với g ++ hoặc gcc có nghĩa là thêm -Og
vào dòng lệnh , để ngăn mã đang được kiểm tra không được tối ưu hóa. Không -O0
nên sử dụng cờ vì nó tạo thêm chi phí không cần thiết sẽ được bao gồm trong kết quả định thời, do đó làm sai lệch tốc độ định thời của mã.
Ngược lại, cả giả định rằng bạn sử dụng -Ofast
(hoặc, ít nhất, -O3
) trên bản dựng sản xuất cuối cùng và bỏ qua vấn đề loại bỏ mã "chết", -Og
thực hiện rất ít tối ưu hóa so với -Ofast
; do đó -Og
có thể mô tả sai tốc độ thực của mã trong sản phẩm cuối cùng.
Hơn nữa, tất cả các bài kiểm tra tốc độ (ở một mức độ nào đó) đều sai sót: trong sản phẩm sản xuất cuối cùng được biên dịch -Ofast
, mỗi đoạn mã / phần / chức năng của mã không bị cô lập; thay vào đó, mỗi đoạn mã liên tục chuyển sang đoạn mã tiếp theo, do đó cho phép trình biên dịch có thể tham gia, hợp nhất và tối ưu hóa các đoạn mã với nhau từ khắp nơi.
Đồng thời, nếu bạn đang đo điểm chuẩn cho một đoạn mã sử dụng nhiều realloc()
, thì đoạn mã đó có thể chạy chậm hơn trong một sản phẩm sản xuất có phân mảnh bộ nhớ đủ cao. Do đó, cụm từ "toàn bộ nhiều hơn tổng các phần của nó" áp dụng cho trường hợp này vì mã trong bản dựng sản xuất cuối cùng có thể chạy nhanh hơn hoặc chậm hơn đáng kể so với đoạn mã riêng lẻ mà bạn đang kiểm tra tốc độ.
Một giải pháp từng phần có thể giảm bớt sự không phù hợp đang sử dụng -Ofast
để kiểm tra tốc độ VỚI việc bổ sung asm volatile("" :: "r"(var))
các biến liên quan đến kiểm tra để ngăn chặn việc loại bỏ mã chết / vòng lặp.
Dưới đây là một ví dụ về cách chuẩn các hàm căn bậc hai trên máy tính 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;
}
Ngoài ra, ghi công cho Mike Jarvis cho Bộ hẹn giờ của anh ấy.
Xin lưu ý (điều này rất quan trọng) rằng nếu bạn định chạy các đoạn mã lớn hơn, thì bạn thực sự phải giảm số lần lặp lại để ngăn máy tính của bạn không bị đóng băng.