aligned_malloc及aligned_free的實現及詳細解釋

John_Jane_Doe發表於2015-10-24

有時候我們需要動態分配對齊的記憶體,例如使用SSE2指令集的時候,但C++並沒有提供分配和釋放對齊的記憶體的函式,這時候就需要我們自己動手實現一個


兩個函式原型如下

void* aligned_malloc(size_t size, size_t alignment);
void aligned_free(void *palignedmem);

alinged_malloc接受兩個size_t型別的引數,size是分配的記憶體塊位元組數,alignment是對齊數目,返回指向對齊的記憶體塊的void*指標

aligned_free接受一個void*型別的引數,是由aligned_malloc分配的對齊的記憶體塊的地址


1.aligned_malloc的實現


首先要檢查alignment是否為2的整數次冪,有一種高效的演算法:

if (alignment & (alignment - 1))
{
	return nullptr;	//alignment不是2的整數次冪則返回nullptr
}
else
{
	//反之,分配記憶體
}

如果一個整數是2的整數次冪,那這個數用二進位制表示一定是100..000(n個零)的形式,把這個數減1,則是111...111(n個1)的形式,把這兩個數做按位與,結果一定是0.反之,結果不是零。

然後要先分配足夠大的記憶體

void *praw = malloc(sizeof(void*) + size + alignment);

這個演算法基於一個顯而易見的結論:n個連續的自然數中一定有一個數可以被n整除。為了能夠找到size位元組的alignment對齊的記憶體,至少要分配(size+alignment)位元組的記憶體。同時,為了能夠有足夠的空間來儲存整個分配的記憶體的起始地址(即praw),還需要多分配一個指標佔用位元組數的記憶體,可用sizeof(void*)得到。


在尋找alignment位元組對齊的記憶體前,要先跳過為記錄整個分配的記憶體的起始地址(即praw的地址)保留的記憶體

void *pbuf = reinterpret_cast<void*>(reinterpret_cast<size_t>(praw) + sizeof(void*));


現在開始從pbuf向上尋找alignment對齊的記憶體塊。可用用下面這個演算法

void *palignedbuf = reinterpret_cast<void*>((reinterpret_cast<size_t>(pbuf) | (alignment - 1)) + 1)


為了方便,先假設記憶體地址寬度是8位元組,並且pbuf是13,用二進位制表示是00001101。alignment是8,用二進位制表示是00001000。比pbuf大的最小的能被8整除的整數是16,用二進位制表示是00010000。這個過程可以分三步完成:1.alignment減1,得到,00000111。2.alignment與pbuf進行按位或,得到00001111。3.加上1,得到00010000,正好是比pbuf大的最小的能夠被alignment整除的數。


返回對齊的記憶體之前,先前移指標大小個位元組來儲存整個分配的記憶體的起始地址(即praw)


(static_cast<void**>(palignedbuf))[-1] = praw;

最後返回對齊的記憶體的地址

return palignedbuf;


2.aligned_free的實現


這個很簡單,分為兩步:

1.從palignedmem前移指標大小個位元組,得到整個分配的記憶體的地址

2.free記憶體


程式碼如下


free(reinterpret_cast<void*>((static_cast<void**>(palignedmem))[-1])

最後貼上完整的程式碼


void* aligned_malloc(size_t size, size_t alignment)
{
    if(alignment & (alignment - 1))
    {
        return nullptr
    }
    else
    {
        void *praw = malloc(sizeof(void*) + size + alignment);
        if(praw)
        {
            void *pbuf = reinterpret_cast<void*>(reinterpret_cast<size_t>(praw) + sizeof(void*));
            void *palignedbuf = reinterpret_cast<void*>((reinterpret_cast<size_t>(pbuf) | (alignment - 1)) + 1);
            (static_cast<void**>palignedbuf)[-1] = praw;
            return palignedbuf;
        }
        else
        {
           return nullptr;
        }
    }
}

void aligned_free(void *palignedmem)
{
    free(reinterpret_cast<void*>((static_cast<void**>palignedmem)[-1]));
}




相關文章