提高C++性能的编程技术笔记:跟踪实例+测试代码
當(dāng)提高性能時(shí),我們必須記住以下幾點(diǎn):
(1). 內(nèi)存不是無限大的。虛擬內(nèi)存系統(tǒng)使得內(nèi)存看起來是無限的,而事實(shí)上并非如此。
(2). 內(nèi)存訪問開銷不是均衡的。對(duì)緩存、主內(nèi)存和磁盤的訪問開銷不在同一個(gè)數(shù)量級(jí)之上。
(3). 我們的程序沒有專用的CPU,只能間歇地獲得一個(gè)時(shí)間片。
(4). 在一臺(tái)單處理器的計(jì)算機(jī)上,并行的線程并不是真正地并行執(zhí)行,它們是輪詢的。
“性能”可以有幾種衡量標(biāo)準(zhǔn),最常見的兩種是空間效率和時(shí)間效率。空間效率標(biāo)準(zhǔn)尋求占用最小內(nèi)存的軟件解決方案。相似的,時(shí)間效率標(biāo)準(zhǔn)尋求占用最少處理器周期的解決方案。時(shí)間效率通常以響應(yīng)時(shí)間和吞吐量來作為衡量標(biāo)準(zhǔn)。其它衡量標(biāo)準(zhǔn)還有編譯時(shí)間和可執(zhí)行文件的大小。
許多C++程序員在跟蹤代碼時(shí)通常的做法是,定義一個(gè)簡(jiǎn)單的Trace類將診斷信息打印到日志文件中。程序員可以在每個(gè)想要跟蹤的函數(shù)中定義一個(gè)Trace對(duì)象,在函數(shù)的入口和出口Trace類可以分別寫一條信息。盡管Trace對(duì)象將增加程序額外的執(zhí)行開銷,但是它能夠幫助程序員找出問題而無須使用調(diào)試器。
最理想的跟蹤性能優(yōu)化的方法應(yīng)該能夠完全消除性能開銷,即把跟蹤調(diào)用嵌入在#ifdef塊內(nèi)。使用這種方法的不足在于必須重新編譯程序來打開或關(guān)閉跟蹤。還有一種選擇:可以通過與正在運(yùn)行的程序通信來動(dòng)態(tài)地控制跟蹤。Trace類能夠在記錄任何跟蹤信息之前先檢查跟蹤狀態(tài)。
影響C++性能的因素:I/O的開銷是高昂的;函數(shù)調(diào)用的開銷是要考慮的一個(gè)因素,因此我們應(yīng)該將短小的、頻繁調(diào)用的函數(shù)內(nèi)聯(lián);復(fù)制對(duì)象的開銷是高昂的,最好選擇傳遞引用,而不是傳遞值。
內(nèi)聯(lián)對(duì)大塊頭函數(shù)的影響是無足輕重的。只有在針對(duì)那些調(diào)用和返回開銷占全部開銷的絕大部分的小型函數(shù)時(shí),內(nèi)聯(lián)對(duì)性能的改善才有較大的影響。內(nèi)聯(lián)消除了常被使用的小函數(shù)調(diào)用所產(chǎn)生的函數(shù)開銷。完美適合內(nèi)聯(lián)的函數(shù)就恰好是非常不適合跟蹤的。
對(duì)象定義會(huì)觸發(fā)隱形地執(zhí)行構(gòu)造函數(shù)和析構(gòu)函數(shù)。我們稱其為”隱性執(zhí)行”而不是”隱性開銷”是因?yàn)閷?duì)象的構(gòu)造和銷毀并不總是意味產(chǎn)生開銷。
通過引用傳遞對(duì)象還是不能保證良好的性能,所以避免對(duì)象的復(fù)制的確有利于提高性能,但是如果我們不必一開始就創(chuàng)建和銷毀該對(duì)象的話,這種處理方式將更有利于性能的提升。
在完成同樣的簡(jiǎn)單工作時(shí),char指針有時(shí)可以比string對(duì)象更有效率。
以下是測(cè)試代碼(the_tracing_war_story.cpp),分別有Trace1,Trace2,Trace3三個(gè)簡(jiǎn)單的類,它們的性能逐漸提高:
#include "the_tracing_war_story.hpp"
#include <string>
#include <iostream>
#include <chrono>namespace tracing_war_story_ {
//
// 較差的Trace1類設(shè)計(jì)
class Trace1 {
public:Trace1(const std::string& name);~Trace1();void debug(const std::string& msg);static bool traceIsActive;
private:std::string theFunctionName;
};inline Trace1::Trace1(const std::string& name) : theFunctionName(name)
{if (traceIsActive) {std::cout << "Enter function: " << name << std::endl;}
}inline Trace1::~Trace1()
{if (traceIsActive) {std::cout <<"Exit function: " << theFunctionName << std::endl;}
}inline void Trace1::debug(const std::string& msg)
{if (traceIsActive) {std::cout << msg << std::endl;}
}bool Trace1::traceIsActive = false; // 默認(rèn)設(shè)置為false關(guān)閉跟蹤;可以在test_tracing_war_story中設(shè)置是否開啟還是關(guān)閉跟蹤int addOne0(int x)
{return x+1;
}int addOne1(int x)
{std::string name = "addOne1";Trace1 t(name);return x+1;
}///
// Trace2類是對(duì)Trace1類的改進(jìn):將函數(shù)參數(shù)string類型調(diào)整為const char*
class Trace2 {
public:Trace2(const char* name);~Trace2();void debug(const char* msg);static bool traceIsActive;
private:std::string theFunctionName;
};inline Trace2::Trace2(const char* name) : theFunctionName(name)
{if (traceIsActive) {std::cout << "Enter function: " << name << std::endl;}
}inline Trace2::~Trace2()
{if (traceIsActive) {std::cout <<"Exit function: " << theFunctionName << std::endl;}
}inline void Trace2::debug(const char* msg)
{if (traceIsActive) {std::cout << msg << std::endl;}
}bool Trace2::traceIsActive = false; // 默認(rèn)設(shè)置為false關(guān)閉跟蹤;可以在test_tracing_war_story中設(shè)置是否開啟還是關(guān)閉跟蹤int addOne2(int x)
{char* name = "addOne2";Trace2 t(name);return x+1;
}/
// Trace3類是對(duì)Trace2類的改進(jìn):消除包含在Trace2類內(nèi)的string成員對(duì)象的無條件的創(chuàng)建
class Trace3 {
public:Trace3(const char* name);~Trace3();void debug(const char* msg);static bool traceIsActive;
private:// string指針可以把string對(duì)象的創(chuàng)建推遲到確定跟蹤處于打開狀態(tài)以后std::string* theFunctionName;
};inline Trace3::Trace3(const char* name) : theFunctionName(nullptr)
{if (traceIsActive) {std::cout << "Enter function: " << name << std::endl;theFunctionName = new std::string(name);}
}inline Trace3::~Trace3()
{if (traceIsActive) {std::cout <<"Exit function: " << theFunctionName << std::endl;delete theFunctionName;}
}inline void Trace3::debug(const char* msg)
{if (traceIsActive) {std::cout << msg << std::endl;}
}bool Trace3::traceIsActive = false; // 默認(rèn)設(shè)置為false關(guān)閉跟蹤;可以在test_tracing_war_story中設(shè)置是否開啟還是關(guān)閉跟蹤int addOne3(int x)
{char* name = "addOne3";Trace3 t(name);return x+1;
}int test_tracing_war_story()
{Trace1::traceIsActive = false;/*Trace1實(shí)現(xiàn)就是一個(gè)無用對(duì)象對(duì)性能帶來破壞性影響的實(shí)例:即創(chuàng)建和后面的銷毀預(yù)計(jì)要使用卻沒有使用的不必要的對(duì)象在關(guān)閉跟蹤情況下產(chǎn)生的開銷:引發(fā)一系列計(jì)算:(1). 創(chuàng)建一個(gè)作用域?yàn)閠est_tracing_war_story的string型變量name(2). 調(diào)用Trace1的構(gòu)造函數(shù)(3). Trace1的構(gòu)造函數(shù)調(diào)用string的構(gòu)造函數(shù)來創(chuàng)建一個(gè)string在此函數(shù)的結(jié)尾,Trace1對(duì)象和兩個(gè)string對(duì)象被銷毀:(1). 銷毀string型變量name(2). 調(diào)用Trace1的析構(gòu)函數(shù)(3). Trace1的析構(gòu)函數(shù)為成員string調(diào)用string的析構(gòu)函數(shù)在跟蹤被關(guān)閉的情況下,string的成員對(duì)象從未被使用,Trace1對(duì)象本身也未被使用,所有這些用于對(duì)象的創(chuàng)建和銷毀的計(jì)算都是純碎的浪費(fèi)*/std::string name = "test_tracing_war_story";Trace1 t(name);std::string moreInfo = "more interesting info";t.debug(moreInfo);using namespace std::chrono;high_resolution_clock::time_point timeStart, timeEnd;int count = 1000000;// 通過addOne0和addOne1測(cè)試Trace1對(duì)象的性能開銷timeStart = high_resolution_clock::now();for (int i = 0; i < count; ++i) {addOne0(i);}timeEnd = high_resolution_clock::now();std::cout<< "addOne0 time spen: " <<(duration_cast<duration<double>>(timeEnd-timeStart)).count()<<" seconds"<<std::endl;timeStart = high_resolution_clock::now();for (int i = 0; i < count; ++i) {addOne1(i);}timeEnd = high_resolution_clock::now();std::cout<< "addOne1 time spen: " <<(duration_cast<duration<double>>(timeEnd-timeStart)).count()<<" seconds"<<std::endl;Trace2::traceIsActive = false;// 通過addOne2測(cè)試Trace2對(duì)象的性能開銷timeStart = high_resolution_clock::now();for (int i = 0; i < count; ++i) {addOne2(i);}timeEnd = high_resolution_clock::now();std::cout<< "addOne2 time spen: " <<(duration_cast<duration<double>>(timeEnd-timeStart)).count()<<" seconds"<<std::endl;Trace3::traceIsActive = false;// 通過addOne3測(cè)試Trace3對(duì)象的性能開銷timeStart = high_resolution_clock::now();for (int i = 0; i < count; ++i) {addOne3(i);}timeEnd = high_resolution_clock::now();std::cout<< "addOne3 time spen: " <<(duration_cast<duration<double>>(timeEnd-timeStart)).count()<<" seconds"<<std::endl;return 0;
}} // namespace tracing_war_story
執(zhí)行結(jié)果如下:
GitHub:https://github.com/fengbingchun/Messy_Test
總結(jié)
以上是生活随笔為你收集整理的提高C++性能的编程技术笔记:跟踪实例+测试代码的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二叉树简介及C++实现
- 下一篇: 提高C++性能的编程技术笔记:构造函数和