2010-06-15 51 views
7

Tôi có một vấn đề hồ sơ - tưởng tượng tôi có đoạn mã sau ...Profiling sự giúp đỡ cần thiết

void main() 
{ 
    well_written_function(); 
    badly_written_function(); 
} 
void well_written_function() 
{ 
    for (a small number) 
    { 
     highly_optimised_subroutine(); 
    } 
} 
void badly_written_function() 
{ 
    for (a wastefully and unnecessarily large number) 
    { 
     highly_optimised_subroutine(); 
    } 
} 
void highly_optimised_subroutine() 
{ 
    // lots of code 
} 

Nếu tôi chạy dưới vtune (hoặc profilers khác) nó là rất khó để phát hiện bất cứ điều gì là sai. Tất cả các điểm nóng sẽ xuất hiện trong phần được đánh dấu "// nhiều mã" đã được tối ưu hóa. Các badly_written_function() sẽ không được đánh dấu trong bất kỳ cách nào mặc dù nó là nguyên nhân của tất cả các rắc rối.

Có một số tính năng của vtune sẽ giúp tôi tìm ra sự cố không?

Có chế độ nào đó theo đó tôi có thể tìm thấy thời gian được thực hiện bởi badly_written_function() và tất cả các chức năng phụ của nó?

+3

Thường được gọi là "hồ sơ cuộc gọi", và tôi khá chắc Visual Studio sẽ làm điều đó. Mặc dù bộ nhớ của tôi hơi mơ hồ một vài năm kể từ khi tôi thực hiện bất kỳ sự phát triển Windows nào. –

+1

Tôi khá chắc chắn vtune cho phép phân tích nơi bạn có thể xem và sắp xếp theo tổng thời gian tiêu thụ bao gồm các chức năng mà nó gọi. Tuy nhiên, để được sử dụng, bạn cần một intuituion hợp lý của bao nhiêu thời gian nên được chi tiêu trong đó chức năng. – torak

+0

@torak: có thể nhớ tên của đối tượng địa lý không? - Tôi có một nắm bắt thô về các chức năng nhất định nên mất bao lâu vì vậy tôi chắc chắn rằng một sự cố như vậy sẽ rất hữu ích thực sự. – Mick

Trả lời

1

Điều đó thường được gọi là "hồ sơ cuộc gọi", và tôi khá chắc chắn Visual Studio sẽ làm điều đó.

1

Lăn riêng của bạn rất đơn giản profiler không phải là khó. Chèn vào main():

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; 
} 

nơi:

#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; 
    } 
} 

Bây giờ phải thừa nhận là trong ví dụ đơn giản bằng cách sử dụng profileCpuUsage này() không thêm nhiều lợi ích. Và nó có bất lợi khi yêu cầu bạn thủ công cụ mã của bạn bằng cách gọi profileCpuUsage() tại các vị trí thích hợp.

Nhưng ưu điểm bao gồm:

  • Bạn có thể thời gian bất kỳ mảnh mã, không chỉ là thủ tục.
  • Nhanh chóng thêm và xóa, khi bạn thực hiện tìm kiếm nhị phân để tìm và/hoặc xóa các điểm nóng mã.
  • Nó chỉ tập trung vào mã bạn quan tâm.
  • Di động!
  • KISS

Một khéo léo điều phi di động là xác định các chức năng realElapsedTime() để nó cung cấp đủ chi tiết để có được thời gian hợp lệ. Điều này thường làm việc cho tôi (bằng cách sử dụng API của Windows dưới Cygwin):

#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; 
} 

Đối với Unix thẳng có những điểm chung:

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() tạo ra khoảng thời gian tường đồng hồ, không xử lý thời gian, mà thường là những gì tôi muốn.

Ngoài ra còn có các phương pháp ít di động khác để đạt được độ chi tiết mịn hơn bằng cách sử dụng RDTSC; xem ví dụ: http://en.wikipedia.org/wiki/Time_Stamp_Counter và các liên kết của nó, nhưng tôi chưa thử.

Chỉnh sửa: câu trả lời rất hay của ravenspoint có vẻ không quá khác biệt so với tôi. câu trả lời của anh ấy sử dụng các chuỗi mô tả tốt đẹp, chứ không phải chỉ là những con số xấu xí mà tôi thường cảm thấy thất vọng.Nhưng điều này có thể được khắc phục chỉ với khoảng một tá dòng phụ (nhưng điều này gần như tăng gấp đôi số số dòng!).

