你應該知道的C語言性能提升法之結構體優化


你應該知道的C語言性能提升法之結構體優化

前兩天碼哥寫了一篇《你應該知道的C語言Cache命中率提升法》的文章,講述關於地址連續性帶來的cache命中率提升,感興趣的朋友可以先翻看一番。

今天的文章是關於如何優化結構體成員來提升cache命中率的。我們先來看一個例子:

代碼一

<code>/* a.c */#include <stdio.h>#include typedef struct test_s {    long i0;    char padding0[1024];    long i1;    char padding1[1024];    long i2;    char padding2[1024];    long i3;    char padding3[1024];    long i4;    char padding4[1024];    long i5;    char padding5[1024];    long i6;    char padding6[1024];    long i7;    char padding7[1024];    long i8;    char padding9[1024];    long i9;} test_t;int main(void){    test_t arr[512];    int i;    struct timeval begin, end;    gettimeofday(&begin, NULL);    for (i = 0; i < sizeof(arr)/sizeof(test_t); ++i) {        arr[i].i0 = 0;        arr[i].i1 = 1;        arr[i].i2 = 2;        arr[i].i3 = 3;        arr[i].i4 = 4;        arr[i].i5 = 5;        arr[i].i6 = 6;        arr[i].i7 = 7;        arr[i].i8 = 8;        arr[i].i9 = 9;    }    gettimeofday(&end, NULL);    printf("%lu(us)\\n", (end.tv_sec*1000000+end.tv_usec)-(begin.tv_sec*1000000+begin.tv_usec));    return 0;}/<stdio.h>/<code>

功能很簡單,我們定義了一個結構體,其中有很多padding,這些padding是用來模擬日常項目中不常訪問的結構體成員。然後我們定義了這樣一個結構體數組,順序訪問每個結構體,並將其中的整型成員進行賦值,並度量這一循環的時間開銷。

碼哥的測試機上,執行的結果大約是:

<code>$ ./a2487(us)/<code>

參考我們之前的那篇關於地址連續性帶來cache命中率提升想法,上面這個例子是否有性能提升的空間呢?

答案當然是有的。請看下面的代碼:

代碼二

<code>/* b.c */#include <stdio.h>#include typedef struct test_s {    long i0;    long i1;    long i2;    long i3;    long i4;    long i5;    long i6;    long i7;    long i8;    long i9;    char padding0[1024];    char padding1[1024];    char padding2[1024];    char padding3[1024];    char padding4[1024];    char padding5[1024];    char padding6[1024];    char padding7[1024];    char padding8[1024];} test_t;int main(void){    test_t arr[512];    int i;    struct timeval begin, end;    gettimeofday(&begin, NULL);    for (i = 0; i < sizeof(arr)/sizeof(test_t); ++i) {        arr[i].i0 = 0;        arr[i].i1 = 1;        arr[i].i2 = 2;        arr[i].i3 = 3;        arr[i].i4 = 4;        arr[i].i5 = 5;        arr[i].i6 = 6;        arr[i].i7 = 7;        arr[i].i8 = 8;        arr[i].i9 = 9;    }    gettimeofday(&end, NULL);    printf("%lu(us)\\n", (end.tv_sec*1000000+end.tv_usec)-(begin.tv_sec*1000000+begin.tv_usec));    return 0;}/<stdio.h>/<code>

可以看到,這段代碼中出了結構體中成員的位置有所調整外,其餘代碼都是一致的,甚至結構體的大小都是一樣的。

那麼這段代碼的執行時間又是如何的呢?

<code>$ ./b1034(us)/<code>

可以看到這個結果比代碼一快了1倍左右。

總結

為何會快出1倍,原因與地址連續性依舊有關。代碼二中,常被訪問的10個整型成員被安排在了一起,這樣當訪問其中一個時,可以儘可能多的將可能被訪問的成員預加載到cache中。而代碼一中,由於間隔了很多padding,且每個padding也比較大,因此cache緩存了很多不常被訪問的部分,所以在我們給每一個整型賦值時都無法利用到前一次賦值的cache緩存,因此效率有所降低。

結論很簡單,

儘可能將常訪問的結構體成員放在一起,甚至推薦貼近結構體開始處存放。


碼哥在這裡給大家拜年啦,祝大家在新的一年裡,身體健康,家庭美滿,事業有成,走上巔峰。

另外,最近疫情也比較嚴峻,大家出門不要忘記做好必要的防護措施。


分享到:


相關文章: