看遊戲引擎架構內存管理有個地方不太清楚?
書上寫的對齊分配,說使用調整的那幾個位元組來儲存調整量(adjustment):
void* allocateAligned(U32 size_bytes,U32 alignment)
{
ASSERT(alignment&>1);//計算總共要分配的內存
U32 expandedSize_bytes = size_bytes + alignment;
//分配未對齊內存塊,並轉換成U32類型
U32 rawAddress = (U32)allocateUnaligned(expandedSize_bytes);//使用掩碼去除地址低位部分,計算「錯位」量,從而計算調整量
U32 mask = alignment - 1;
U32 misalignment = rawAddress mask;
U32 adjustment = alignment - misalignment;//計算調整後的地址,並把它以指針類型返回
U32 alignedAddress = rawAddress + adjustment;//把alignment儲存在調整後地址的前4個位元組
U32 *pAdjustment = (U32*)(alignedAddress - 4);
*pAdjustment = adjustment;return (void*)alignedAddress;
}//釋放函數
void freeAligned(void*p)
{
U32 alignedAddress = (U32)p;
U8* pAdjustment = (U8*)(alignedAddress - 4);
U32 adjustment = (U32)*pAdjustment;U32 rawAddress = alignedAddress - adjustment;
freeUnaligned((void*)rawAddress);
}
第一個分配函數的這句話感覺不太懂:
//把alignment儲存在調整後地址的前4個位元組
U32 *pAdjustment = (U32*)(alignedAddress - 4);
*pAdjustment = adjustment;
這裡把地址前移4位元組,再往裡面寫入一個U32類型的數據,萬一adjustment小於4位元組呢?比如adjustment只後移了一位元組,那再前移4位元組豈不是訪問其他的內存區域了?再往裡面寫入數據不會破壞掉其他的內存數據?
我看後面的讀取那個adjustment的時候是使用U8數據類型去讀取的,基本可以理解,就是在想前面是不是會破壞到不屬於分配到內存的數據?
////////那些翻譯都來自@Milo Yip版權都是他的另外,此書第二版更新了這些代碼,那個對齊分配函數變成了這樣:
// Aligned allocation function. IMPORTANT: "alignment"
// must be a power of 2 (typically 4 or 16).
void* allocateAligned(size_t size_bytes, size_t alignment)
{
ASSERT(alignment &>= 1);
ASSERT(alignment &<= 128); ASSERT((alignment (alignment - 1)) == 0); // pwr of 2 // Determine total amount of memory to allocate. size_t expandedSize_bytes = size_bytes + alignment; // Allocate unaligned block convert address to uintptr_t. uintptr_t rawAddress = reinterpret_cast&(
allocateUnaligned(expandedSize_bytes));
// Calculate the adjustment by masking off the lower bits
// of the address, to determine how "misaligned" it is.
size_t mask = (alignment - 1);
uintptr_t misalignment = (rawAddress mask);
ptrdiff_t adjustment = alignment - misalignment;
// Calculate the adjusted address.
uintptr_t alignedAddress = rawAddress + adjustment;
// Store the adjustment in the byte immediately
// preceding the adjusted address.
ASSERT(adjustment &< 256); //這個地方改成了這樣 U8* pAlignedMem = reinterpret_cast&(alignedAddress);
pAlignedMem[-1] = static_cast&(adjustment); return static_cast&
(pAlignedMem);
}
天啊是誰邀請的我......這代碼我太熟悉了因為SIMD的關係天天跟內存對齊打交道:
這段代碼不知是哪裡來的,但是有嚴重的問題,既然用了ASSERT,那應該檢測一下alignment,因為以下代碼中:
....................
U32 mask = alignment - 1;
U32 misalignment = rawAddress mask;
....................
用了個小技巧,即 一個數x對a求余mod(x,a),等同於 x(a-1),計算起來飛快! 但是唯一的問題是
這個小技巧能成立,必須保證a是power of 2 ! 不然一切全是錯的,所以函數一開始的斷言應該檢查一下:ASSERT(alignment&>1);
ASSERT(isPowerOfTwo(alignment)); //應該加一句,如果有isPowerOfTwo這個函數
所以,如果輸入非power of 2的數,比如3啊,5啊1啊的,這個函數會完全無法正常工作,於是乎
就不存在題主所問"萬一adjustment小於4位元組呢"的問題了,adjustment必須是4,8,16....這些.
//---------------------------------- 更新答案 -------------------------------------------------------------------------
剛才草草看到2的冪問題就沒仔細往下看,下面的代碼確實是有問題的,而且問題還不小....
首先就是題主的困惑,你的困惑是對的因為它這個代碼就是不對....如果rawAddress直接返回一個對齊到需要值的地址那麼adjustment會等於0,然後絕對後面就越界了...... 另外還有一個
問題指針轉U32,x64下直接崩了.剛才我就想這個代碼我好想在哪裡見過,翻了一下收藏夾我找到了一個帖子:
c++ - Aligned memory allocator: memory corruption (game engine architecture[Jason Gregory])供題主參考~
//-------------------------------- 再更新答案 (喂喂題主你不要改代碼啊..) ----------------------------
剛更新完代碼題主把代碼調整了...... 這是什麼書? 第二版代碼還是不對因為即使用U8也避免不了adjustment有可能會為0的問題,最簡單的避免問題的方法就是把這句改成:size_t expandedSize_bytes = size_bytes + alignment + sizeof(int);
..................
size_t mask = (alignment - 1);
uintptr_t misalignment = ( (rawAddress+sizeof(int)) mask);
ptrdiff_t adjustment = alignment - misalignment;
..................
我也看到這,我也覺得有問題,個人覺得應該是U8 *pAdjustment = (U8*)(alignedAddress - 1); *pAdjustment = adjustment; 因為對齊至少要移動一個位元組,調整的位元組書又不會超過256,所以這樣應該是可以了
推薦閱讀:
※C語言和內存管理有什麼關係?為什麼說學習C語言的關鍵在內存管理?
※講C語言內存管理的書籍或者博客?
※筆記本內存佔用突然變高,本來29%左右,現在開機就58%,沒開任何程序啊,急求,感謝?
※asp.net 應用佔用內存過大如何排查?
※Python可以視作同時支持像C++一樣的RAII特性,也具有垃圾回收GC的編程語言嗎?