【问题标题】:Efficient single-bit negation in FortranFortran 中的高效单位求反
【发布时间】:2017-01-27 05:58:33
【问题描述】:

我正在编写性能密集型 Fortran 代码,它的核心是一个无矩阵矩阵乘法子例程 mfmult(x,y),它接受一个输入向量 x,并返回一个输出向量 y,如果 i = i_{n-1}i_{n-2}...i_2i_1i_0 是一个 n 位二进制数(可能带有前导零),那么

y(i) = sum{x(j) : j 是 i 的任何 1 位否定}

y(000) = x(001)+x(010)+x(100)
y(1101) = x(0101)+x(1001)+x(1111) + x(1100) etc.

什么是实现这一点的有效方法?一个相关的问题是:Fortran 中是否存在用于单位否定的快速内在函数?我看过内在函数和https://rosettacode.org/wiki/Bitwise_operations#Fortran,但没有单位否定操作,我担心任何手动编码/分支语句都会让事情变得太慢。

【问题讨论】:

  • 好吧,无论如何,CPU 总是访问整个字节。您可以使用 IBSET(),但我不希望它特别有效。您还可以将 IEOR() 与某些特定常量一起使用。
  • IBSET 和 IBCLR 的问题是我首先需要一个 BTEST if 语句来检查该位是否为 0 和 1,以便我可以选择相反的,我希望它在一个巨大的环形。我不确定如何使用 IEOR()。翻转一个位似乎是一种基本操作,以至于这些方法似乎有点矫枉过正。这是否只是 Fortran 作为一种语言的限制,它没有这种内在特性?
  • 如果您想将其称为限制...您认为 Fortran 中缺少 C 或 x86 程序集的等效项是什么?
  • 谢谢,IEOR 方法运行得非常快。事实上,我很惊讶地发现它比使用稀疏矩阵显式地执行向量转换更快(大约快两倍)。在做了更多研究之后,我现在看到其他语言也只是使用这种方法进行位否定。

标签: binary fortran bit


【解决方案1】:

我建议您使用 IEOR() 内在函数。您必须先准备好正确的口罩。可能看起来是间接的,但我认为实际上没有比这更直接的了,见C / Assembly: how to change a single bit in a CPU register?

所以如果你有整数i,并且你想翻转第三位,我真的会这样做:

i = ieor(i, int(b'00000100'))

这在 x86_64 程序集中的作用是

xorl    $4, the_register_holding_i

如果您想翻转该整数中的多个位,最好相应地准备掩码并只调用一次IEOR()

请注意,CPU 总是(至少)处理整个字节或更有效地处理整个字,没有指令只需要一个位的地址并只操作那个地址。您不必担心您正在访问整个 32 位整数,因为您有一个 32 位或 64 位处理器。该指令集已准备好有效地处理 32 位和 64 位整数。

【讨论】:

    【解决方案2】:

    这应该是有效的:

    program test
      implicit none
      integer :: filter(0:31)
      data filter / &
        b'00000000000000000000000000000001', &
        b'00000000000000000000000000000010', &
        b'00000000000000000000000000000100', &
        b'00000000000000000000000000001000', &
        b'00000000000000000000000000010000', &
        b'00000000000000000000000000100000', &
        b'00000000000000000000000001000000', &
        b'00000000000000000000000010000000', &
        b'00000000000000000000000100000000', &
        b'00000000000000000000001000000000', &
        b'00000000000000000000010000000000', &
        b'00000000000000000000100000000000', &
        b'00000000000000000001000000000000', &
        b'00000000000000000010000000000000', &
        b'00000000000000000100000000000000', &
        b'00000000000000001000000000000000', &
        b'00000000000000010000000000000000', &
        b'00000000000000100000000000000000', &
        b'00000000000001000000000000000000', &
        b'00000000000010000000000000000000', &
        b'00000000000100000000000000000000', &
        b'00000000001000000000000000000000', &
        b'00000000010000000000000000000000', &
        b'00000000100000000000000000000000', &
        b'00000001000000000000000000000000', &
        b'00000010000000000000000000000000', &
        b'00000100000000000000000000000000', &
        b'00001000000000000000000000000000', &
        b'00010000000000000000000000000000', &
        b'00100000000000000000000000000000', &
        b'01000000000000000000000000000000', &
        b'10000000000000000000000000000000'/
      integer :: a
      integer :: k
    
      ! Number whose single-bit negations should be found
      a = b'1101'
    
      do k=0,31
        print *, ieor(a,filter(k))
      enddo
    
    end
    

    【讨论】:

    • 只是一个风格注释 - 数据有点过时,我会在声明中初始化过滤器,并将其作为参数
    猜你喜欢
    • 1970-01-01
    • 2014-10-23
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 1970-01-01
    • 2010-10-19
    • 1970-01-01
    相关资源
    最近更新 更多