profiler.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #include "pocketpy/profiler.h"
  2. namespace pkpy{
  3. static std::string left_pad(std::string s, int width){
  4. int n = width - s.size();
  5. if(n <= 0) return s;
  6. return std::string(n, ' ') + s;
  7. }
  8. static std::string to_string_1f(f64 x){
  9. char buf[32];
  10. snprintf(buf, 32, "%.1f", x);
  11. return buf;
  12. }
  13. void LineProfiler::begin(){
  14. frames.clear();
  15. }
  16. void LineProfiler::_step(LinkedFrame* linked_frame){
  17. Frame* frame = &linked_frame->frame;
  18. auto line_info = frame->co->lines[frame->_ip];
  19. if(line_info.is_virtual) return;
  20. std::string_view filename = frame->co->src->filename.sv();
  21. int line = line_info.lineno;
  22. if(frames.empty()){
  23. frames.push({linked_frame, clock(), nullptr});
  24. }else{
  25. _step_end(linked_frame, line);
  26. }
  27. auto& file_records = records[filename];
  28. if(file_records.empty()){
  29. // initialize file_records
  30. int total_lines = frame->co->src->line_starts.size();
  31. file_records.resize(total_lines + 1);
  32. for(int i=1; i<=total_lines; i++){
  33. file_records[i].line = i;
  34. }
  35. }
  36. frames.top().prev_record = &file_records.at(line);
  37. }
  38. void LineProfiler::_step_end(LinkedFrame* linked_frame, int line){
  39. clock_t now = clock();
  40. _FrameRecord& top_frame_record = frames.top();
  41. _LineRecord* prev_record = top_frame_record.prev_record;
  42. int id_delta;
  43. if(linked_frame == top_frame_record.frame){
  44. id_delta = 0;
  45. }else if(linked_frame->f_back == top_frame_record.frame){
  46. id_delta = 1;
  47. }else{
  48. id_delta = -1; // unsafe
  49. }
  50. // current line is about to change
  51. if(prev_record->line != line){
  52. clock_t delta = now - top_frame_record.prev_time;
  53. top_frame_record.prev_time = now;
  54. if(id_delta != 1) prev_record->hits++;
  55. prev_record->time += delta;
  56. }
  57. if(id_delta == 1){
  58. frames.push({linked_frame, now, nullptr});
  59. }else{
  60. if(id_delta == -1) frames.pop();
  61. }
  62. }
  63. void LineProfiler::end(){
  64. clock_t now = clock();
  65. _FrameRecord& top_frame_record = frames.top();
  66. _LineRecord* prev_record = top_frame_record.prev_record;
  67. clock_t delta = now - top_frame_record.prev_time;
  68. top_frame_record.prev_time = now;
  69. prev_record->hits++;
  70. prev_record->time += delta;
  71. frames.pop();
  72. PK_ASSERT(frames.empty());
  73. }
  74. Str LineProfiler::stats(){
  75. SStream ss;
  76. for(FuncDecl* decl: functions){
  77. int start_line = decl->code->start_line;
  78. int end_line = decl->code->end_line;
  79. if(start_line == -1 || end_line == -1) continue;
  80. std::string_view filename = decl->code->src->filename.sv();
  81. std::vector<_LineRecord>& file_records = records[filename];
  82. if(file_records.empty()) continue;
  83. clock_t total_time = 0;
  84. for(int line = start_line; line <= end_line; line++){
  85. total_time += file_records.at(line).time;
  86. }
  87. ss << "Total time: " << (f64)total_time / CLOCKS_PER_SEC << "s\n";
  88. ss << "File: " << filename << "\n";
  89. ss << "Function: " << decl->code->name << " at line " << start_line << "\n";
  90. ss << "Line # Hits Time Per Hit % Time Line Contents\n";
  91. ss << "==============================================================\n";
  92. for(int line = start_line; line <= end_line; line++){
  93. const _LineRecord& record = file_records.at(line);
  94. if(!record.is_valid()) continue;
  95. ss << left_pad(std::to_string(line), 6);
  96. if(record.hits == 0){
  97. ss << std::string(10 + 13 + 9 + 9, ' ');
  98. }else{
  99. ss << left_pad(std::to_string(record.hits), 10);
  100. ss << left_pad(std::to_string(record.time), 13);
  101. ss << left_pad(std::to_string(record.time / record.hits), 9);
  102. if(total_time == 0){
  103. ss << left_pad("0.0", 9);
  104. }else{
  105. ss << left_pad(to_string_1f(record.time * (f64)100 / total_time), 9);
  106. }
  107. }
  108. // line_content
  109. auto [_0, _1] = decl->code->src->_get_line(line);
  110. ss << " " << std::string_view(_0, _1-_0) << "\n";
  111. }
  112. ss << "\n";
  113. }
  114. return ss.str();
  115. }
  116. } // namespace pkpy