簡體   English   中英

為什么在我的測試程序中,malloc比靜態內存分配快?

[英]Why malloc is faster than static memory allocation in my test program?

我有一個測試程序。 我在ubuntu可信64位中執行此結果。

分配時間:9571

靜態時間:45587

為什么malloc比靜態內存分配快,或者我的測試程序錯了?

測試程序是這樣的。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#define TIME 10000

int data[1024] = { 1,2,3,4,5,6,6,7,8,5,4,3,2,3 };
int st[TIME][1024];
int main(void) {
    int i = 0;
    int time = 0;
    struct timeval tv1,tv2;

    /* test for malloc */
    memset(&tv1,0,sizeof(tv1));
    memset(&tv2,0,sizeof(tv2));
    gettimeofday(&tv1,NULL);
    for(i=0;i<TIME;i++) {
        void * p = malloc(4096);
        memset(p,0,4096);
        memcpy(p,data,sizeof(data));
        free(p);
        p = NULL;
    }
    gettimeofday(&tv2,NULL);
    time = ((tv2.tv_sec - tv1.tv_sec) * 1000000 +
        (tv2.tv_usec - tv1.tv_usec));
    printf("malloc time:%d\n",time);

    /* test for static memory allocation */
    memset(&tv1,0,sizeof(tv1));
    memset(&tv2,0,sizeof(tv2));
    gettimeofday(&tv1,NULL);
    for(i=0;i<TIME;i++) {
        memset(st[i],0,4096);
        memcpy(st[i],data,sizeof(data));
    }
    gettimeofday(&tv2,NULL);
    time = ((tv2.tv_sec - tv1.tv_sec) * 1000000 +
        (tv2.tv_usec - tv1.tv_usec));
    printf("static time:%d\n",time);

    return 0;
}

該基准實質上是沒有意義的,因為它所測量的大部分與兩個內存區域的使用關系不大。

當程序啟動時(即開始執行main時),尚未初始化默認初始化的數據段(即40 MB的st[40000][1024] )。 虛擬內存地址已被標記為用於延遲映射到零初始化的內存,但是直到程序實際嘗試引用這些地址時,這種情況才會發生。 每個此類引用都需要內核干預才能調整虛擬內存映射(以及對物理內存頁面進行零初始化)。 干預之后,將映射該虛擬內存頁面,但不會映射同一數據段中的任何其他頁面。 因此,當您越過st數組時,將生成大量的頁面錯誤,每個頁面錯誤都需要花費大量時間。 您在“靜態內存”測試中測量的大部分時間都包含這些內核陷阱。

另一方面,第一次調用malloc將導致標准庫初始化內存分配系統。 盡管該初始化不太復雜,但它也不是免費的。 因此,大多數情況下,您在“分配的內存”測試中進行的測量都由該初始化組成。

為了進行有意義的基准測試,您需要在開始測量之前確保所有懶惰初始化均已完成。 一種方法是在同一可執行文件中多次執行基准測試,並丟棄前幾次(或前幾次)重復。

作為說明,我通過在main的所有內容(return語句除外)周圍添加以下循環來輕松修改基准測試:

for (int reps = 0; reps < 4; ++reps) {
  printf("Repetition %d\n", reps);
  /* Body of main goes here */
}

結果為以下輸出:

Repetition 0:
malloc time:9584
static time:26923
Repetition 1:
malloc time:2467
static time:4360
Repetition 2:
malloc time:2463
static time:4332
Repetition 3:
malloc time:2413
static time:4609

注意在“熱身”迭代(重復0)中測量的時間與其余時間之間的差異。

在動態和靜態內存測試之間仍然存在差異。 在這里,值得注意的是,這兩個測試以不同的方式使用內存。 malloc測試(可能)在每次迭代中都重復使用相同的緩沖區,因為free內存塊后,下一個具有該大小的malloc可能會立即返回它。 另一方面,靜態內存測試會循環訪問整個40MB分配空間。 一個更好的比較將是要么malloc相同大小的緩沖器st使用相同的代碼作為靜態測試和循環通過它,或使st的1024個整數單個載體(如malloc的),並使用相同的代碼重用作為malloc測試。 換句話說,要比較兩種可能的方法,請最小化兩者之間的差異。 (我將其保留為練習。)

如果做出建議的更改,您可能會發現差異減少了。 但是有可能會存在一些一致(但很小)的差異,這將反映出難以控制的兩個循環之間的差異,包括代碼和數據的對齊方式以及CPU緩存的詳細信息。 這些細微的差別將屬於“巧合”類別,如@seb對這個問題的回答所明確指出的 (盡管我認為了解基准測試中可避免的陷阱很重要,但我要強調,@ seb在該問題上的建議無疑是正確的。)

巧合不是因果關系。 您的CPU代碼緩存可能很好地適合了第一個循環(測試malloc ),然后需要將第二個循環從較慢的主內存中提取到較快的代碼緩存中,這會給您第二個計時帶來偏差。

可能會有類似的數據緩存,這意味着在整個持續時間內,靜態對象st緩存速度將比malloc分配的內存快(反之亦然)。 重點應該放在威力上 不需要是這種情況。 正如您所注意到的,純屬巧合,一個比另一個快。

您不應該通過滾動自己的探查器來測試理論瓶頸的速度來執行瓶頸分析,例如確定更快的存儲持續時間,因為這只會得出結論,贊成在整個解決方案中進行凌亂的過早優化,而不是大多數干凈的代碼可能有些混亂。

相反,您應該專注於使用干凈,可維護的代碼解決實際問題。 一旦有了可以解決實際問題的程序,就應該使用探查器來確定它是否足夠快,如果不能,那么確定代碼的哪些部分可以優化,以便可以使大部分代碼保持干凈。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM