滾動自己很簡單分析器並不那麼難。使用此profileCpuUsage()不會增加太多的好處在簡單的例子
#define NUMBER(a) ((int)(sizeof(a)/sizeof(a)[0]))
void profileCpuUsage(int slice)
{
static struct {
int iterations;
double elapsedTime;
} slices[30]; // 0 is a don't care slice
if (slice < 0) { // -1 = print
if (slices[0].iterations)
for (slice = 1; slice < NUMBER(slices); slice++)
printf("Slice %2d Iterations %7d Seconds %7.3f\n", slice,
slices[slice].iterations, slices[slice].elapsedTime);
}
else {
static int i; // = previous slice
static double t; // = previous t1
const double t1 = realElapsedTime(); // see below for definition
assert (slice < NUMBER(slices));
slices[i].iterations += 1;
slices[i].elapsedTime += t1 - t; // i = 0 first time through
i = slice;
t = t1;
}
}
現在公認的:插入到主():
int main()
{
profileCpuUsage(1); // start timer #1
well_written_function();
profileCpuUsage(2); // stop timer #1, and start timer #2
badly_written_function();
profileCpuUsage(-1); // print stats for timers #1 and #2
return 0;
}
哪裏。它的缺點是要求你通過手動通過在適當的位置調用profileCpuUsage()來測試你的代碼。
但優勢包括:
- 您可以在時間任何的代碼片段,而不僅僅是程序。
- 與查找和/或刪除代碼熱點的二進制搜索一樣,添加和刪除操作非常快捷。
- 它只關注您感興趣的代碼。
- 便攜!
- KISS
一個棘手的非便攜的就是定義函數realElapsedTime(),所以它提供足夠的粒度,以獲得有效時間。這通常對我的作品(使用Cygwin下的Windows API):
#include <windows.h>
double realElapsedTime(void) // <-- granularity about 50 microsec on test machines
{
static LARGE_INTEGER freq, start;
LARGE_INTEGER count;
if (!QueryPerformanceCounter(&count))
assert(0 && "QueryPerformanceCounter");
if (!freq.QuadPart) { // one time initialization
if (!QueryPerformanceFrequency(&freq))
assert(0 && "QueryPerformanceFrequency");
start = count;
}
return (double)(count.QuadPart - start.QuadPart)/freq.QuadPart;
}
對於直Unix上有共同的:
double realElapsedTime(void) // returns 0 first time called
{
static struct timeval t0;
struct timeval tv;
gettimeofday(&tv, 0);
if (!t0.tv_sec)
t0 = tv;
return tv.tv_sec - t0.tv_sec + (tv.tv_usec - t0.tv_usec)/1000000.;
}
realElapsedTime()提供掛鐘時間,不處理時間,這通常是我想要的。
還有其他一些不太方便的方法來使用RDTSC實現更精細的粒度;例如參見http://en.wikipedia.org/wiki/Time_Stamp_Counter及其鏈接,但我沒有嘗試過這些。
編輯: ravenspoint的非常好的答案似乎不是我的太不相似。 和他的回答使用了很好的描述性字符串,而不僅僅是醜陋的數字,我經常感到沮喪。但是,這可以修復只有十幾個額外的行(但這幾乎雙打行數!)。
請注意,我們希望避免malloc()的任何用法,並且我甚至對strcmp()有點懷疑。所以切片的數量永遠不會增加。散列衝突只是被標記爲相當被解析:人類分析器可以通過手動地從30增加切片的數量或者通過改變描述來解決這個問題。 未經測試
static unsigned gethash(const char *str) // "djb2", for example
{
unsigned c, hash = 5381;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; // hash * 33 + c
return hash;
}
void profileCpuUsage(const char *description)
{
static struct {
int iterations;
double elapsedTime;
char description[20]; // added!
} slices[30];
if (!description) {
// print stats, but using description, mostly unchanged...
}
else {
const int slice = gethash(description) % NUMBER(slices);
if (!slices[slice].description[0]) { // if new slice
assert(strlen(description) < sizeof slices[slice].description);
strcpy(slices[slice].description, description);
}
else if (!!strcmp(slices[slice].description, description)) {
strcpy(slices[slice].description, "!!hash conflict!!");
}
// remainder unchanged...
}
}
另一點是,通常你要禁用此分析的發行版本;這也適用於ravenspoint的答案。這可以通過使用一個邪惡的宏的把戲來完成,以確定它扔掉:
#define profileCpuUsage(foo) // = nothing
如果這樣做了,你當然需要括號添加至定義禁用禁用宏:
void (profileCpuUsage)(const char *description)...
這通常被稱爲「callgraph profile」,我相當確信Visual Studio會這樣做。儘管自從我完成Windows開發幾年後,我的記憶就有些模糊。 – 2010-06-15 17:00:09
我非常確定vtune允許在可以看到的所有細分中進行排序,並按所花費的總時間(包括它所調用的函數)進行排序。然而,爲了使用它,你需要一個合理的直覺來理解在哪個函數中需要花費多少時間。 – torak 2010-06-15 17:06:49
@torak:是否可以記住功能的名稱? - 我可以粗略地瞭解某些功能應該採用多長時間,所以我相信這樣的崩潰確實會非常有用。 – Mick 2010-06-15 17:11:34