我需要一些 Complex 数学库,所以我在使用不可变 Complex 的库和使用可变 Complex 的库之间犹豫不决。显然,我希望计算运行得相当快(除非它会破坏可读性等)。
所以我创建了速度可变与不可变的简单测试:
final class MutableInt {
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public MutableInt() {
this(0);
}
public MutableInt(int value) {
this.value = value;
}
}
final class ImmutableInt {
private final int value;
public ImmutableInt(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public class TestImmutableSpeed {
static long testMutable(final int arrLen) {
MutableInt[] arrMutable = new MutableInt[arrLen];
for (int i = 0; i < arrMutable.length; ++i) {
arrMutable[i] = new MutableInt(i);
for (int j = 0; j < arrMutable.length; ++j) {
arrMutable[i].setValue(arrMutable[i].getValue() + j);
}
}
long sumMutable = 0;
for (MutableInt item : arrMutable) {
sumMutable += item.getValue();
}
return sumMutable;
}
static long testImmutable(final int arrLen) {
ImmutableInt[] arrImmutable = new ImmutableInt[arrLen];
for (int i = 0; i < arrImmutable.length; ++i) {
arrImmutable[i] = new ImmutableInt(i);
for (int j = 0; j < arrImmutable.length; ++j) {
arrImmutable[i] = new ImmutableInt(arrImmutable[i].getValue() + j);
}
}
long sumImmutable = 0;
for (ImmutableInt item : arrImmutable) {
sumImmutable += item.getValue();
}
return sumImmutable;
}
public static void main(String[] args) {
final int arrLen = 1<<14;
long tmStart = System.nanoTime();
System.out.println("sum = " + testMutable(arrLen));
long tmMid = System.nanoTime();
System.out.println("sum = " + testImmutable(arrLen));
long tmEnd = System.nanoTime();
System.out.println("speed comparison mutable vs immutable:");
System.out.println("mutable " + (tmMid - tmStart)/1000000 + " ms");
System.out.println("immutable " + (tmEnd - tmMid)/1000000 + " ms");
}
}
如果测试运行太慢/太快,您可以调整数组的大小。
我运行: -server -Xms256m -XX:+AggressiveOpts 我得到:
总和 = 2199023247360 总和 = 2199023247360 速度比较可变与不可变: 可变 102 毫秒 不可变 1506 毫秒
问题:我是否缺少一些优化参数,或者不可变版本慢了 15 倍?
如果是这样,为什么有人会在其中编写包含不可变类 Complex 的数学库?不可变只是“花哨”但没用吗?
我知道不可变类作为散列映射键更安全,或者不能有竞争条件,但这是可以处理的特殊情况,没有任何地方的不变性。
编辑:我用卡尺重新运行这个微基准测试,正如一个答案所建议的那样,它的运行速度慢了 12 倍,而不是 15 倍,仍然是同一点。更改了 Caliper 基准测试的代码:
导入 com.google.caliper.Runner; 导入 com.google.caliper.SimpleBenchmark; 最后一类 MutableInt { 私有 int 值; 公共 int getValue() { 返回值; } 公共无效setValue(int值){ this.value = 值; } 公共 MutableInt() { 这(0); } 公共可变整数(整数值){ this.value = 值; } } 最终类 ImmutableInt { 私有最终 int 值; 公共不可变整数(整数值){ this.value = 值; } 公共 int getValue() { 返回值; } } 公共类 TestImmutableSpeed 扩展 SimpleBenchmark { 静态长 testMutable(final int arrLen) { MutableInt[] arrMutable = new MutableInt[arrLen]; 对于 (int i = 0; 我
卡尺输出:
0% 场景{vm=java, trial=0, benchmark=Mutable, type=-server, minMemory=-Xms256m, optimizations=-XX:+AggressiveOpts} 91614044.60 ns; ?=250338.20 ns @ 3 次试验 50% 场景{vm=java, trial=0, benchmark=Immutable, type=-server, minMemory=-Xms256m, optimizations=-XX:+AggressiveOpts} 1108057922.00 ns; ?=3920760.98 ns @ 3 次试验 基准毫秒线性运行时间 可变 91.6 == 不可变 1108.1 ===============================
请注意,如果没有 Caliper 的 JVM 输出的优化参数,则为:
0% 场景{vm=java, trial=0, benchmark=Mutable} 516562214.00 ns; ?=623120.57 ns @ 3 次试验 50% 场景{vm=java, trial=0, benchmark=Immutable} 1706758503.00 ns; ?=5842389.60 ns @ 3 次试验 基准毫秒线性运行时间 可变 517 ========= 不可变1707 ===============================
如此糟糕的参数使两个版本都变慢了,但比率不那么糟糕(但仍然不重要)。