[英]Memory manipulation: set every n-th bit (C/C++) in modern CPUs/GPUs
據我了解,“現代” CPU具有相當出色的例程來處理二進制數據,例如通過同一操作流式傳輸許多數據。
臨時,我找不到使用這些CPU或GPU硬件編寫簡單指令(在GB內存中每5位設置一位)的庫,僅是經典| << &
| << &
技巧。
但是,設置第5位或第721位必須與在寬度為5或寬度721的黑白圖片中畫一條垂直的黑線相同,我希望這是一種快速的方法。
所以我的問題是:是否有任何提示如何在主流x86_64 Intel / AMD CPU或GPU上快速有效地處理位? 開源將是附帶條件。
首先,對大量內存執行此操作將因高速緩存未命中而成為瓶頸。 當前的CPU可以在每次加載/存儲時執行很多指令,並且仍然可以最大程度地利用內存帶寬。 如果我們要談論的是L1高速緩存中已經存在的幾千個內存,那么問題就更加有趣了。
如果您將第721位設置為1,那么矢量素材將無濟於事。 您的步幅為90.125字節,甚至比AVX512向量還要大。 因此,最佳解決方案是在適當的地址處執行單字節“ OR
”操作。 編寫循環以跟蹤字節中的位位置和字節位置並非易事。 如果這是一個編譯時常量,則將其展開8會很容易。 (每8個OR
額外增加一個字節)。
; pointer in rdi
; loop counter in ecx
.loop:
or byte ptr [rdi+90*0], 1<<0
or byte ptr [rdi+90*1], 1<<1
or byte ptr [rdi+90*2], 1<<2
or byte ptr [rdi+90*3], 1<<3
or byte ptr [rdi+90*4], 1<<4
or byte ptr [rdi+90*5], 1<<5
or byte ptr [rdi+90*6], 1<<6
or byte ptr [rdi+90*7], 1<<7
add rdi, 90*8 + 1
sub ecx, 8
jg .loop
; handle the last up to 7 iterations
對於不是編譯時常量的stride % 8
,可以在執行ptr += stride/8 + carry
位時,將stride % 8
的8位寄存器旋轉stride % 8
。 實際上,按寄存器計數循環比通常的ALU操作(在最近的Intel上)要慢一些,但是可變計數移位也是如此。
; ecx = unsigned int stride. rdi=char *dest
mov ebx, ecx
and ecx, 7 ; ecx = stride%8
shr ebx, 3 ; ebx = stride/8
mov al, 1
.loop:
or byte ptr [rdi], al
rol al, cl
add rdi, rbx
; efficiently figure out when we need to add an extra 1 to rdi
; lost interest at this point, feel free to edit or post another answer finishing this code.
dec edx
jg .loop
我正在嘗試一種方法來增加在換行時設置進位標志的字節內位的位置,因此您可以adc
執行ptr+= stride + carry
。 否則,只需添加0或1。
如果您的步幅等於128b,那么事情就微不足道了。 只需讀取/修改並使用恆定掩碼存儲到POR
。
如果步幅較小,那么事情會變得有趣。 向量寄存器沒有按位旋轉指令。 巧妙地操作可能會移位xmm寄存器中的多個設置位。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.