看遊戲引擎架構內存管理有個地方不太清楚?

書上寫的對齊分配,說使用調整的那幾個位元組來儲存調整量(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的編程語言嗎?

TAG:程序員 | 編程 | 遊戲引擎 | 內存管理 |