Global variables: to use or not to use?
有時候,為了效率,不得不用比較糟糕的程式寫法,好比說,全域變數 (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_ACCESS 為 0 的時候,表示使用全域變數存取 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,否則,應該還是會使用全域變數來實作核心功能。



2 Comments
通常我用全域變數不是因為速度效率什麼的
而是因為我不想傳參數 XD
我的觀點向來是 global variables 能少用就少用,因為它帶來的問題向來比他的效益還要多。前面的測試程式如果改成:int main()
{
int* local_var = struct_var->v;
#define ACCESS_VAR local_var
...
}程式的效能就跟使用 global variables 相差不多囉。就我的經驗,大部分程式的效率往往取決於設計與演算法,最後只要花點時間優化所謂的熱點 (hot spots),想要有接近 global variables 的效率而無其缺點並非不能達到。
Post a Comment