Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中检测偶数最有效的方法是什么?_Java_Performance_Math - Fatal编程技术网

在Java中检测偶数最有效的方法是什么?

在Java中检测偶数最有效的方法是什么?,java,performance,math,Java,Performance,Math,确定一个数字是否使用Java的最有效方式是什么?为什么 它会使用模或减法,或者其他一些我实际上没有想到的方式吗 有人想象我可以通过一个简单的测试类来确定这一点——我可以——但这并不能解释为什么,不是吗 我并不是为了更快地处理这么多项目而进行疯狂的性能调整。但我很好奇,作为一种常见做法,一种方法是否应该优先于另一种方法。同样地,我们不会用&代替&,当我们可以使用&时,为什么要使用%?TL;DR按位and版本似乎是最快的。基准和样本结果如下 这应该比模运算快,因为只有两个步骤可以直接在硬件中处理:

确定一个数字是否使用Java的最有效方式是什么?为什么

它会使用模或减法,或者其他一些我实际上没有想到的方式吗

有人想象我可以通过一个简单的测试类来确定这一点——我可以——但这并不能解释为什么,不是吗


我并不是为了更快地处理这么多项目而进行疯狂的性能调整。但我很好奇,作为一种常见做法,一种方法是否应该优先于另一种方法。同样地,我们不会用
&
代替
&
,当我们可以使用
&
时,为什么要使用
%

TL;DR按位and版本似乎是最快的。基准和样本结果如下


这应该比模运算快,因为只有两个步骤可以直接在硬件中处理:

if ((n & 1) == 0) {
  // even number here
}
这是一个微基准,证明了我和aasylias的观点:

    // setup
    int runs = 10;
    int numbers = 200000000; // 200.000.000
    int[] randomNumbers = new int[numbers];
    Random random = new Random();
    for (int i = 0; i < randomNumbers.length; i++) {
        randomNumbers[i] = random.nextInt();
    }
    int even = 0;
    int odd = 0;

    // bitwiseAnd
    long andStart = System.currentTimeMillis();
    for (int i = 0; i < runs; i++) {
        for (int number : randomNumbers) {
            if ((number & 1) == 0)
                even++;
            else
                odd++;
        }
    }
    long andDone = System.currentTimeMillis();
    long andDuration = andDone - andStart;

    System.out.println("Even " + even + ", odd " + odd);

    // reset variables
    even = 0;
    odd = 0;

    // Modulo
    long moduloStart = System.currentTimeMillis();
    for (int i = 0; i < runs; i++) {
        for (int number : randomNumbers) {
            if (number % 2 == 0)
                even++;
            else
                odd++;
        }
    }
    long moduloDone = System.currentTimeMillis();
    long moduloDuration = moduloDone - moduloStart;
    // Done with modulo

    System.out.println("Even " + even + ", odd " + odd);

    // reset variables
    even = 0;
    odd = 0;

    // Shift
    long shiftStart = System.currentTimeMillis();
    for (int i = 0; i < runs; i++) {
        for (int number : randomNumbers) {
            if ((number << 31) == 0)
                even++;
            else
                odd++;
        }
    }
    long shiftDone = System.currentTimeMillis();
    long shiftDuration = shiftDone - shiftStart;
    // Done with shift

    System.out.println("Even " + even + ", odd " + odd);

    System.out.println("Modulo Time    " + moduloDuration);
    System.out.println("Bitwise & Time " + andDuration);
    System.out.println("Shift Time     " + shiftDuration);

如果检查这两种方法中热点7生成的程序集:

public static boolean isEvenBit(int i) {
    return (i & 1) == 0;
}
public static boolean isEvenMod(int i) {
    return i % 2 == 0;
}
您将看到,虽然mod经过优化,基本上执行位
操作,但它有一些额外的指令,因为这两个操作并不完全等效*。其他JVM可能会以不同的方式对其进行优化。大会张贴在下面的参考

我还运行了一个微基准测试,它证实了我们的观察结果:isEventBit稍微快一点(但两者都在2纳秒左右运行,因此可能不会对整个典型程序产生太大影响):


伊塞文比特 伊塞文莫德
*正如评论中指出的,
%
并不是真正的模;剩下的就是了。所以
(i%2)!=(i&1)
if
i<0
isEvenMod
代码中的额外指令将结果的符号设置为
i
(然后将其与零进行比较,这样就浪费了精力)。

另一种方法是运行微基准测试并分析每个变量所花费的时间。结果如下:

Benchmark    Mean    Units    Time vs. baseline
baseline     10.330  nsec/op     0.000
bitAnd       12.075  nsec/op     1.745
bitShift     12.309  nsec/op     1.979
modulo       12.309  nsec/op     4.529
(基线是一个只返回
i==0
的方法)

结论:

  • i&1
    ----->大约需要1.75ns
  • i大约需要2.00ns
  • i%2
    -->大约需要4.50ns
换句话说,
i%2
i&1
慢2倍


