繁体   English   中英

内存操作:在现代CPU / GPU中将第n位(C / C ++)设置为

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM