คำตอบใหม่นี้ใช้<chrono>
สิ่งอำนวยความสะดวกของ C ++ 11 แม้ว่าจะมีคำตอบอื่น ๆ ที่แสดงวิธีการใช้งาน<chrono>
แต่ก็ไม่มีคำตอบใดแสดงวิธีใช้<chrono>
กับRDTSC
สิ่งอำนวยความสะดวกที่กล่าวถึงในคำตอบอื่น ๆ ที่นี่ ดังนั้นผมจึงคิดว่าฉันจะแสดงวิธีการใช้ด้วยRDTSC
<chrono>
นอกจากนี้ผมจะแสดงให้เห็นถึงวิธีการที่คุณสามารถ templatize รหัสการทดสอบบนนาฬิกาเพื่อให้คุณได้อย่างรวดเร็วสามารถสลับระหว่างRDTSC
และระบบของคุณในตัวสิ่งอำนวยความสะดวกนาฬิกา (ซึ่งมีแนวโน้มที่จะต้องอยู่บนพื้นฐานclock()
, และclock_gettime()
/ หรือQueryPerformanceCounter
โปรดทราบว่าRDTSC
คำสั่งเป็นแบบ x86 โดยเฉพาะ QueryPerformanceCounter
เป็น Windows เท่านั้น และclock_gettime()
เป็น POSIX เท่านั้น ด้านล่างนี้ฉันขอแนะนำนาฬิกาใหม่สองนาฬิกา: std::chrono::high_resolution_clock
และstd::chrono::system_clock
ซึ่งถ้าคุณสามารถสมมติว่า C ++ 11 เป็นแบบข้ามแพลตฟอร์ม
ขั้นแรกนี่คือวิธีสร้างนาฬิกาที่เข้ากันได้กับ C ++ 11 จากrdtsc
คำแนะนำการประกอบของ Intel ฉันจะเรียกมันว่าx::clock
:
#include <chrono>
namespace x
{
struct clock
{
typedef unsigned long long rep;
typedef std::ratio<1, 2'800'000'000> period; // My machine is 2.8 GHz
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<clock> time_point;
static const bool is_steady = true;
static time_point now() noexcept
{
unsigned lo, hi;
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return time_point(duration(static_cast<rep>(hi) << 32 | lo));
}
};
} // x
นาฬิกาทั้งหมดนี้นับรอบ CPU และเก็บไว้ในจำนวนเต็ม 64 บิตที่ไม่ได้ลงชื่อ คุณอาจต้องปรับแต่งไวยากรณ์ภาษาแอสเซมบลีสำหรับคอมไพเลอร์ของคุณ หรือคอมไพเลอร์ของคุณอาจเสนออินทรินซิคที่คุณสามารถใช้แทนได้ (เช่นnow() {return __rdtsc();}
)
ในการสร้างนาฬิกาคุณต้องให้มันเป็นตัวแทน (ประเภทการจัดเก็บ) คุณต้องระบุช่วงเวลานาฬิกาด้วยซึ่งต้องเป็นค่าคงที่ของเวลาคอมไพล์แม้ว่าเครื่องของคุณอาจเปลี่ยนความเร็วสัญญาณนาฬิกาในโหมดพลังงานที่แตกต่างกัน และจากสิ่งเหล่านี้คุณสามารถกำหนดระยะเวลาและจุดเวลาแบบ "ดั้งเดิม" ของนาฬิกาได้อย่างง่ายดายในแง่ของปัจจัยพื้นฐานเหล่านี้
หากสิ่งที่คุณต้องการทำคือแสดงจำนวนขีดของนาฬิกาไม่สำคัญว่าคุณจะให้ตัวเลขใดสำหรับช่วงเวลานาฬิกา ค่าคงที่นี้จะเข้ามาเล่นก็ต่อเมื่อคุณต้องการแปลงจำนวนเห็บนาฬิกาเป็นหน่วยเรียลไทม์เช่นนาโนวินาที และในกรณีนี้ยิ่งคุณสามารถระบุความเร็วสัญญาณนาฬิกาได้แม่นยำมากเท่าไหร่การแปลงเป็นนาโนวินาทีก็จะแม่นยำมากขึ้น (มิลลิวินาทีก็ตาม)
x::clock
ด้านล่างเป็นโค้ดตัวอย่างที่แสดงให้เห็นถึงวิธีการใช้งาน อันที่จริงฉันได้เทมเพลตโค้ดบนนาฬิกาแล้วเพราะฉันต้องการแสดงให้เห็นว่าคุณสามารถใช้นาฬิกาหลายแบบที่มีไวยากรณ์เดียวกันได้อย่างไร การทดสอบเฉพาะนี้แสดงให้เห็นว่าค่าใช้จ่ายในการวนซ้ำคืออะไรเมื่อเรียกใช้สิ่งที่คุณต้องการเวลาภายใต้ลูป:
#include <iostream>
template <class clock>
void
test_empty_loop()
{
// Define real time units
typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
// or:
// typedef std::chrono::nanoseconds nanoseconds;
// Define double-based unit of clock tick
typedef std::chrono::duration<double, typename clock::period> Cycle;
using std::chrono::duration_cast;
const int N = 100000000;
// Do it
auto t0 = clock::now();
for (int j = 0; j < N; ++j)
asm volatile("");
auto t1 = clock::now();
// Get the clock ticks per iteration
auto ticks_per_iter = Cycle(t1-t0)/N;
std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
// Convert to real time units
std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
<< "ps per iteration\n";
}
สิ่งแรกที่โค้ดนี้ทำคือสร้างหน่วย "เรียลไทม์" เพื่อแสดงผลลัพธ์ฉันได้เลือก picoseconds แล้ว แต่คุณสามารถเลือกหน่วยใดก็ได้ที่คุณต้องการไม่ว่าจะเป็นอินทิกรัลหรือทศนิยมตาม ตัวอย่างเช่นมีstd::chrono::nanoseconds
ยูนิตสำเร็จรูปที่ฉันสามารถใช้ได้
เป็นอีกตัวอย่างหนึ่งที่ฉันต้องการพิมพ์จำนวนรอบนาฬิกาโดยเฉลี่ยต่อการวนซ้ำเป็นจุดลอยตัวดังนั้นฉันจึงสร้างช่วงเวลาอื่นขึ้นอยู่กับคู่ซึ่งมีหน่วยเดียวกับที่ขีดของนาฬิกาทำ (เรียกCycle
ในรหัส)
ลูปจะหมดเวลาโดยมีการโทรไปclock::now()
ที่ด้านใดด้านหนึ่ง หากคุณต้องการตั้งชื่อประเภทที่ส่งกลับจากฟังก์ชันนี้คือ:
typename clock::time_point t0 = clock::now();
(ดังที่แสดงไว้อย่างชัดเจนในx::clock
ตัวอย่างและยังเป็นจริงของนาฬิกาที่ระบบให้มาด้วย)
ในการหาระยะเวลาในรูปของนาฬิกาจุดลอยตัวให้ทำเครื่องหมายเพียงหนึ่งลบสองจุดเวลาและเพื่อให้ได้ค่าการวนซ้ำให้หารระยะเวลานั้นด้วยจำนวนการวนซ้ำ
คุณสามารถรับการนับในช่วงเวลาใดก็ได้โดยใช้count()
ฟังก์ชันสมาชิก สิ่งนี้ส่งกลับการแสดงภายใน ในที่สุดฉันก็ใช้std::chrono::duration_cast
เพื่อแปลงระยะเวลาCycle
เป็นระยะเวลาpicoseconds
และพิมพ์ออกมา
ในการใช้รหัสนี้ทำได้ง่าย:
int main()
{
std::cout << "\nUsing rdtsc:\n";
test_empty_loop<x::clock>();
std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
test_empty_loop<std::chrono::high_resolution_clock>();
std::cout << "\nUsing std::chrono::system_clock:\n";
test_empty_loop<std::chrono::system_clock>();
}
ดังกล่าวข้างต้นผมออกกำลังกายโดยใช้การทดสอบของเราทำที่บ้านx::clock
และเปรียบเทียบผลผู้ที่มีการใช้สองของนาฬิการะบบจัด: และstd::chrono::high_resolution_clock
std::chrono::system_clock
สำหรับฉันสิ่งนี้พิมพ์ออกมา:
Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration
Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration
Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration
สิ่งนี้แสดงให้เห็นว่านาฬิกาแต่ละเรือนเหล่านี้มีช่วงเวลาเห็บที่แตกต่างกันเนื่องจากเห็บต่อการวนซ้ำจะแตกต่างกันอย่างมากสำหรับนาฬิกาแต่ละเรือน อย่างไรก็ตามเมื่อแปลงเป็นหน่วยเวลาที่ทราบ (เช่น picoseconds) ฉันจะได้ผลลัพธ์ที่เหมือนกันโดยประมาณสำหรับนาฬิกาแต่ละเรือน (ระยะทางของคุณอาจแตกต่างกันไป)
สังเกตว่ารหัสของฉันไม่มี "ค่าคงที่การแปลงเวทมนตร์" โดยสิ้นเชิง อันที่จริงมีตัวเลขวิเศษเพียงสองตัวในตัวอย่างทั้งหมด:
x::clock
ความเร็วนาฬิกาของเครื่องของฉันเพื่อกำหนด
- จำนวนการทำซ้ำที่จะทดสอบ หากการเปลี่ยนตัวเลขนี้ทำให้ผลลัพธ์ของคุณแตกต่างกันไปมากคุณควรเพิ่มจำนวนการทำซ้ำให้สูงขึ้นหรือทำให้คอมพิวเตอร์ของคุณว่างเปล่าจากกระบวนการแข่งขันขณะทดสอบ