Lưu ý rằng chúng tôi muốn tránh bất kỳ việc sử dụng malloc() và thậm chí tôi không chắc chắn về strcmp(). Vì vậy, số lượng lát không bao giờ tăng lên. Và các va chạm băm đơn giản là gắn cờ nó thay vì được giải quyết: profiler của con người có thể sửa lỗi này bằng cách chạm thủ công số lượng lát từ 30 hoặc bằng cách thay đổi mô tả. chưa được kiểm tra

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... 
    } 
} 

Và điểm khác là thường bạn sẽ muốn vô hiệu hóa hồ sơ này cho các phiên bản phát hành; điều này cũng áp dụng cho câu trả lời của ravenspoint. Điều này có thể được thực hiện bằng các trick của việc sử dụng một vĩ mô ác để xác định nó đi:

#define profileCpuUsage(foo)    // = nothing 

Nếu đây được thực hiện, bạn sẽ tất nhiên cần phải thêm dấu ngoặc đơn để định nghĩa để vô hiệu hóa các macro vô hiệu hóa:

void (profileCpuUsage)(const char *description)... 
1

Tôi có thể đề xuất mã nguồn mở profiler của riêng mình :: set :: cRunWatch? Nó được thiết kế cho chính xác vấn đề này và hoạt động trên Windows bằng cách sử dụng Visual Studio 2008 Standard Edition, vì vậy bạn không cần phải trả tiền cho phiên bản có trình bao gồm.

tôi đã lấy mã của bạn, sắp xếp lại nó một chút để nó biên dịch mà không cần tờ khai phía trước và thêm vào các cuộc gọi cần thiết để cRunWatch

// RunWatchDemo.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

void highly_optimised_subroutine() 
{ 
    raven::set::cRunWatch runwatch("highly_optimised_subroutine"); 
    Sleep(2); 
} 


void badly_written_function() 
{ 
    raven::set::cRunWatch runwatch("badly_written_function"); 
    for (int k = 1; k < 1000; k++) 
    { 
     highly_optimised_subroutine(); 
    } 
} 

void well_written_function() 
{ 
    raven::set::cRunWatch runwatch("well_written_function"); 
    for (int k = 1; k < 10; k++) 
    { 
     highly_optimised_subroutine(); 
    } 
} 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
raven::set::cRunWatch::Start(); 

    well_written_function(); 
    badly_written_function(); 

raven::set::cRunWatch::Report(); 

    return 0; 
} 

Khi chạy này sẽ cho kết quả

raven::set::cRunWatch code timing profile 
        Scope Calls  Mean (secs)  Total 
highly_optimised_subroutine  1008 0.002921  2.944146 
    badly_written_function  1  2.926662  2.926662 
    well_written_function  1  0.026239  0.026239 

Điều này cho thấy badly_written_function để là người dùng thứ hai rất gần, và do đó là thủ phạm.

Bạn có thể lấy cRunWatch từ here Bạn sẽ nhận ra những mẫu mã trong Hướng dẫn sử dụng :-)

+0

+1 Nhưng yêu cầu BOOST. –

+0

Nó yêu cầu tăng cường. Hầu như mọi chương trình tôi viết đều sử dụng tăng theo cách này hay cách khác. Boost là miễn phí và cực kỳ hữu ích. – ravenspoint

+0

Nhưng tôi làm việc trên các nền tảng HPUX 10.20 15 tuổi. Đừng hỏi: ( –

0

Nói chung, đây là cái gì đó mà bạn muốn quan sát tổng thời gian chức năng như trái ngược với tự thời gian, để đảm bảo rằng bạn đang xem xét thời gian bao gồm thời gian của các hàm được gọi.

Trong VTune, tôi khuyên bạn nên sử dụng tab Trên cùng cho điều đó. Hoặc, thậm chí tốt hơn và nếu bạn đang sử dụng bản cập nhật mới nhất, hãy thử chế độ xem Caller-Callee thử nghiệm mới. Bạn có thể biết chi tiết về điều này tại đây - http://software.intel.com/en-us/forums/topic/376210. Nó nhận được một danh sách các hàm chức năng với tổng thời gian của chúng để bạn có thể xem các subtrees tiêu thụ hàng đầu trong chương trình của bạn là bao nhiêu.

Các vấn đề liên quan