快速模10 in c

dtcbnfnu  于 2023-03-22  发布在  其他
关注(0)|答案(5)|浏览(130)

我正在寻找一个快速模10算法,因为我需要加快我的程序,它做了许多模运算周期。
我已经检查了this page,它比较了一些替代品。据我正确理解,T3是所有中最快的。我的问题是,如何将x % y看起来像使用T3技术?
为了简单起见,我在这里复制了T3技术,以防链接中断。

for (int x = 0; x < max; x++)
{
        if (y > (threshold - 1))
        {
               y = 0; //reset
               total += x;
        }
        y += 1;
}

关于评论,如果这不是真的快,然后定期国防部,我正在寻找至少2倍的模比使用%。我已经看到了许多例子与使用2的权力,但由于10不是,我怎么才能让它工作?

编辑:

对于我的程序,假设我有2个循环,其中n=1 000 000m=1000
看起来像这样:

for (i = 1; i <= n; i++) {
        D[(i%10)*m] = i;
        for (j = 1; j <= m; j++) {
           ...
        }
}
vm0i2vca

vm0i2vca1#

以下是你可以编写的最快的模10函数:

unsigned mod10(unsigned x)
{
    return x % 10;
}

下面是它编译后的样子:

movsxd rax, edi
imul rcx, rax, 1717986919
mov rdx, rcx
shr rdx, 63
sar rcx, 34
add ecx, edx
add ecx, ecx
lea ecx, [rcx + 4*rcx]
sub eax, ecx
ret

注意缺少除法/求模指令,神秘的常量,使用最初用于复杂数组索引的指令等。不用说,编译器知道很多技巧,可以让你的程序尽可能快。你很少会在这样的任务中击败它。

o7jaxewo

o7jaxewo2#

你可能无法击败编译器。
调试版本

//     int foo = x % 10;
010341C5  mov         eax,dword ptr [x]  
010341C8  cdq  
010341C9  mov         ecx,0Ah  
010341CE  idiv        eax,ecx  
010341D0  mov         dword ptr [foo],edx

零售构建(在那里做一些忍者数学...)

//    int foo = x % 10;
00BD100E  mov         eax,66666667h  
00BD1013  imul        esi  
00BD1015  sar         edx,2  
00BD1018  mov         ecx,edx  
00BD101A  shr         ecx,1Fh  
00BD101D  add         ecx,edx  
00BD101F  lea         eax,[ecx+ecx*4]  
00BD1022  add         eax,eax  
00BD1024  sub         esi,eax
zf9nrax1

zf9nrax13#

代码不是模的直接替代,它在那种情况下替代模 *。你可以通过类比来编写你自己的mod(对于ab〉0):

int mod(int a, int b) {
    while (a >= b) a -= b;
    return a;
}

但它是否比%更快是非常值得怀疑的。

zlwx9yxi

zlwx9yxi4#

我遇到了这个讨论,而对于uint64_t,执行mod 10操作的最佳方法确实是通过在我的标准笔记本电脑上使用编译器。然而,对于我最近的ubuntu linux上的unt128_t,对于例程:

for (int i = 0; i < 1000000000; i++)
{
  uint128_t x = n + i;
  s += x % 10;
}

时间:

Executed in   21,74 secs   fish           external 
   usr time      21,73 secs  420,00 micros   21,73 secs 
   sys time       0,00 secs  237,00 micros    0,00 secs

这与我使用uint64_t得到的结果非常不同。因此,可以期望在这里做一些聪明的事情(我敢打赌,在未来的gcc版本中,他们会实现以下技巧的某种形式)。我们可以利用规则,

(a+b) mod 10   = (a mod 10 + b mod 10) mod 10

还有

(ab) mode 10 = ((a mode 10)*(b mod 10)) mod 10

为了生成代码,

for (int i = 0; i < 1000000000; i++)
{
  uint128_t x = n + i;
  uint64_t  a = (uint64_t)(x >> 64);
  uint64_t  b = (uint64_t)(x & (~0UL));
  
  s += ((a%10)*2*((1UL<<63)%10) + (b%10))%10;
}

这是以速度为基准

Executed in    3,55 secs   fish           external 
usr time       3,55 secs  409,00 micros    3,55 secs 
sys time       0,00 secs  233,00 micros    0,00 secse here

对于模10运算来说,这是一个很好的5倍加速。注意,10在这里并不是魔术,除了编译器可能对64位无符号整数的10特别聪明。类似的技巧可以用于整数除以10,我们注意到我们总是可以将数字x写成x = a 10 + b,其中a = x/10和b = x%10,然后我们可以再次研究x1*x2和x1+x2,以利用64位整数的快速版本推导出128位整数除法的类似规则。

