C++統(tǒng)計函數(shù)執(zhí)行時間的最佳實踐
前言
在軟件開發(fā)過程中,性能分析是優(yōu)化程序的重要環(huán)節(jié)。特別是在C++這樣的高性能語言中,了解函數(shù)的執(zhí)行時間分布對于識別性能瓶頸至關(guān)重要。今天我們來介紹一個輕量級的C++函數(shù)執(zhí)行時間統(tǒng)計工具,它能夠幫助開發(fā)者快速定位性能問題。
工具特性
這個函數(shù)計時器具有以下核心特性:
功能完備性
- 統(tǒng)計函數(shù)調(diào)用次數(shù)、總執(zhí)行時間、平均時間、最小時間、最大時間
- 支持多函數(shù)同時監(jiān)控
- 提供統(tǒng)計報告輸出和數(shù)據(jù)清理功能
易用性
- 使用RAII(Resource Acquisition Is Initialization)機(jī)制自動計時
- 一行宏定義即可啟用監(jiān)控:
TIMER_SCOPE("函數(shù)名") - 無侵入性設(shè)計,不影響原有代碼邏輯
線程安全性
- 使用互斥鎖保護(hù)共享數(shù)據(jù)
- 支持多線程環(huán)境下的并發(fā)使用
性能友好
- 基于
std::chrono::steady_clock提供高精度計時 - 單例模式減少內(nèi)存開銷
核心設(shè)計
1. 數(shù)據(jù)結(jié)構(gòu)設(shè)計
struct FunctionStats {
std::string functionName;
long totalTimeMs; // 總執(zhí)行時間
int callCount; // 調(diào)用次數(shù)
long minTimeMs; // 最小執(zhí)行時間
long maxTimeMs; // 最大執(zhí)行時間
double avgTimeMs; // 平均執(zhí)行時間
};
FunctionStats 結(jié)構(gòu)體封裝了單個函數(shù)的完整統(tǒng)計信息,通過 addExecution 方法動態(tài)更新統(tǒng)計數(shù)據(jù)。
2. 單例模式管理器
FunctionTimer 類采用線程安全的單例模式,確保全局只有一個統(tǒng)計管理器實例:
static FunctionTimer* getInstance() {
std::lock_guard<std::mutex> lock(instanceMutex);
if (instance == nullptr) {
instance = std::unique_ptr<FunctionTimer>(new FunctionTimer());
}
return instance.get();
}
3. RAII自動計時
ScopedTimer 類是整個工具的核心,利用C++的RAII特性實現(xiàn)自動計時:
class ScopedTimer {
std::string functionName;
std::chrono::steady_clock::time_point startTime;
public:
explicit ScopedTimer(const std::string& funcName);
~ScopedTimer(); // 析構(gòu)時自動記錄執(zhí)行時間
};
當(dāng)對象創(chuàng)建時記錄開始時間,當(dāng)對象銷毀(離開作用域)時自動計算并記錄執(zhí)行時間。
使用方法
基本用法
#include "function_timer.h"
void someFunction() {
TIMER_SCOPE("someFunction"); // 添加這一行即可
// 原有的函數(shù)邏輯
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
int main() {
// 執(zhí)行一些被監(jiān)控的函數(shù)
for(int i = 0; i < 10; i++) {
someFunction();
}
// 打印統(tǒng)計報告
FunctionTimer::getInstance()->printStats();
return 0;
}
高級用法
// 獲取特定函數(shù)的統(tǒng)計信息
FunctionStats stats = FunctionTimer::getInstance()->getFunctionStats("someFunction");
std::cout << "函數(shù)調(diào)用了 " << stats.callCount << " 次" << std::endl;
// 獲取所有統(tǒng)計數(shù)據(jù)
auto allStats = FunctionTimer::getInstance()->getAllStats();
for(const auto& pair : allStats) {
std::cout << pair.first << ": " << pair.second.avgTimeMs << "ms" << std::endl;
}
// 清理統(tǒng)計數(shù)據(jù)
FunctionTimer::getInstance()->clearStats();
輸出示例
========== 函數(shù)執(zhí)行時間統(tǒng)計報告 ==========
函數(shù)名稱 | 調(diào)用次數(shù) | 總時間(ms) | 平均時間(ms) | 最小時間(ms) | 最大時間(ms)
----------------------------------------------------------------------
someFunction | 10 | 1005 | 100 | 99 | 102
anotherFunction | 5 | 250 | 50 | 48 | 53
==========================================
實現(xiàn)亮點
1. 線程安全保證
使用std::mutex和std::lock_guard確保多線程環(huán)境下的數(shù)據(jù)一致性:
void recordExecution(const std::string& functionName, long executionTimeMs) {
std::lock_guard<std::mutex> lock(statsMutex);
// 線程安全的數(shù)據(jù)更新
stats[functionName].addExecution(executionTimeMs);
}
2. 異常安全
即使函數(shù)執(zhí)行過程中拋出異常,ScopedTimer 的析構(gòu)函數(shù)仍會被調(diào)用,確保計時統(tǒng)計的準(zhǔn)確性。
3. 內(nèi)存管理
使用智能指針std::unique_ptr管理單例實例,避免內(nèi)存泄漏。
性能考量
這個工具設(shè)計時充分考慮了性能影響:
- 時間復(fù)雜度:記錄操作為O(1),查詢操作為O(1)
- 空間復(fù)雜度:每個監(jiān)控函數(shù)只需少量內(nèi)存存儲統(tǒng)計信息
- 運行時開銷:主要開銷來自時間獲取和互斥鎖操作,影響極小
擴(kuò)展建議
添加采樣功能:對于高頻調(diào)用的函數(shù),可以添加采樣機(jī)制減少性能影響
支持更多統(tǒng)計指標(biāo):如95百分位數(shù)、標(biāo)準(zhǔn)差等
可視化輸出:生成圖表或JSON格式的統(tǒng)計報告
持久化存儲:將統(tǒng)計數(shù)據(jù)保存到文件中供后續(xù)分析
適用場景
這個工具特別適用于以下場景:
- 性能調(diào)優(yōu)階段:識別熱點函數(shù)和性能瓶頸
- 算法比較:對比不同算法實現(xiàn)的性能差異
- 回歸測試:監(jiān)控性能回歸問題
- 生產(chǎn)環(huán)境監(jiān)控:輕量級的性能監(jiān)控(建議添加開關(guān)控制)
總結(jié)
這個C++函數(shù)執(zhí)行時間統(tǒng)計工具雖然簡潔,但功能完備、使用方便。它體現(xiàn)了優(yōu)秀工具設(shè)計的幾個原則:
- 簡單易用:一行代碼即可啟用監(jiān)控
- 功能完整:提供全面的統(tǒng)計信息
- 性能友好:最小化對原程序的影響
- 線程安全:支持多線程環(huán)境
對于需要進(jìn)行性能分析的C++項目,這是一個非常實用的工具。通過合理使用,開發(fā)者可以快速定位性能問題,提升程序效率。
完整代碼
function_timer.h
#pragma once
#include <chrono>
#include <string>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <climits>
struct FunctionStats {
std::string functionName;
long totalTimeMs;
int callCount;
long minTimeMs;
long maxTimeMs;
double avgTimeMs;
FunctionStats() : totalTimeMs(0), callCount(0), minTimeMs(LONG_MAX), maxTimeMs(0), avgTimeMs(0.0) {}
void addExecution(long executionTimeMs) {
totalTimeMs += executionTimeMs;
callCount++;
if (executionTimeMs < minTimeMs) minTimeMs = executionTimeMs;
if (executionTimeMs > maxTimeMs) maxTimeMs = executionTimeMs;
avgTimeMs = static_cast<double>(totalTimeMs) / callCount;
}
};
class FunctionTimer {
private:
static std::unique_ptr<FunctionTimer> instance;
static std::mutex instanceMutex;
std::unordered_map<std::string, FunctionStats> stats;
mutable std::mutex statsMutex;
FunctionTimer() = default;
public:
static FunctionTimer* getInstance();
void recordExecution(const std::string& functionName, long executionTimeMs);
void printStats() const;
void clearStats();
FunctionStats getFunctionStats(const std::string& functionName) const;
std::unordered_map<std::string, FunctionStats> getAllStats() const;
};
class ScopedTimer {
private:
std::string functionName;
std::chrono::steady_clock::time_point startTime;
public:
explicit ScopedTimer(const std::string& funcName);
~ScopedTimer();
};
#define TIMER_SCOPE(funcName) ScopedTimer timer(funcName)
function_timer.cpp
#include "../include/function_timer.h"
#include <iostream>
#include <iomanip>
#include <climits>
#include "../../common/logging/log_helper.h"
std::unique_ptr<FunctionTimer> FunctionTimer::instance = nullptr;
std::mutex FunctionTimer::instanceMutex;
FunctionTimer* FunctionTimer::getInstance() {
std::lock_guard<std::mutex> lock(instanceMutex);
if (instance == nullptr) {
instance = std::unique_ptr<FunctionTimer>(new FunctionTimer());
}
return instance.get();
}
void FunctionTimer::recordExecution(const std::string& functionName, long executionTimeMs) {
std::lock_guard<std::mutex> lock(statsMutex);
if (stats.find(functionName) == stats.end()) {
stats[functionName].functionName = functionName;
}
stats[functionName].addExecution(executionTimeMs);
}
void FunctionTimer::printStats() const {
std::lock_guard<std::mutex> lock(statsMutex);
LogHelper::logInfo("========== 函數(shù)執(zhí)行時間統(tǒng)計報告 ==========");
LogHelper::logInfo("函數(shù)名稱 | 調(diào)用次數(shù) | 總時間(ms) | 平均時間(ms) | 最小時間(ms) | 最大時間(ms)");
LogHelper::logInfo("----------------------------------------------------------------------");
for (const auto& pair : stats) {
const FunctionStats& stat = pair.second;
std::string logMsg = stat.functionName + " | " +
std::to_string(stat.callCount) + " | " +
std::to_string(stat.totalTimeMs) + " | " +
std::to_string(static_cast<long>(stat.avgTimeMs)) + " | " +
std::to_string(stat.minTimeMs == LONG_MAX ? 0 : stat.minTimeMs) + " | " +
std::to_string(stat.maxTimeMs);
LogHelper::logInfo(logMsg);
}
LogHelper::logInfo("==========================================");
}
void FunctionTimer::clearStats() {
std::lock_guard<std::mutex> lock(statsMutex);
stats.clear();
}
FunctionStats FunctionTimer::getFunctionStats(const std::string& functionName) const {
std::lock_guard<std::mutex> lock(statsMutex);
auto it = stats.find(functionName);
if (it != stats.end()) {
return it->second;
}
return FunctionStats();
}
std::unordered_map<std::string, FunctionStats> FunctionTimer::getAllStats() const {
std::lock_guard<std::mutex> lock(statsMutex);
return stats;
}
ScopedTimer::ScopedTimer(const std::string& funcName)
: functionName(funcName), startTime(std::chrono::steady_clock::now()) {
}
ScopedTimer::~ScopedTimer() {
auto endTime = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
long executionTimeMs = duration.count();
FunctionTimer::getInstance()->recordExecution(functionName, executionTimeMs);
}
以上就是C++統(tǒng)計函數(shù)執(zhí)行時間的最佳實踐的詳細(xì)內(nèi)容,更多關(guān)于C++統(tǒng)計函數(shù)執(zhí)行時間的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++實現(xiàn)二分法的一些細(xì)節(jié)(常用場景)
二分法算法思想首先確定有根區(qū)間,將區(qū)間二等分,通過判斷f(x)的符號,逐步將有根區(qū)間縮小,直至有根區(qū)間足夠小,便可求出滿足精度要求的近似值2021-07-07
c++與python實現(xiàn)二分查找的原理及實現(xiàn)
本文介紹了c++與python實現(xiàn)二分查找的原理及實現(xiàn),二分查找指首先將數(shù)組中間值和目標(biāo)值進(jìn)行比較,如果相等則返回;如果不相等,則選擇中間值左邊的一半或者右邊的一半進(jìn)行比較;不斷重復(fù)直到檢索完畢,下文相關(guān)資料需要的朋友可以參考一下2022-03-03