注意:使用jmh完成基准测试。基线很高,因为我生成随机数以确保该方法不会被优化。测试在i7@2.8GHz(即一个周期=0.35ns)上运行,热点为7。

关于偶数,我们知道的唯一一件事是,当除以2时,偶数为0,不是吗?我很好奇:这是用于实际应用吗?我无法想象你需要在这个层次上优化它。我通常让编译器发挥它的魔力,然后进行度量。你会发现很难比%做得更好。当然,你真正能做的就是个人资料。不过,比这更重要的是,你可能会花更多的时间从内存中提取数据,而不是使用实际的mod,所以我不会太挂断。TLDR--在你确定这是你应用程序中的瓶颈之前,不要担心。在我的机器上,PSR的两种方法都需要约2.9纳秒。不要优化。如果它被证明太慢,你可以再看一看。如果你需要更高的速度,检查你是否可以做更少的检查!你决定在实践中更快吗?@OliCharlesworth:不,我说应该;)我认为JIT可以很好地优化
%2
,但是这里没有太多的优化空间,因为它可以在我所知道的每个平台上直接翻译成机器代码。我现在正在写一个微基准,几分钟后就会发布。@AlexKreutznaer:还有,一百万对于这样一个微型基准来说是个小数目。我选择了20亿。额外说明的一个原因是检查是否发生了0除法。我对这个回答感到非常激动。谢谢@jlordo:FWIW,这看起来像x86程序集,而不是JVM字节码…@jlordo是的,它是由JIT生成的程序集。我在另一个答案中添加了一个微观基准。我得到的结果与你的一致。@OliCharlesworth@jlordo我不认为它是在检查除以零
%
不是真正的模;剩下的就是了。所以
(i%2)!=(i&1)
如果
i
为负值。
%
代码中的额外指令将结果的符号设置为
i
的符号。(然后将其与零进行比较,这样做是浪费精力。)另一个帖子的答案表明你是正确的,但你给出了这个答案,并且没有给出为什么它是最佳方法的理由,这使得它是一个糟糕的答案。如果你填好答案,我很乐意收回反对票。
Benchmark                     Mode  Samples  Score   Error  Units
c.a.p.SO16969220.isEvenBit    avgt       10  1.869 ± 0.069  ns/op
c.a.p.SO16969220.isEvenMod    avgt       10  2.554 ± 0.142  ns/op
  # {method} 'isEvenBit' '(I)Z' in 'javaapplication4/Test1'
  # parm0:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x00000000026c2580: sub    rsp,0x18
  0x00000000026c2587: mov    QWORD PTR [rsp+0x10],rbp  ;*synchronization entry
                                                ; - javaapplication4.Test1::isEvenBit@-1 (line 66)
  0x00000000026c258c: and    edx,0x1
  0x00000000026c258f: mov    eax,edx
  0x00000000026c2591: xor    eax,0x1            ;*ireturn
                                                ; - javaapplication4.Test1::isEvenBit@11 (line 66)
  0x00000000026c2594: add    rsp,0x10
  0x00000000026c2598: pop    rbp
  0x00000000026c2599: test   DWORD PTR [rip+0xfffffffffdb6da61],eax        # 0x0000000000230000
                                                ;   {poll_return}
  0x00000000026c259f: ret    
  # {method} 'isEvenMod' '(I)Z' in 'javaapplication4/Test1'
  # parm0:    rdx       = int
  #           [sp+0x20]  (sp of caller)
  0x00000000026c2780: sub    rsp,0x18
  0x00000000026c2787: mov    QWORD PTR [rsp+0x10],rbp  ;*synchronization entry
                                                ; - javaapplication4.Test1::isEvenMod@-1 (line 63)
  0x00000000026c278c: mov    r10d,edx
  0x00000000026c278f: and    r10d,0x1           ;*irem
                                                ; - javaapplication4.Test1::isEvenMod@2 (line 63)
  0x00000000026c2793: mov    r11d,r10d
  0x00000000026c2796: neg    r11d
  0x00000000026c2799: test   edx,edx
  0x00000000026c279b: cmovl  r10d,r11d
  0x00000000026c279f: test   r10d,r10d
  0x00000000026c27a2: setne  al
  0x00000000026c27a5: movzx  eax,al
  0x00000000026c27a8: xor    eax,0x1            ;*ireturn
                                                ; - javaapplication4.Test1::isEvenMod@11 (line 63)
  0x00000000026c27ab: add    rsp,0x10
  0x00000000026c27af: pop    rbp
  0x00000000026c27b0: test   DWORD PTR [rip+0xfffffffffdb6d84a],eax        # 0x0000000000230000
                                                ;   {poll_return}
  0x00000000026c27b6: ret    
Benchmark    Mean    Units    Time vs. baseline
baseline     10.330  nsec/op     0.000
bitAnd       12.075  nsec/op     1.745
bitShift     12.309  nsec/op     1.979
modulo       12.309  nsec/op     4.529