我正在测试来自黑客喜悦书的divs10函数吞吐量,在我的jdk 1.7 64bit版本21和i7 intel Box上用
java编码
处理器:7
vendor_id:GenuineIntel
cpu家庭:6
型号:26
型号名称:Intel(R)Core(TM)i7 cpu 920 @ 2.67GHz
处理器:7
vendor_id:GenuineIntel
cpu家庭:6
型号:26
型号名称:Intel(R)Core(TM)i7 cpu 920 @ 2.67GHz
我想知道为什么默认的java运算符/比黑客的喜悦书中的divs10函数更快,结果显示divs10比“/”运算符慢3倍,令我惊讶.
任何人都可以告诉我,是否有任何花哨的内在jvm可以使用?
源代码如下.
public class div10 { public static final int divs10(int n) { int q,r; n = n + (n >> 31 & 9); q = (n >> 1) + (n >> 2); q += q >> 4; q += q >> 8; q += q >> 16; q = q >> 3; r = n - ((q << 3) + (q << 1)); return q + ((r + 6) >> 4); } public static void main(String[] args) { /* long count = 0; for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { if ( (i/10) != divs10(i) ) { System.err.println("error dividing :" + i ); } if ((i & 0xFFFFFFF ) == 0 ) { System.out.println("Finished:" + Long.toHexString(count) + ":" + count + ":" + i); } count++; } System.out.println("Success:" + count); */ long start = System.nanoTime(); long count = 0L; int iter = 100_000; for (int j = 0; j < 10; j++) for (int i = -iter; i < iter; i++) { count += (i/10); } for (int j = 0; j < 10; j++) for (int i = -iter; i < iter; i++) { count += divs10(i); } System.out.println(count + " warm up done ") ; start = System.nanoTime(); count = 0L; for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { count += i/10; } System.out.println(count + ",took:" + (System.nanoTime() - start) / 1000_000L + " ms," + (System.nanoTime() - start) / ((long)Integer.MAX_VALUE - (long)Integer.MIN_VALUE) + " ns per ops" ) ; start = System.nanoTime(); count = 0L; for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) { count += divs10(i); } System.out.println(count + "," + (System.nanoTime() - start) / ((long)Integer.MAX_VALUE - (long)Integer.MIN_VALUE) + " ns per ops" ) ; } }
解决方法
更新:当查看较新的
Ivy Bridge table(第174页)时,我看到所有延迟都在1.这意味着我之前的解释是不正确的.
计算在divs10方法中执行的指令的尝试是27(没有函数调用的开销)指令.您正在进行操作,要求在下一个操作开始之前完成前一个操作.这意味着您应该考虑指令的延迟.根据Ivy Bridge指令表,所涉及的所有指令都具有1个时钟周期的延迟.这总共给你27个时钟周期.
这与单个IDIV(8位)指令相比较.在表中,我发现这需要大约20个时钟周期的延迟.
原始估计将给出:27个周期/ 20个周期= 1.35倍慢.这与您的结果不一致.我不是这方面的专家,但我认为这是因为IDIV指令的划分可以并行运行,因为它们是独立的. IDIV指令的吞吐量为8个时钟周期.这允许cpu以这样的方式优化指令:它可以每52个周期运行大约4个分区(这是一个估计).
因此,要使用位移算法执行4次除法,您需要108个周期,而IDIV则需要大约64个时钟周期.这给出:108/52 =慢2.1倍.