OI中C 的常數優化詳解

2021-10-02 21:36:35 字數 4089 閱讀 5853

ppt版本

常數優化有效性測試根據前文,我們有必要評估函式的耗時,從而進行有效的優化。

使用標頭檔案cti

me

ctime

ctime或tim

e.

htime.h

time.h

中的clock()函式,此函式返回當前的總執行時間。

樣例**:

#include

int tmp=

clock()

;function()

;int _time=

clock()

-tmp;

這段**執行後,_time變數就表示函式function()的執行時間。

其單位為:ms(毫秒)

為了保證測量精確度,因為對於耗時較小的函式_time可能會為0,所以我們應該多次執行該函式取它們的總時間並求平均值。

這種方法與測量紙的厚度的方法是一樣的。用for迴圈實現。

各型別比較:

int運算是最快的。unsigned無符號數做除法比有符號數快。

float比任何整數運算更慢,double比float慢,long double最慢。

注意:實測int比char和short具有更快的速度。

正如part01所說,不同的資料型別以及它們的各運算速度是不一樣的。

3.1.1 一般情況的運算優化

1.優化除法/取模運算。例:將a/b==c/d化為a\*d==c\*b,事實上,這對於整數來說也能夠保持精度。另外,unsigned無符號數比有符號數能夠更快地完成除法運算。而對於取模,可以將其優化為:

inline int mod(int x)3.2 記憶體優化

內容較多,如有需要請移步c++卡常數之記憶體優化

3.3 分支優化

分支語句判斷會影響cpu的流水線執行模式,消耗多餘時間。

三目和短路運算子可以優化它。

if

(maxn(maxn=x);if

(a==b)

work()

;化為a==b&&

(work()

,0);

另外,請將最可能的情況放置在if中而不是else中,並且盡量讓自己的分支語句合理一些,不具有太大的不確定性。

為啥?因為編譯器會進行分支**,這樣做能夠配合編譯器做這個優化。

單獨乙個if時不會對效率造成太大影響,但ifelse的開銷較大。三目主要的作用是優化ifelse語句。

例如void func()}是不良好的習慣。沒有必要的else語句應該略去,否則一方面影響效率,一方面使**不夠美觀。

3.4 迴圈展開與多路並行

由於展開能夠消除分支以及一些管理歸納變數的**,因此可以減少一些分支開銷。一些情況下,迴圈展開甚至能夠促使cpu併發。

注:展開過度會使迴圈體無法放入跟蹤快取(tc),造成負優化!

多路並行能取消上下文關聯性,從而有機會使cpu併發。同樣地,不可以過度並行。它一般是與迴圈展開配合使用的。

乙個例子:

inline

intsum

(int a,

int len)

這是乙個求陣列和的函式。這裡使用了迴圈展開+四路並行。

wc2017t2就使用過這種優化方法。

3.5 讀寫優化

介紹兩個函式:

size_t fread(void *buffer,size_t size,size_t count,file *stream)

size_t fwrite(const void *buffer,size_t size,size_t count,file *stream)

fread讀,fwrite寫。buffer為讀寫陣列,size為每次讀寫的位元組數,count為讀寫的總位元組數,stream為流函式。

快速讀寫模板:

static

char in[

10000000

],out[

10000000

],ch[20]

,*p=in,

*q=out,

*t=ch;

inline

intread()

inline

void

write

(int x)

}//注意:如果讀入量很大(比如超過了10000000位元組),請使用下面的模板。

static

char in[

10000000],

*p,*pp,out[

10000000],

*q=out,ch[20]

,*t=ch;

//讀寫大小自行調整。

#define getch p==pp&&(pp=(p=in)+fread(in,1,10000000,stdin),p==pp)?eof:*p++;

inline

intread()

inline

void

write

(int x)

}int

main()

另外乙個功能齊全但是常數較大的版本請參見部落格:

請讀者自行優化其常數。

注意因為fread的引數太大也會降低速度。因此在檔案讀寫的情況下可以這樣:

freopen

(filename,」r」,

stdin);

file* fp=

fopen

(filename,」r」)

;fseek

(fp,0l,

seek_end);

fread

(in,1,

ftell

(fp)

,stdin

);

3.6 stl容器優化

如果能手寫盡量手寫,如果不能手寫則優化其記憶體分配。

眾所周知,stl的預設分配器是allocator,這是會動態分配記憶體的,我們可以開乙個足夠大的記憶體池,自定義myalloc類來優化記憶體分配的消耗。

myalloc的定義如下。

#include

using

namespace std;

#define reg register

static

char space[

10000000],

*sp=space;

template

<

typename t>

struct myalloc:allocator

template

<

typename t2>

myalloc

(const myalloc

&a)template

<

typename t2>

myalloc

&operator=(

const myalloc

&a)template

<

typename t2>

struct rebind

;inline t*

allocate

(size_t n)

inline

void

deallocate

(t* p,size_t n)

};

完成後,需要這樣定義stl容器:

例:

list<

int,myalloc<

int>

> l;vector<

double

,myalloc<

double

>

> vec;

實測:對於list,在我的電腦上甚至比不加優化的list快10倍以上。

3.7 其他

例如:逗號運算子比分號運算子快,for(register int i=1;i<=n;++i)…比while(n–)…慢,因為少了i的變數列舉。

另外感謝@ywr8 補充:for(;n;–n)比while(n–)快。

OI中C 常數問題及其優化

常數是個謎,卡常是件很煩的事,被常數坑死的oier已經不少了 常數不可避免,但是可以理性地去優化 當時間複雜度已經難以優化時,考慮常數優化 i o讀入和輸出 如果量小倒也沒什麼,如果大規模讀入或者輸出,c 自帶的方式是很慢的 首先,拒絕cin cout,實在是太慢了,受不了 接著scanf prin...

神奇的常數優化

話說noip不開優化,那我們來看看開優化後會發生什麼 乙個簡單的累加 include include 計時看看差別 includeusing namespace std int main 按理來說,上面的兩種方法差不多 在devc 5.7.1中,xp系統 學校機房的那種 不開任何優化 大約前一種是8...

神奇的常數優化

話說noip不開優化,那我們來看看開優化後會發生什麼 乙個簡單的累加 include include 計時看看差別 includeusing namespace std intmain 按理來說,上面的兩種方法差不多 在devc 5.7.1中,xp系統 學校機房的那種 不開任何優化 大約前一種是85...