【发布时间】:2015-12-03 03:58:51
【问题描述】:
我有以下代码对数组中的位进行循环移位:
private static void method1(byte[] bytes) {
byte previousByte = bytes[0];
bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((bytes[bytes.length - 1] & 0xff) << 7));
for (int i = 1; i < bytes.length; i++) {
byte tmp = bytes[i];
bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((previousByte & 0xff) << 7));
previousByte = tmp;
}
}
然后我认为像这样倒退更容易,更易读:
private static void method2(byte[] bytes) {
byte lastByte = bytes[bytes.length-1];
for (int i = bytes.length-1; i > 0; i--) {
bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((bytes[i-1] & 0xff) << 7));
}
bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((lastByte & 0xff) << 7));
}
但我注意到第二个(方法 2)比第一个(方法 1)慢!我注意到了差异,因为我调用了该方法数千次。所以我做了一个测试来确定,这里是调用每个方法 3000 次的 20 次测试的平均结果(字节数是 100 万):
method1 average : 4s 572ms
method2 average : 5s 630ms
所以我的问题是:为什么第一个比第二个快?
这是测试代码,以确保我的测试没有做错:
import java.math.BigInteger;
public class BitShiftTests {
public static void main(String[] args) {
int numOfTests = 20;
int numberOfShifts = 3000;
byte[] numbers = new byte[1000000];
for (int i = 0; i < numbers.length; i++) {
numbers[i] = (byte) (i % 255);
}
System.out.println("Testing method1...");
BigInteger method1Sum = new BigInteger("00000000", 2);
for (int i = 1; i <= numOfTests; i++) {
long total = 0L;
for (int j = 0; j < numberOfShifts; j++) {
long startTime = System.nanoTime();
method1(numbers);
long endTime = System.nanoTime();
total = total + (endTime - startTime);
}
method1Sum = method1Sum.add(new BigInteger(Long.toString(total), 10));
System.out.println(String.format("%-2d: %s", i, getTime(total)));
}
System.out.println("Testing method2...");
BigInteger method2Sum = new BigInteger("00000000", 2);
for (int i = 1; i <= numOfTests; i++) {
long total = 0L;
for (int j = 0; j < numberOfShifts; j++) {
long startTime = System.nanoTime();
method2(numbers);
long endTime = System.nanoTime();
total = total + (endTime - startTime);
}
method2Sum = method2Sum.add(new BigInteger(Long.toString(total), 10));
System.out.println(String.format("%-2d: %s", i, getTime(total)));
}
System.out.println("method1 average : " + getTime(method1Sum.longValue() / numOfTests));
System.out.println("method2 average : " + getTime(method2Sum.longValue() / numOfTests));
}
private static void method1(byte[] bytes) {
byte previousByte = bytes[0];
bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((bytes[bytes.length - 1] & 0xff) << 7));
for (int i = 1; i < bytes.length; i++) {
byte tmp = bytes[i];
bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((previousByte & 0xff) << 7));
previousByte = tmp;
}
}
private static void method2(byte[] bytes) {
byte lastByte = bytes[bytes.length-1];
for (int i = bytes.length-1; i > 0; i--) {
bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((bytes[i-1] & 0xff) << 7));
}
bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((lastByte & 0xff) << 7));
}
private static String getTime(long nanoSecs) {
int minutes = (int) (nanoSecs / 60000000000.0);
int seconds = (int) (nanoSecs / 1000000000.0) - (minutes * 60);
int millisecs = (int) (((nanoSecs / 1000000000.0) - (seconds + minutes * 60)) * 1000);
int nanosecs = (int) nanoSecs - (millisecs * 1000000000);
if (minutes == 0 && seconds == 0 && millisecs == 0) {
return nanosecs + "ns";
}
if (minutes == 0 && seconds == 0) {
return millisecs + "ms";
}
if (minutes == 0 && millisecs == 0) {
return seconds + "s";
}
if (seconds == 0 && millisecs == 0) {
return minutes + "min";
}
if (minutes == 0) {
return seconds + "s " + millisecs + "ms";
}
if (seconds == 0) {
return minutes + "min " + millisecs + "ms";
}
if (millisecs == 0) {
return minutes + "min " + seconds + "s";
}
return minutes + "min " + seconds + "s " + millisecs + "ms";
}
}
更新:
看起来原因是我在第二种方法中的每个循环中访问了 2 个不同的索引,而在第一种方法中我只访问了 1 个索引。所以它与反转循环无关。
感谢@rm5248 和@Ben,如果可以的话,我会选择你们的两个答案,但我选择了较早的一个。
【问题讨论】:
-
@Cory 我在第二种方法中访问 bytes.length 的次数更少,但仍然比第一种方法慢。
-
哦,哎呀。我读到第二个更快......现在我很困惑。
-
也许您可以发布您的测试代码?我们假设您的测试是干净的,但我们不能确定。而且,3000次也不算多。如果您运行 1,000 次调用它 100,000 次的测试会怎么样?如果运行时间过长,请减少您正在测试的字节数。
-
@Mysticial 测试数组有100万个元素,实际数组可能是double。
-
请注意,在讨论性能时,务必说明您使用的是哪个运行时(供应商和版本、CPU 架构以及服务器与桌面)
标签: java performance optimization