Bu yeni cevap C ++ 11'in <chrono>
imkanını kullanır . Nasıl kullanılacağını gösteren başka cevaplar olsa da <chrono>
, bunların hiçbiri buradaki diğer cevapların birçoğunda bahsedilen tesis <chrono>
ile nasıl kullanılacağını göstermiyor RDTSC
. Bu yüzden nasıl kullanılacağını göstermek düşündüm RDTSC
ile <chrono>
. Ek olarak, saat üzerindeki test kodunu nasıl şablon haline getirebileceğinizi göstereceğim, böylece RDTSC
sisteminizin yerleşik saat tesisleri arasında hızlı bir şekilde geçiş yapabilirsiniz (muhtemelen clock()
, clock_gettime()
ve / veya QueryPerformanceCounter
.
Not o RDTSC
talimat x86 özeldir. QueryPerformanceCounter
yalnızca Windows'dur. Ve clock_gettime()
sadece POSIX. Aşağıda iki yeni saat tanıtıyorum: std::chrono::high_resolution_clock
ve std::chrono::system_clock
, eğer C ++ 11'i varsayabilirseniz, artık platformlar arası.
İlk olarak, Intel rdtsc
montaj talimatından C ++ 11 uyumlu bir saati nasıl oluşturacağınız aşağıda açıklanmıştır . Ben onu arayacağım 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
Bu saatin tek yaptığı CPU döngülerini saymak ve onu işaretsiz 64 bitlik bir tamsayıda saklamaktır. Derleyiciniz için assembly dili sözdizimini ayarlamanız gerekebilir. Veya derleyiciniz bunun yerine kullanabileceğiniz bir intrinsic sunabilir (örn. now() {return __rdtsc();}
).
Bir saat inşa etmek için ona gösterimi (depolama türü) vermelisiniz. Ayrıca, makineniz farklı güç modlarında saat hızını değiştirebilse de, derleme zamanı sabiti olması gereken saat periyodunu da sağlamalısınız. Ve bunlardan, saatinizin "yerel" zaman süresini ve zaman noktasını bu temeller açısından kolayca tanımlayabilirsiniz.
Tek yapmak istediğiniz saat tiklerinin sayısını çıkarmaksa, saat periyodu için hangi sayıyı verdiğiniz önemli değildir. Bu sabit, yalnızca saat tiklerinin sayısını nanosaniye gibi gerçek zamanlı birime dönüştürmek istiyorsanız devreye girer. Ve bu durumda, saat hızını ne kadar doğru sağlayabiliyorsanız, nanosaniyeye dönüştürme o kadar doğru olacaktır (milisaniye, her neyse).
Aşağıda nasıl kullanılacağını gösteren örnek kod bulunmaktadır x::clock
. Aslında, aynı sözdizimiyle birçok farklı saati nasıl kullanabileceğinizi göstermek istediğim için saatin kodunu şablon haline getirdim. Bu özel test, bir döngü altında zamanlamak istediğiniz şeyi çalıştırırken döngü ek yükünün ne olduğunu gösteriyor:
#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";
}
Bu kodun yaptığı ilk şey, sonuçları görüntülemek için "gerçek zamanlı" bir birim oluşturmaktır. Pikosaniyeleri seçtim, ancak integral veya kayan nokta tabanlı istediğiniz herhangi bir birimi seçebilirsiniz. Örnek olarak std::chrono::nanoseconds
kullanmış olabileceğim önceden yapılmış bir birim var.
Başka bir örnek olarak, yineleme başına ortalama saat döngüsü sayısını bir kayan nokta olarak yazdırmak istiyorum, bu nedenle, saatin onay işaretiyle aynı birimlere sahip ( Cycle
kodda çağrılır ) çifte dayalı başka bir süre oluşturuyorum .
Döngü, clock::now()
her iki taraftaki çağrılarla zamanlanır . Bu işlevden döndürülen türü adlandırmak isterseniz:
typename clock::time_point t0 = clock::now();
( x::clock
örnekte açıkça gösterildiği gibi ve sistem tarafından sağlanan saatler için de geçerlidir).
Kayan noktalı saat tikleri cinsinden bir süre elde etmek için, kişi yalnızca iki zaman noktasını çıkarır ve yineleme başına değeri elde etmek için bu süreyi yineleme sayısına bölün.
count()
Üye işlevini kullanarak istediğiniz süre içinde sayıyı alabilirsiniz . Bu, dahili gösterimi döndürür. Sonunda kullanmak std::chrono::duration_cast
süresini dönüştürmek Cycle
süresine picoseconds
ve bu out yazdırın.
Bu kodu kullanmak basittir:
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>();
}
Yukarıda, testi ev yapımı makinemizi kullanarak yapıyorum x::clock
ve bu sonuçları sistem tarafından sağlanan iki saati kullanarak karşılaştırıyorum: std::chrono::high_resolution_clock
ve std::chrono::system_clock
. Benim için bu çıktı:
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
Bu, bu saatlerin her birinin farklı bir tıklama periyoduna sahip olduğunu gösterir, çünkü yineleme başına tik sayısı her saat için büyük ölçüde farklıdır. Ancak bilinen bir zaman birimine dönüştürüldüğünde (örneğin pikosaniye), her saat için yaklaşık olarak aynı sonucu alıyorum (kilometreniz değişebilir).
Kodumun nasıl tamamen "sihirli dönüşüm sabitleri" içermediğine dikkat edin. Aslında, tüm örnekte yalnızca iki sihirli sayı vardır:
- Tanımlamak için makinemin saat hızı
x::clock
.
- Üzerinde test edilecek yineleme sayısı. Bu sayıyı değiştirmek sonuçlarınızın büyük ölçüde değişmesine neden oluyorsa, muhtemelen yineleme sayısını artırmanız veya test sırasında bilgisayarınızı rakip süreçlerden boşaltmanız gerekir.