有一些很好的方法來知道函數如何在C中執行嗎?我想,例如將我自己的函數與庫函數進行比較。如何測試C函數的性能?
回答
在您輸入功能之前,關閉系統時間。 從功能返回後,請關閉系統時間。 減去差異並比較兩個實現。
商店時間戳才進入功能
商店時間戳後退出功能
比較時間戳
確保使用SI因爲時間分辨率可能會改變你的結果。對於短期功能尤其如此。使用高分辨率定時器(大多數平臺都可以使用微秒分辨率)。
運行(他們)數百萬次(每個)並測量所花費的時間。
完成速度更快的是更好的性能。
gprof可以幫助:)
這裏的gprof的結果,當我跑我的10秒的程序(函數名稱變更)
Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 60.29 8.68 8.68 115471546 0.00 0.00 workalot 39.22 14.32 5.64 46 122.70 311.32 work_b 0.49 14.39 0.07 inlined 0.07 14.40 0.01 46 0.22 0.22 work_c 0.00 14.40 0.00 460 0.00 0.00 find_minimum 0.00 14.40 0.00 460 0.00 0.00 feedback 0.00 14.40 0.00 46 0.00 0.00 work_a
我同意這*一般*。但是,由於緩存問題,第一次迭代可能比其他迭代慢得多。如果例行程序通常只進行一次,而不是緊密循環,這會給你一張歪斜的圖片。 OTOH,如果例行程序只進行一次,則不應該浪費寶貴的時間嘗試分析或優化它。 – 2009-11-06 15:21:16
感謝pmg,我會檢查gprof。我注意到我甚至默認安裝了它。 – Fred 2009-11-06 15:25:46
T.E.D.有幾個優點。操作系統的CPU緩存和緩存將大大提高除第一次迭代之外的所有功能的性能,如果函數單獨運行或在其他功能足夠豐富以替代CPU緩存的內容。但這可能是目前最好的簡單分析技術,並且仍會給你一個良好/可接受/糟糕的性能數據。 – Dogmang 2009-11-06 15:48:02
需要高分辨率定時器。
在Linux上,gettimeofday()
是一個不錯的選擇,它給你微秒的分辨率。在Windows上,QueryPerformanceCounter()
是典型的。確保你多次運行你的功能,以獲得穩定的讀數。
快速樣品,對於Linux:
struct timeval t0, t1;
unsigned int i;
gettimeofday(&t0, NULL);
for(i = 0; i < 100000; i++)
function_to_measure();
gettimeofday(&t1, NULL);
printf("Did %u calls in %.2g seconds\n", i, t1.tv_sec - t0.tv_sec + 1E-6 * (t1.tv_usec - t0.tv_usec);
你當然會調整數(100,000)來匹配功能的性能。如果函數真的需要一段時間才能運行,那麼最好是循環和/或函數調用開銷可能占主導地位。
結帳HighResTimer高性能計時器。
您可能會發現存儲前/後的時間不夠準確,並且可能會導致0,除非您有更長的運行功能。
退房RDTSC但最好是像下面這樣做。
0 - 呼叫系統的睡眠或產量的功能,這樣,當它返回時,你有一個新的時間片
1 - RDTSC
2 - 呼叫你的函數
3 - RDTSC
如果你的功能是長期運行的,你必須使用像gprof這樣的分析工具(這非常容易使用)&英特爾的VTune應用程序(我很久沒用過了)。在看到Art的回答後,我將自己的思想從gprof改變爲Callgrind。過去我只使用Valgrind的Memcheck工具,這是一個宏偉的工具。我之前沒有使用過Callgrind,但我確信它比gprof更好...
有趣的是,我不知道這裏有詳細的說明。也許不得不嘗試這個以及看它是如何工作的。 – Fred 2009-11-06 15:57:44
開放源代碼Callgrind profiler(用於Linux)是衡量性能的非常棒的方法。再加上KCacheGrind,你可以很好地看到你的時間花在哪裏。
Callgrind是Valgrind的一部分。
- 藝術
正如你可以使用標準的函數時(),它返回自紀元秒當前數量的簡單和便攜的方式。
#include <time.h>
time_t starttime, endtime;
starttime = time(NULL);
for (i = 0; i < 1000000; i++)
{
testfunc();
}
endtime = time(NULL);
printf("Time in seconds is %d\n", (int)(endtime-starttime));
根據您的需要調整迭代次數。如果一個函數調用需要5秒鐘,那麼你需要一杯laaarge咖啡來進行1000000次迭代......當差異小於1秒時,即使是大量的,你也應該問問你自己是否重要,如果是的話,2 )檢查你最喜歡的編譯器是否已經具有內置分析功能。
Fred,我注意到你在評論中說你在OS X上。在OS X上獲得非常精確的小規模函數時間的最好方法是使用mach_absoute_time()
函數。
#include <mach/mach_time.h>
#include <stdint.h>
int loopCount;
uint64_t startTime = mach_absolute_time();
for (loopCount = 0; loopCount < iterations; ++loopCount) {
functionBeingTimed();
}
uint64_t endTime = mach_absolute_time();
double averageTime = (double)(endTime-startTime)/iterations;
這讓您在iterations
調用該函數的平均時間:您可以按如下方式使用它。這可能會受到系統外部進程以外的影響的影響。因此,你可能反而想利用最快的時間:
#include <mach/mach_time.h>
#include <stdint.h>
int loopCount;
double bestTime = __builtin_inf();
for (loopCount = 0; loopCount < iterations; ++loopCount) {
uint64_t startTime = mach_absolute_time();
functionBeingTimed();
uint64_t endTime = mach_absolute_time();
double bestTime = __builtin_fmin(bestTime, (double)(endTime-startTime));
}
這可能有其自身的問題,特別是如果被計時的功能是非常非常快的。你需要考慮你真正想要測量的是什麼,並選擇一種科學合理的方法(好的實驗設計是硬)。我經常使用這兩種方法之間的混合來作爲衡量一項新任務的第一次嘗試(對於許多呼叫來說最小的平均值)。
還要注意,在上面的代碼樣本中,時間以「時間單位」表示。如果你只是想比較算法,這通常很好。出於其他一些目的,您可能希望將它們轉換爲納秒或週期。要做到這一點,你可以使用以下功能:
#include <mach/mach_time.h>
#include <sys/sysctl.h>
#include <stdint.h>
double ticksToNanoseconds(double ticks) {
static double nanosecondsPerTick = 0.0;
// The first time the function is called
// ask the system how to convert mach
// time units to nanoseconds
if (0.0 == nanosecondsPerTick) {
mach_timebase_info_data_t timebase;
// to be completely pedantic, check the return code of this call:
mach_timebase_info(&timebase);
nanosecondsPerTick = (double)timebase.numer/timebase.denom;
}
return ticks * nanosecondsPerTick;
}
double nanosecondsToCycles(double nanoseconds) {
static double cyclesPerNanosecond = 0.0;
// The first time the function is called
// ask the system what the CPU frequency is
if (0.0 == cyclesPerNanosecond) {
uint64_t freq;
size_t freqSize = sizeof(freq);
// Again, check the return code for correctness =)
sysctlbyname("hw.cpufrequency", &freq, &freqSize, NULL, 0L);
cyclesPerNanosecond = (double)freq * 1e-9;
}
return nanoseconds * cyclesPerNanosecond;
}
注意,轉換爲納秒永遠是聲音,但轉換到週期可能出差錯以不同的方式,因爲現代的CPU不會在一個運行固定速度。儘管如此,它一般工作得很好。
謝謝Stephen,非常棒!我會試試這個。 – Fred 2009-11-06 17:34:34
如果您遇到任何問題,請告訴我;我從內存中輸入了所有這些,所以我可能在某處出錯了) – 2009-11-06 17:40:03
所有這些其他答案都使用gettimeofday()
的某些變體進行計時。這很簡單,因爲通常需要多次運行內核才能獲得可重複的結果。將它放在一個緊密的循環中會改變代碼和數據高速緩存的狀態,所以這些結果可能不會指示真實的性能。
更好的選擇是實際使用CPU週期計數器。在x86上,你可以用rdtsc
指令來做到這一點。這是x264:
static inline uint32_t read_time(void)
{
uint32_t a = 0;
#if defined(__GNUC__) && (defined(ARCH_X86) || defined(ARCH_X86_64))
asm volatile("rdtsc" :"=a"(a) ::"edx");
#elif defined(ARCH_PPC)
asm volatile("mftb %0" : "=r" (a));
#elif defined(ARCH_ARM) // ARMv7 only
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(a));
#endif
return a;
}
更多關於使用各種硬件計數器分析,看PAPI。出於某些目的,模擬器(如Callgrind和基於中斷廓線儀(Oprofile)是有用的
你好,我會給你一個例子,解釋它:使用程序處理器時間:
#include <stdio.h>
#include <time.h>
int main(void)
{
clock_t start_clk = clock();
/*
put any code here
*/
printf("Processor time used by program: %lg sec.\n", \
(clock() - start_clk)/(long double) CLOCKS_PER_SEC);
return 0;
}
輸出4.94066 E-324秒
time.h中:。
聲明clock_t表示這是一個算術(你可以做在這個值的數學像我在示例做的)時間值 基本上把任何代碼評論的地方。
CLOCKS_PER_SEC是time.h中聲明的宏,用它作爲分母將值轉換爲秒。
它轉換爲long double的原因有兩個很重要的:
- 我們不知道是什麼類型clock_t表示實際上是,但我們想打印(你穿什麼轉換printf中?) 。
- long double是一個非常精確的類型,它可以表示非常小的值。
- 1. 如何在C#中測試性能?
- 2. jmeter性能測試:使用函數
- 3. C#從性能測試
- 4. 如何測試數據庫性能
- 5. 如何測試回撥函數? (C++ Boost單元測試)
- 6. 函數用於配置/性能測試PHP函數?
- 7. Objective-C/iOS - 測試C函數的存在性?
- 8. 數量/性能測試
- 9. C++的性能測試實用程序
- 10. 測試C++代碼的性能
- 11. 性能測試
- 12. 性能測試
- 13. Node.js:如何測試函數
- 14. 如果性能測試太慢,性能測試會失敗嗎?
- 15. 如何生成測試數據庫性能的隨機數據?
- 16. 清除C#性能測試緩衝區?
- 17. 用於測試性能的C++單元測試(合成基準測試)
- 18. 如何在android中測試性能?
- 19. 你如何做http性能測試?
- 20. 如何性能測試端點?
- 21. OpenX性能 - 如何測試它?
- 22. 如何跟蹤性能測試
- 23. 如何使用Xcode內建的單元測試框架來測試C函數?
- 24. Cython性能測試
- 25. Jmeter性能測試:
- 26. iOS - 性能測試?
- 27. Silverlight性能測試
- 28. Redis性能測試
- 29. Docker性能測試
- 30. JMeter - 性能測試
謝謝傑夫,這似乎是一個很好的方法。 – Fred 2009-11-06 15:02:25
當然,循環它足夠多次,你不會得到零的差異。 – Cascabel 2009-11-06 15:02:51