有時候,為了效率,不得不用比較糟糕的程式寫法,好比說,全域變數 (global variable) 的使用。不過,真的是這樣子嗎?多使用全域變數,真的可以讓程式的執行效率變高嗎?

一般來說,CPU 會有多種定址模式 (addressing mode),可以給 array、pointer 用,難道這些定址模式,沒有增進 array、pointer 使用效率的功能嗎?當效率至上的時候,如果使用全域變數,可以增進效能很多的時候,當然我們可以考慮犧牲維護性 (mantainability)、可讀性 (readability);但如果效能只有增進一點點,那全域變數的使用,就會變成值得商榷思考,慎重考慮的選項了。

所以,我們最好做個實驗,看看到底效能的差異有多少。測試程式如下:

#include <stdio.h>#include <stdlib.h>

#define INDIRECT_ACCESS 1

#define VAR_NUM (1024 * 32)#define LOOP_NUM1 (1024)#define LOOP_NUM2 (1024)

int global_var[VAR_NUM];

struct struct_var_t{    int v[VAR_NUM];};

struct struct_var_t* struct_var;

#if INDIRECT_ACCESS#   define ACCESS_VAR struct_var->v#else#   define ACCESS_VAR global_var#endif

int main(){    int i;    int x, y;

    struct_var = (struct struct_var_t*)calloc(1, sizeof(struct struct_var_t));

    for (i = 0; i < VAR_NUM; ++i) {        ACCESS_VAR[i] = i;    }

    printf("Start working...\n");

    for (x = 0; x < LOOP_NUM1; ++x) {        for (y = 0; y < LOOP_NUM2; ++y) {            for (i = 0; i < VAR_NUM; ++i) {                ACCESS_VAR[i] *= 7;                ACCESS_VAR[i] /= 7;            }        }        fprintf(stderr, ".");    }

    free(struct_var);

    return 0;}

INDIRECT_ACCESS0 的時候,表示使用全域變數存取 array 裡的值;反之,則是使用 pointer 存取 array 裡的值。之所以最終仍是要存取 array 裡的值,是因為我們部門所需要的效率至上的這種程式,大都會是這種運算類型。

測試結果如下表:

Compiler, Platform GCC 3.4.2 (-O2) FreeBSD 5 VMWare/P4-HT GCC 3.4.2 (-O2) FreeBSD 5 Xeon VC 6 (Release) WinXP P4-HT IntelC 8 (Release) WinXP P4-HT GCC 3.3.x (-O2) WinXP P4-HT
Access Method Direct Indirect Direct Indirect Direct Indirect Direct Indirect Direct Indirect
Time spent in user mode (CPU seconds) 384.198s 371.917s 424.729s 411.056s 0.00s 0.00s 0.00s 0.00s 0.00s 0.00s
Time spent in kernel mode (CPU seconds) 10.327s 9.663s 0.281s 0.281s 0.00s 0.00s 0.00s 0.00s 0.00s 0.00s
Total time 6:39.96s 6:27.19s 7:13.80s 6:53.66s 3:39.79s 5:12.62s 3:07.76s 4:04.45s 3:30.34s 4:41.21s
CPU utilisation (percentage) 98.6% 98.5% 97.9% 99.4% 0.0% 0.0% 0.0% 0.0% 0.0% 0.0%
Times the process was swapped 0 0 0 0 (n/a) (n/a) (n/a) (n/a) (n/a) (n/a)
Times of major page faults 0 0 0 0 (n/a) (n/a) (n/a) (n/a) (n/a) (n/a)
Times of minor page faults 118 87 118 87 (n/a) (n/a) (n/a) (n/a) (n/a) (n/a)

從測試結果我們可以發現,GCC 3.4.2 在 FreeBSD 上的表現特別異類,使用 pointer 間接存取 array 值,效率反而比用全域變數要來的好[1]。反而是在 DOS/Windows 上的各種 compiler,使用 pointer 間接存取 array 值的效率都很差,有著明顯的差距。

因為不太可能在 FreeBSD 上跑,多半是在 Windows 上跑,否則便是在 Linux 上跑[2],而在 Windows 上跑,多半會使用 Intel Compiler,因為與 VC6 比起來,平均會有 10% 的效率增進。所以,最後的結論是,除非有架構上的需求,好比說 multi-threading,否則,應該還是會使用全域變數來實作核心功能。


  1. 有可能是 GCC 3.4.x 與 GCC 3.3.x 的差異所造成的。有必要找 GCC 3.4.x for Windows 與 GCC 3.3.x for FreeBSD 來測測看。
  2. 我漏測 Linux 的表現了。