日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

堆状态分析的利器——valgrind的DHAT

發(fā)布時間:2023/11/27 生活经验 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 堆状态分析的利器——valgrind的DHAT 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

? ? ? ? 在《堆問題分析的利器——valgrind的massif》一文中,我們介紹了如何使用massif查看和分析堆分配/釋放的問題。但是除了申請和釋放,堆空間還有其他問題,比如堆空間的使用率、使用周期等。通過分析這些問題,我們可以對程序代碼進行優(yōu)化以提高性能。本文介紹的工具DHAT——dynamic heap analysis tool就是分析這些問題的利器。(轉(zhuǎn)載請指明出于breaksoftware的csdn博客)

? ? ? ? 不同于massif是在程序結(jié)束時產(chǎn)生報告,DHAT是在程序運行時實時輸出信息的。

? ? ? ? 我們繼續(xù)以《堆問題分析的利器——valgrind的massif》文中末尾的代碼為例

#include <stdlib.h>void* create(unsigned int size) {return malloc(size);
}void create_destory(unsigned int size) {void *p = create(size);free(p);
}int main(void) {const int loop = 4;char* a[loop];unsigned int kilo = 1024;for (int i = 0; i < loop; i++) {const unsigned int create_size = 10 * kilo;create(create_size);const unsigned int malloc_size = 10 * kilo;a[i] = malloc(malloc_size);const unsigned int create_destory_size = 100 * kilo;create_destory(create_destory_size);}for (int i = 0; i < loop; i++) {free(a[i]);}return 0;
}

? ? ? ? 第19行通過create方法申請了10K堆空間,一共申請了4次。整個代碼周期它們都沒釋放,所以存在著內(nèi)存泄漏。

? ? ? ? 第22行通過malloc方法申請了10K堆空間,一共申請了4次。在第29行,通過free方法釋放了這些空間,沒有造成內(nèi)存泄漏。

? ? ? ? 第25行通過create_destory方法申請并使用了100K的空間,所以也沒有內(nèi)存泄漏。

? ? ? ? 我們使用下面指令分析編譯后的結(jié)果

valgrind --tool=exp-dhat ./test

? ? ? ? 得出的結(jié)果如下

==9121== ======== ORDERED BY decreasing "max-bytes-live": top 10 allocators ========
==9121== 
==9121== -------------------- 1 of 10 --------------------
==9121== max-live:    102,400 in 1 blocks
==9121== tot-alloc:   409,600 in 4 blocks (avg size 102400.00)
==9121== deaths:      4, at avg age 35 (0.02% of prog lifetime)
==9121== acc-ratios:  0.00 rd, 0.00 wr  (0 b-read, 0 b-written)
==9121==    at 0x4C2DECF: malloc (in /usr/lib/valgrind/vgpreload_exp-dhat-amd64-linux.so)
==9121==    by 0x10870F: create (test.c:4)
==9121==    by 0x108726: create_destory (test.c:8)
==9121==    by 0x10882C: main (test.c:25)
==9121== 
==9121== -------------------- 2 of 10 --------------------
==9121== max-live:    40,960 in 4 blocks
==9121== tot-alloc:   40,960 in 4 blocks (avg size 10240.00)
==9121== deaths:      none (none of these blocks were freed)
==9121== acc-ratios:  0.00 rd, 0.00 wr  (0 b-read, 0 b-written)
==9121==    at 0x4C2DECF: malloc (in /usr/lib/valgrind/vgpreload_exp-dhat-amd64-linux.so)
==9121==    by 0x10870F: create (test.c:4)
==9121==    by 0x1087EE: main (test.c:19)
==9121== 
==9121== -------------------- 3 of 10 --------------------
==9121== max-live:    40,960 in 4 blocks
==9121== tot-alloc:   40,960 in 4 blocks (avg size 10240.00)
==9121== deaths:      4, at avg age 454 (0.32% of prog lifetime)
==9121== acc-ratios:  0.00 rd, 0.00 wr  (0 b-read, 0 b-written)
==9121==    at 0x4C2DECF: malloc (in /usr/lib/valgrind/vgpreload_exp-dhat-amd64-linux.so)
==9121==    by 0x108808: main (test.c:22)
==9121== 
==9121== 
==9121== 
==9121== ==============================================================

? ? ? ? 第4到11行顯示了create_destory方法導(dǎo)致堆變化的信息。

? ? ? ? 第4行意思是分配的最大一個區(qū)塊大小是100K。

