profiler.cpp 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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. prev_time = 0;
  15. prev_record = nullptr;
  16. prev_line = -1;
  17. records.clear();
  18. }
  19. void LineProfiler::_step(Frame *frame){
  20. std::string_view filename = frame->co->src->filename.sv();
  21. int line = frame->co->lines[frame->_ip];
  22. // std::string_view function = frame->co->name.sv();
  23. if(prev_record == nullptr){
  24. prev_time = clock();
  25. }else{
  26. _step_end();
  27. }
  28. std::vector<LineRecord>& file_records = records[filename];
  29. if(file_records.empty()){
  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. prev_record = &file_records.at(line);
  37. }
  38. void LineProfiler::_step_end(){
  39. clock_t now = clock();
  40. clock_t delta = now - prev_time;
  41. prev_time = now;
  42. if(prev_record->line != prev_line){
  43. prev_record->hits++;
  44. prev_line = prev_record->line;
  45. }
  46. prev_record->time += delta;
  47. }
  48. void LineProfiler::end(){
  49. _step_end();
  50. }
  51. Str LineProfiler::stats(){
  52. SStream ss;
  53. for(FuncDecl* decl: functions){
  54. int start_line = decl->code->start_line;
  55. int end_line = decl->code->end_line;
  56. if(start_line == -1 || end_line == -1) continue;
  57. std::string_view filename = decl->code->src->filename.sv();
  58. std::vector<LineRecord>& file_records = records[filename];
  59. if(file_records.empty()) continue;
  60. clock_t total_time = 0;
  61. for(int line = start_line; line <= end_line; line++){
  62. total_time += file_records.at(line).time;
  63. }
  64. ss << "Total time: " << (f64)total_time / CLOCKS_PER_SEC << "s\n";
  65. ss << "File: " << filename << "\n";
  66. ss << "Function: " << decl->code->name << " at line " << start_line << "\n";
  67. ss << "Line # Hits Time Per Hit % Time Line Contents\n";
  68. ss << "==============================================================\n";
  69. for(int line = start_line; line <= end_line; line++){
  70. const LineRecord& record = file_records.at(line);
  71. if(!record.is_valid()) continue;
  72. ss << left_pad(std::to_string(line), 6);
  73. if(record.hits == 0){
  74. ss << std::string(10 + 13 + 9 + 9, ' ');
  75. }else{
  76. ss << left_pad(std::to_string(record.hits), 10);
  77. ss << left_pad(std::to_string(record.time), 13);
  78. ss << left_pad(std::to_string(record.time / record.hits), 9);
  79. ss << left_pad(to_string_1f(record.time * (f64)100 / total_time), 9);
  80. }
  81. // line_content
  82. auto [_0, _1] = decl->code->src->_get_line(line);
  83. ss << " " << std::string_view(_0, _1-_0) << "\n";
  84. }
  85. ss << "\n";
  86. }
  87. return ss.str();
  88. }
  89. } // namespace pkpy