最快的汇编代码,用于在x64系统上增加一串长字

时间:2016-05-07 23:48:24

标签: assembly 64-bit x86-64

很久以前我为Z80和68000汇编程序做了一些爱好编程,所以我理解了基础知识,但我是x86 / x64汇编程序的新手。我试图找到** 最快的 **代码来执行以下操作(需要最佳速度的关键部分仅为步骤2-4)。

我根本不需要步骤1或5的帮助,它们仅针对上下文显示。 我不是在寻找完整的代码编写,但会欣赏有关最佳指令的任何提示。这个平台的算法。有很多方法可以编写这样的例程但通常显而易见的方法并不是最优的。如果有人说像#34;尝试使用XYZ指令"我会没事的。另外,正如我在下面提到的,在汇编程序中使用数组可能不是最快的方法,所以关于如何最佳地构建数据以获得速度的任何建议也是我正在寻找的答案的一部分。 ( x64汇编程序甚至可以处理带索引的4GB数组吗?

  • 步骤1.阅读"字符串"来自文件的长字元素。每个元素都包含外部提供的数据,可以将其视为带符号的31位数字。

该文件通常很小(小于100K),但可能有时长达1GB的元素。只要可以直接访问/修改各个元素,就可以同时将所有元素读入内存,这是。本能地说这听起来像使用4GB阵列会是最快的,但我是x64汇编程序的新手,并且不确定阵列的开销是否有助于或损害速度。

  • 步骤2.将元素增加1。

  • 步骤3.检查符号标志(查看增量是否设置为高位)。

    • 如果设置,则转移到将修改元素的例程,然后继续执行步骤4 ..
    • 如果未设置,则跳转到步骤5(退出)。

在子程序中花费的时间超出了本问题的范围,您现在可以立即使用立即返回指令。 然而,子程序需要知道元素的索引。

  • 步骤4.转到下一个元素并重复步骤2.

  • 步骤5.关闭文件,保存所有修改过的数据。

另外,有两个相关的问题:

  • 由于元素是32位,代码在32位系统上运行得更快吗?

  • 如果第2步是1以外的增量,代码会有什么不同?

对“太多”的回应"关闭标志:

这个问题怎么样?#34;太宽泛"即使正确符合SO Help Page上的社区准则之上的所有主题描述:

  • 特定编程问题 - (如何优化特殊类型的数组处理)
  • 软件算法 - (上述阵列算法,具体为上述步骤2-4)
  • 程序员常用的软件工具 - (x64汇编程序是程序员常用的)
  • 一个软件开发独有的实用,可回答的问题 - (由于@Jester,@ PeterCordes和@ Ped7g提供了几个答案/建议,我会说这是不言而喻的就是这种情况)

1 个答案:

答案 0 :(得分:2)

有关很多内容的信息,请参阅标记维基。

为什么要在写回之前完成所有I / O操作?您的未指定子例程是否需要随机访问任意元素?如果是这样,mmap(2)整个文件。 (假设POSIX系统调用)。

如果没有,read(2)进入一个大约128kB左右的缓冲区。 (小于L2缓存)。处理,然后pwrite(2)回到您从中读取它的地方。 (或lseek(2) / write(2))。

您的子程序是否可以中止整个过程,导致文件没有修改?

您可以使用SSE2进行增量:使用PADDD / MOVMSKPS并行执行四个32位(双字)加法,然后提取符号位。在掩码结果上使用test以查看是否有任何元素设置了其符号位。如果是这样,请调用这些元素的子例程。

bsf将找到第一个设置位。有BMI1或BMI2指令清除最低设置位IIRC。您可以使用它来遍历掩码中的设置位。

或者如果您发现向量中的任何元素都设置了它们的符号位,您可以跳过向量存储回到数组中,而是使用标量代码再次通过这些元素。这样做的好处是可以将数组中的相邻元素放在"正确的#34;调用子程序时的状态。

e.g。

    ;; set up constants
    pcmpeqw   xmm1, xmm1
    psrld     xmm1, 31     ; xmm1 = [ 1 1 1 1]

    ; rsi = start,  rdi = one-past-the-end
    ; or maybe prefer keeping these in regs the subroutine won't clobber
.vectorloop:
    movdqa    xmm0, [rsi]
    paddd     xmm0, xmm1
    movmskps  eax, xmm1       ; pmovmskb would give us the high bit of every byte.  This is just every dword element
    test      eax, eax
    jnz     .at_least_one_sign_bit_set
    movdqa    [rsi], xmm0     ; vector store back, since no elements had sign bits set
.resume_vectorloop:    ; scalar code jumps back here when done
    add       rsi, 16
    cmp       rsi, rdi
    jb       .vectorloop

    jmp     all_done

.at_least_one_sign_bit_set:

    ; Array isn't modified at this point.
    inc     dword [rsi]                ;; or better, load / inc / jns, passing the pointer and index to the subroutine, so it doesn't have to load again after the read-modify-write inc.
    jns ...
        ;; maybe add rsi, 4  here, depending on how we want want to call the subroutine.
    inc     dword [rsi+4]
    jns ...
    ...
    jmp      .resume_vectorloop    ;; or duplicate the tail and cmp/jb to .vectorloop

这假设您的缓冲区已对齐且大小为矢量宽度的倍数,因此您不必关心未对齐或标量清理。你控制缓冲区,所以这应该很容易。 (除了mmap的长度部分,可能。但它不是一个难以解决的问题。)