? ? ? ? 第5行意思是一共分配了4個區(qū)塊,一共400K,平均每個區(qū)塊100K。

? ? ? ? 第6行意思是釋放了4次堆上空間,平均生命周期是35,占程序運行時間的0.02%。

? ? ? ? 第7行意思是這些區(qū)塊被讀寫了0次,讀寫的空間也是0。

? ? ? ? 第8到11行是調(diào)用堆棧。

? ? ? ? 類似的我們可以解讀14到20行(代碼第19行的create)、23到28行(代碼第22行的malloc)的信息。

? ? ? ? 我們注意下第16行信息,其意思是create方法申請的堆沒有一次釋放,所以釋放的空間大小是0。這對我們動態(tài)分析程序執(zhí)行比較有意義,可以借此檢查相應(yīng)代碼是否發(fā)生了內(nèi)存泄漏。

? ? ? ? 再注意下第6行和第25行,可以看到create_destory和main中直接malloc申請的堆的生命周期分別占比是0.02%和0.32%。這也是符合我們代碼的設(shè)計的,因為create_destory方法申請的空間立即被釋放掉了,所以它們的生命周期短點。而main中第22行malloc的空間存在一段時間之后才在第29行被釋放掉,所以它們的生命周期長點。這個信息也非常有意義。因為一個函數(shù)申請的堆頻繁被快速釋放,其表現(xiàn)就是它們的生命周期變短,那么此時使用堆是否合適,或者說這種頻繁釋放堆空間的行為是否合適?

? ? ? ? 最后我們注意下第7,17和26行,它們顯示這些堆被讀寫的情況。這個信息也非常重要。因為通過讀寫情況可以分析出堆空間的特點,比如是讀多還是寫多,這在多線程編程下,可能可以優(yōu)化堆中存儲的結(jié)構(gòu)或者管理這段堆的鎖粒度。還可以通過讀寫情況分析出這個堆空間是否存在不被使用的情況,從而可以優(yōu)化掉對應(yīng)的代碼?;蛘咄ㄟ^對堆數(shù)據(jù)寫入的多少,來分析申請這么大的空間是否合適。

? ? ? ? 現(xiàn)在我們開始以觀察到的現(xiàn)象來分析和優(yōu)化代碼。

頻繁申請和釋放堆

? ? ? ? 現(xiàn)象如下

==10111== -------------------- 1 of 10 --------------------
==10111== max-live:    1,024 in 1 blocks
==10111== tot-alloc:   4,096 in 4 blocks (avg size 1024.00)
==10111== deaths:      4, at avg age 170 (0.12% of prog lifetime)
==10111== acc-ratios:  0.00 rd, 1.06 wr  (0 b-read, 4,352 b-written)
==10111==    at 0x4C2DECF: malloc (in /usr/lib/valgrind/vgpreload_exp-dhat-amd64-linux.so)
==10111==    by 0x108703: main (test.c:8)

? ? ? ? 這段信息顯示:一共申請的4個堆空間生命周期只占比0.12%——非常低。被申請的空間只被寫入,從來沒被讀取過。堆空間的使用率(當(dāng)前只有寫操作)也不高,只有1.06次寫入(4352/4096)。我們看下代碼

#include <stdlib.h>
#include <string.h>int main(void) {const int loop_count = 4;const unsigned int kilo = 1024;for (int i = 0; i < loop_count; i++) {char* buf = malloc(kilo);memset(buf, 'a', kilo);free(buf);}return 0;
}

? ? ? ? 第8行申請的堆,在第9行被使用后,立即被第10行釋放掉。頻繁申請和釋放堆會造成內(nèi)存碎片等問題,所以一般我們都是希望堆的使用率是是比較高的。發(fā)現(xiàn)和定位問題后,我們可以做如下改動

#include <stdlib.h>
#include <string.h>int main(void) {const int loop_count = 4;const unsigned int kilo = 1024;char* buf = malloc(kilo);for (int i = 0; i < loop_count; i++) {memset(buf, 'a', kilo);}free(buf);return 0;
}

? ? ? ? 得到如下分析結(jié)果

==10119== max-live:    1,024 in 1 blocks
==10119== tot-alloc:   1,024 in 1 blocks (avg size 1024.00)
==10119== deaths:      1, at avg age 602 (0.43% of prog lifetime)
==10119== acc-ratios:  0.00 rd, 4.25 wr  (0 b-read, 4,352 b-written)
==10119==    at 0x4C2DECF: malloc (in /usr/lib/valgrind/vgpreload_exp-dhat-amd64-linux.so)
==10119==    by 0x1086FA: main (test.c:7)

