contributed by < vasv75182366
>
$uname -a
Linux uscca22-Aspire-M5810 4.4.0-96-generic #119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
$lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.3 LTS
Release: 16.04
Codename: xenial
$lscpu
Architecture: x86_64
CPU 作業模式: 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
每核心執行緒數:2
每通訊端核心數:4
Socket(s): 1
NUMA 節點: 1
供應商識別號: GenuineIntel
CPU 家族: 6
型號: 30
Model name: Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz
製程: 5
CPU MHz: 1200.000
CPU max MHz: 2801.0000
CPU min MHz: 1200.0000
BogoMIPS: 5586.03
虛擬: VT-x
L1d 快取: 32K
L1i 快取: 32K
L2 快取: 256K
L3 快取: 8192K
NUMA node0 CPU(s): 0-7
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf pni dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm tpr_shadow vnmi flexpriority ept vpid dtherm ida
$free
total used free shared buff/cache available
Mem: 16423308 2705868 13140196 102348 577244 13297076
置換: 999420 0 999420
照著perf 原理和實務的步驟練習指令
a
進一部做分析。$ perf top -p $pid
$ perf list
cache-misses
、cache-references
、instructions
、cycles
這四個 event。$ perf stat --repeat <num> -e <event> <bin>
按照步驟先 clone 專案再執行 make
編譯
執行make run
後的輸出:
size of entry : 136 bytes
execution time of append() : 0.068812 sec
execution time of findName() : 0.005673 sec
3
觀察主程式流程:
紀錄 append 起始時間
→讀取資料並 append 建 Singly Linked List
→紀錄 append 結束時間
→紀錄 findName 起始時間
→紀錄 findName 結束時間
→釋放記憶體
在 append 以及 findName 是要進行優化的地方,注意到在程式最後沒有妥善釋放記憶體:
if (pHead->pNext) free(pHead->pNext);
free(pHead);
先對 main.c 根據 phonebook_orig.h 的資料結構做修改:
entry *tmp;
while (pHead->pNext) {
tmp = pHead->pNext;
free(pHead);
pHead = tmp;
}
free(pHead);
執行
$ make cache-test
查看未優化過的 cache-misses:
Performance counter stats for './phonebook_orig' (100 runs):
1,349,155 cache-misses # 93.717 % of all cache refs ( +- 0.10% )
1,439,606 cache-references ( +- 0.14% )
326,375,105 instructions # 1.18 insns per cycle ( +- 0.01% )
277,271,600 cycles ( +- 0.11% )
0.090609402 seconds time elapsed ( +- 0.33% )
參考 HMKR 的共筆修改 Makefile 內的 cache-test 讓資料更新
typedef struct __PHONE_BOOK_DETAIL {
char firstName[16];
char email[16];
char phone[10];
char cell[10];
char addr1[16];
char addr2[16];
char city[16];
char state[2];
char zip[5];
} detail;
typedef struct __PHONE_BOOK_ENTRY {
char lastName[MAX_LAST_NAME_SIZE];
detail *pDetail;
struct __PHONE_BOOK_ENTRY *pNext;
} entry;
Performance counter stats for './phonebook_opt' (100 runs):
334,623 cache-misses # 79.267 % of all cache refs ( +- 0.41% )
422,146 cache-references ( +- 1.21% )
287,704,114 instructions # 1.45 insns per cycle ( +- 0.01% )
197,902,294 cycles ( +- 0.50% )
0.067796114 seconds time elapsed ( +- 0.72% )
降低 entry 的大小可以使 cache 內存放更多筆 entry 降低 cache-misses
根據前面已修改過的資料結構增加 Hash Table 的查詢功能,另外新增了兩個檔案 phonebook_hash.c
、 phonebook_hash.h
, findName 以及 append 兩個函式都沒有做變動,僅增加運算雜湊值的函式:
BKRD Hash
unsigned int BKDRHash(char *str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
unsigned int hash = 0;
while (*str) {
hash = hash * seed + (*str++);
}
return (hash & 0x7FFFFFFF) % HASH_TABLE_SIZE;
}
以及
#define HASH 1
用來在 main.c
內根據 #ifdef HASH
來做差別運算
修改 Makefile
、 calculate.c
、 runtime.png
以新增 hash 過後的數據進行比較
以下是使用 hash table size 為 1024 的 BKDRHash 結果
Performance counter stats for './phonebook_hash' (100 runs):
899,631 cache-misses # 60.016 % of all cache refs ( +- 0.31% )
1,498,980 cache-references ( +- 0.30% )
319,910,641 instructions # 1.05 insns per cycle ( +- 0.01% )
306,092,376 cycles ( +- 0.16% )
0.102225082 seconds time elapsed ( +- 0.46% )
Cache miss 明顯從 80% 下降至 60%
在 append 所需的時間由於要進行 hash 明顯上升,但是在 findName 的時間明顯可以透過查表大幅下降
dictionary/words.txt
內的資料來看,看起來並不太具有代表性,因為其中包含了如 zzzzzz
、zzz
之類的非英文姓氏的單字,並且在真實生活中名字會因各種情況而有其他差異,例如:部份名字較為熱門常見、根據地區或可能有所不同。