inline uint128_t div10q(uint128_t x)
{
  uint64_t   x1  = (uint64_t)(x >> 64);
  uint128_t  x2  = ((unt128_t)1) << 64;
  uint64_t   x3  = (uint64_t)(x & (~0UL));

  uint64_t   b1   = x1%10;
  uint128_t  y1  = x1/10;

  uint64_t   b2  = x2%10;
  uint128_t  y2  = x2/10;

  uint128_t yy1 = y1*y2*10+b1*y2+b2*y1 + (b1*b2)/10;
  uint64_t  bb1 = (b1*b2)%10;

  uint64_t  bb2 = x3 % 10;
  uint128_t yy2 = x3 / 10;

  return yy1+yy2+(bb1+bb2)/10;
}

在gcc中使用优化-O3编译到类似的5倍加速。

8cdiaqws

8cdiaqws5#

这将适用于大于机器字的(多字)值(但假设是二进制计算机...):

#include <stdio.h>

unsigned long mod10(unsigned long val)
{
unsigned res=0;

res =val &0xf;
while (res>=10) { res -= 10; }

for(val >>= 4; val; val >>= 4){
        res += 6 * (val&0xf);
        while (res >= 10) { res -= 10; }
        }

return res;
}

int main (int argc, char **argv)
{
unsigned long val;
unsigned res;

sscanf(argv[1], "%lu", &val);

res = mod10(val);
printf("%lu -->%u\n", val,res);

return 0;
}

更新:通过一些额外的努力,你可以得到没有乘法的算法,并且 * 通过适当的优化 * 我们甚至可以得到内联的递归调用:

static unsigned long mod10_1(unsigned long val)
{
unsigned char res=0; //just to show that we don't need a big accumulator

res =val &0xf; // res can never be > 15
if (res>=10) { res -= 10; }

for(val >>= 4; val; val >>= 4){
        res += (val&0xf)<<2 | (val&0xf) <<1;
        res= mod10_1(res); // the recursive call
        }

return res;
}

mod10_1的结果看起来没有穆尔/div,几乎没有分支:

mod10_1:
.LFB25:
    .cfi_startproc
    movl    %edi, %eax
    andl    $15, %eax
    leal    -10(%rax), %edx
    cmpb    $10, %al
    cmovnb  %edx, %eax
    movq    %rdi, %rdx
    shrq    $4, %rdx
    testq   %rdx, %rdx
    je      .L12
    pushq   %r12
    .cfi_def_cfa_offset 16
    .cfi_offset 12, -16
    pushq   %rbp
    .cfi_def_cfa_offset 24
    .cfi_offset 6, -24
    pushq   %rbx
    .cfi_def_cfa_offset 32
    .cfi_offset 3, -32
.L4:
    movl    %edx, %ecx
    andl    $15, %ecx
    leal    (%rcx,%rcx,2), %ecx
    leal    (%rax,%rcx,2), %eax
    movl    %eax, %ecx
    movzbl  %al, %esi
    andl    $15, %ecx
    leal    -10(%rcx), %r9d
    cmpb    $9, %cl
    cmovbe  %ecx, %r9d
    shrq    $4, %rsi
    leal    (%rsi,%rsi,2), %ecx
    leal    (%r9,%rcx,2), %ecx
    movl    %ecx, %edi
    movzbl  %cl, %ecx
    andl    $15, %edi
    testq   %rsi, %rsi
    setne   %r10b
    cmpb    $9, %dil
    leal    -10(%rdi), %eax
    seta    %sil
    testb   %r10b, %sil
    cmove   %edi, %eax
    shrq    $4, %rcx
    andl    $1, %r10d
    leal    (%rcx,%rcx,2), %r8d
    movl    %r10d, %r11d
    leal    (%rax,%r8,2), %r8d
    movl    %r8d, %edi
    andl    $15, %edi
    testq   %rcx, %rcx
    setne   %sil
    leal    -10(%rdi), %ecx
    andl    %esi, %r11d
    cmpb    $9, %dil
    seta    %bl
    testb   %r11b, %bl
    cmovne  %ecx, %edi
    andl    $1, %r11d
    andl    $240, %r8d
    leal    6(%rdi), %ebx
    setne   %cl
    movl    %r11d, %r8d
    andl    %ecx, %r8d
    leal    -4(%rdi), %ebp
    cmpb    $9, %bl
    seta    %r12b
    testb   %r8b, %r12b
    cmovne  %ebp, %ebx
    andl    $1, %r8d
    cmovne  %ebx, %edi
    xorl    $1, %ecx
    andl    %r11d, %ecx
    orb     %r8b, %cl
    cmovne  %edi, %eax
    xorl    $1, %esi
    andl    %r10d, %esi
    orb     %sil, %cl
    cmove   %r9d, %eax
    shrq    $4, %rdx
    testq   %rdx, %rdx
    jne     .L4
    popq    %rbx
    .cfi_restore 3
    .cfi_def_cfa_offset 24
    popq    %rbp
    .cfi_restore 6
    .cfi_def_cfa_offset 16
    movzbl  %al, %eax
    popq    %r12
    .cfi_restore 12
    .cfi_def_cfa_offset 8
    ret
.L12:
    movzbl  %al, %eax
    ret
    .cfi_endproc
.LFE25:
    .size   mod10_1, .-mod10_1
    .p2align 4,,15
    .globl  mod10
    .type   mod10, @function

相关问题