? ? ? ? 可以發(fā)現(xiàn)堆的生命周期提高到了0.43%,使用率提高到了4.25。

使用堆是否合理

? ? ? ? 上面例子也沒必要使用堆,我們可以把其變成棧上空間,這樣執(zhí)行效率更高。

#include <stdlib.h>
#include <string.h>int main(void) {const int loop_count = 4;const unsigned int kilo = 1024;char buf[kilo];for (int i = 0; i < loop_count; i++) {memset(buf, 'a', kilo);}return 0;
}

是否有沒有使用的堆

? ? ? ? 沒有使用的堆是指:堆空間被申請出來,但是沒有對其進行過讀寫操作。我們看個現(xiàn)象

==10309== -------------------- 1 of 10 --------------------
==10309== max-live:    1,024 in 1 blocks
==10309== tot-alloc:   1,024 in 1 blocks (avg size 1024.00)
==10309== deaths:      1, at avg age 664 (0.47% of prog lifetime)
==10309== acc-ratios:  0.00 rd, 4.25 wr  (0 b-read, 4,352 b-written)
==10309==    at 0x4C2DECF: malloc (in /usr/lib/valgrind/vgpreload_exp-dhat-amd64-linux.so)
==10309==    by 0x1086FA: main (test.c:7)
==10309== -------------------- 2 of 10 --------------------
==10309== max-live:    1,024 in 1 blocks
==10309== tot-alloc:   1,024 in 1 blocks (avg size 1024.00)
==10309== deaths:      1, at avg age 602 (0.43% of prog lifetime)
==10309== acc-ratios:  0.00 rd, 0.00 wr  (0 b-read, 0 b-written)
==10309==    at 0x4C2DECF: malloc (in /usr/lib/valgrind/vgpreload_exp-dhat-amd64-linux.so)
==10309==    by 0x108709: main (test.c:8)

? ? ? ? 第5行顯示這個堆被寫入過,第12行顯示這個堆從來沒有被讀寫過。這樣我們就需要懷疑test.c的第8行申請的空間是否必要??聪麓a,可以發(fā)現(xiàn)這個堆的確沒必要申請。

#include <stdlib.h>
#include <string.h>int main(void) {const int loop_count = 4;const unsigned int kilo = 1024;char* buf = malloc(kilo);char* unused = malloc(kilo);for (int i = 0; i < loop_count; i++) {memset(buf, 'a', kilo);}free(unused);free(buf);return 0;
}

申請的大小是否合理

? ? ? ? 有時候,我們并不是那么節(jié)約。比如一個只需要1K的空間,我們可能申請了10K。但是這個單純的靠梳理代碼可能比較難以發(fā)現(xiàn)。我們看個分析結(jié)果

==10571== -------------------- 1 of 10 --------------------
==10571== max-live:    1,024 in 1 blocks
==10571== tot-alloc:   1,024 in 1 blocks (avg size 1024.00)
==10571== deaths:      1, at avg age 134 (0.09% of prog lifetime)
==10571== acc-ratios:  0.00 rd, 0.00 wr  (0 b-read, 8 b-written)
==10571==    at 0x4C2DECF: malloc (in /usr/lib/valgrind/vgpreload_exp-dhat-amd64-linux.so)
==10571==    by 0x1086AA: main (test.c:7)
==10571== 
==10571== Aggregated access counts by offset:
==10571== 
==10571== [   0]  1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 
==10571== [  16]  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
==10571== [  32]  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
==10571== [  48]  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

? ? ? ? 從第9行開始,顯示的是該堆每個字節(jié)被訪問的次數(shù)。我們看到只有前8個字節(jié)被訪問了1次。但是剩余的其他字節(jié)都沒有被訪問過。于是我們看下代碼

#include <stdlib.h>
#include <string.h>int main(void) {const int loop_count = 8;const unsigned int kilo = 1024;char* buf = malloc(kilo);for (int i = 0; i < loop_count; i++) {buf[i] = 'a' + i;}free(buf);return 0;
}

? ? ? ? 可以看到,第8到10行的確只操作了前8個字節(jié)。所以我們沒必要申請1K的空間。

總結(jié)

以上是生活随笔為你收集整理的堆状态分析的利器——valgrind的DHAT的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。