一般的malloc實現(xiàn),對一塊已分配的內(nèi)存,都有兩個機器字的簿記,甚至更多。如果不需要排錯,理論上講,只需要一個字長的額外開銷,用來記錄這塊內(nèi)存的尺寸(放在intptr[-1]處是個好主意)。
為什么需要這個開銷呢?因為free傳入的只是個指針,它不知道要釋放多大的內(nèi)存,因此free內(nèi)部必須通過某種方式來獲得這塊內(nèi)存的尺寸。
可以想象,如果用 malloc/free 來作為一個關(guān)聯(lián)數(shù)組(map)的分配器,要浪費不少內(nèi)存。不過好在實際數(shù)據(jù)的尺寸往往比額外消耗要大很多,相比起來,浪費的比例不算很大,況且現(xiàn)在內(nèi)存還很便宜。
其實,打造一個高效的分配器并不難,難的是它的適用范圍(多線程?cell尺寸,chunk尺寸,對齊,排錯…),如果可以忍受這些缺陷,或者說是限制,還是比較值得的。下一步就是它的靈活性——讓它可以更加容易集成進其它系統(tǒng)。
對于C標準庫,如果能增加一個/一族這樣的分配器,還是很有價值的。從理論上講,只要free時多傳一個size參數(shù),就可以完全去掉額外的開銷。這樣兩個函數(shù)就可以做到:
這樣做還有一個額外的好處,就是可以更好地對齊,假定程序需要按32字節(jié)對齊,malloc/free 就至少需要32字節(jié)做簿記,如果再加上內(nèi)存越界檢測,就需要64字節(jié)。salloc/sfree則只需要將分配的內(nèi)存對齊到32字節(jié)邊界即可。
但是這對程序的正確性要求很高,malloc/free中,內(nèi)存越界檢測可以很容易實現(xiàn),而salloc/sfree就完全做不到(除非增加額外簿記)。一個好主意是可以在debug版中加入這些差錯功能,而在release版中去掉。
更好(確切地講應(yīng)該是更靈活)的方案是,實現(xiàn)一個
而讓 salloc/sfree簡單地作為 mpool 的包裝。
gcc的std::allocator基本上是按這樣的方式實現(xiàn)的,只不過,它的size參數(shù),大多數(shù)時刻是自動傳遞的(知道具體的class/struct,也就知道它的尺寸)。實現(xiàn)方式上,使用 size_aligned/align 作為索引去訪問特定尺寸的mempool,一個 mempool 是多個鏈表串起來的大chunk,每個chunk內(nèi)部是鏈表穿起來的cell。這也許是最好的實現(xiàn)方式了,除了節(jié)省的額外空間開銷,時間開銷上,如果不考慮加鎖,一次alloc平均可以在10時鐘周期內(nèi)完成,dealloc用的時間更短。相比之下malloc/free耗的時間也要多得多。