[英]Very fast memcpy for image processing?
我正在用 C 進行圖像處理,需要在內存周圍復制大塊數據 - 源和目標永遠不會重疊。
在使用GCC (其中SSE 、 SSE2 但不是 SSE3 可用)的 x86 平台上執行此操作的絕對最快方法是什么?
我希望解決方案要么是在匯編中,要么是使用 GCC 內在函數?
我找到了以下鏈接,但不知道這是否是最好的方法(作者還說它有一些錯誤): http : //coding.derkeiler.com/Archive/Assembler/comp.lang.asm。 x86/2006-02/msg00123.html
編輯:請注意,副本是必要的,我無法繞過必須復制數據(我可以解釋原因,但我不會給你解釋:))
由William Chan和 Google 提供。 在 Microsoft Visual Studio 2005 中比 memcpy 快 30-70%。
void X_aligned_memcpy_sse2(void* dest, const void* src, const unsigned long size)
{
__asm
{
mov esi, src; //src pointer
mov edi, dest; //dest pointer
mov ebx, size; //ebx is our counter
shr ebx, 7; //divide by 128 (8 * 128bit registers)
loop_copy:
prefetchnta 128[ESI]; //SSE2 prefetch
prefetchnta 160[ESI];
prefetchnta 192[ESI];
prefetchnta 224[ESI];
movdqa xmm0, 0[ESI]; //move data from src to registers
movdqa xmm1, 16[ESI];
movdqa xmm2, 32[ESI];
movdqa xmm3, 48[ESI];
movdqa xmm4, 64[ESI];
movdqa xmm5, 80[ESI];
movdqa xmm6, 96[ESI];
movdqa xmm7, 112[ESI];
movntdq 0[EDI], xmm0; //move data from registers to dest
movntdq 16[EDI], xmm1;
movntdq 32[EDI], xmm2;
movntdq 48[EDI], xmm3;
movntdq 64[EDI], xmm4;
movntdq 80[EDI], xmm5;
movntdq 96[EDI], xmm6;
movntdq 112[EDI], xmm7;
add esi, 128;
add edi, 128;
dec ebx;
jnz loop_copy; //loop please
loop_copy_end:
}
}
您可以根據您的具體情況和您能夠做出的任何假設進一步優化它。
您可能還想查看 memcpy 源代碼 (memcpy.asm) 並去除其特殊情況處理。 或許可以進一步優化!
hapalibashi 發布的 SSE-Code 是要走的路。
如果您需要更高的性能並且不要回避編寫設備驅動程序的漫長而曲折的道路:現在所有重要的平台都有一個 DMA 控制器,它能夠更快地執行復制工作並與 CPU 代碼並行可以做。
不過,這涉及編寫驅動程序。 由於安全風險,我所知道的大型操作系統都沒有向用戶端公開此功能。
但是,這可能是值得的(如果您需要性能),因為地球上沒有任何代碼可以勝過設計用於完成此類工作的硬件。
這個問題已經四年了,我有點驚訝還沒有人提到內存帶寬。 CPU-Z 報告我的機器有 PC3-10700 RAM。 RAM 的峰值帶寬(又名傳輸速率、吞吐量等)為 10700 兆字節/秒。 我機器中的 CPU 是 i5-2430M CPU,峰值睿頻為 3 GHz。
從理論上講,使用無限快的 CPU 和我的 RAM, memcpy 可以達到5300 MBytes/sec ,即 10700 的一半,因為 memcpy 必須從 RAM 讀取然后寫入 RAM。 (編輯:正如 v.oddou 指出的,這是一個簡單的近似值)。
另一方面,想象一下我們有無限快的 RAM 和一個真實的 CPU,我們能實現什么? 讓我們以我的 3 GHz CPU 為例。 如果它可以在每個周期進行 32 位讀取和 32 位寫入,那么它可以傳輸 3e9 * 4 = 12000 MBytes/sec 。 這對於現代 CPU 來說似乎很容易實現。 我們已經可以看到,在 CPU 上運行的代碼並不是真正的瓶頸。 這是現代機器具有數據緩存的原因之一。
當我們知道數據被緩存時,我們可以通過對 memcpy 進行基准測試來衡量 CPU 真正能做什么。 准確地做到這一點很繁瑣。 我制作了一個簡單的應用程序,將隨機數寫入一個數組,將它們 memcpy 到另一個數組,然后對復制的數據進行校驗和。 我單步調試調試器中的代碼,以確保聰明的編譯器沒有刪除副本。 改變數組的大小會改變緩存性能——小數組適合緩存,大數組不太適合。 我得到了以下結果:
顯然,我的CPU每個周期可以讀寫超過32位,因為16000比我上面理論計算的12000還要多。 這意味着 CPU 的瓶頸甚至比我想象的還要小。 我使用了 Visual Studio 2005,進入標准的 memcpy 實現,我可以看到它在我的機器上使用了 movqda 指令。 我猜這每個周期可以讀寫 64 位。
hapalibashi 發布的漂亮代碼在我的機器上達到了 4200 MBytes/sec - 比 VS 2005 實現快大約 40%。 我猜它更快,因為它使用預取指令來提高緩存性能。
總而言之,在 CPU 上運行的代碼不是瓶頸,調優代碼只會帶來小的改進。
在-O1
或更高級別的任何優化級別上,GCC 將使用諸如memcpy
函數的內置定義 - 使用正確的-march
參數( -march=pentium4
用於您提到的功能集)它應該生成非常優化的特定於體系結構的內聯代碼。
我會對它進行基准測試,看看結果如何。
如果您可以訪問 DMA 引擎,則沒有什么比這更快的了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.