我正在学校做一个数独求解器,我们正在进行一场小小的表演比赛。现在,我的算法在第一次运行时(大约2.5ms)非常快,但是当我解决相同的谜题10 000次(每次运行大约0.5ms)时更快。当然,这些时间取决于正在解决的难题。我知道JVM在多次调用方法时会做一些优化,这是我怀疑发生的事情。
我认为我不能进一步优化算法本身(虽然我会继续寻找),所以我想知道是否可以复制JVM完成的一些优化。
注意:编译为本机代码不是一个选项
谢谢!
编辑:所有这些VM选项都很不错,但在算法竞赛中并非真正“合法”,因为每个人都可以使用这些选项并获得性能提升。我正在寻找代码优化。
答案 0 :(得分:3)
您可以将许多微优化应用于您的代码。这个Article of JavaWorld给出了概述。可以找到更多想法on this Page。
但是,我不希望它有任何大的收益。如果您无法再优化您的算法,请尝试优化数据结构以便于访问并利用缓存局部性。答案 1 :(得分:2)
如果你有一个“热身”游戏来让JIT编译游戏类,你可以添加选项
-XX:CompileThreshold=20
确保大多数热点都是在第一次运行时编译的。然后,后续运行将在没有任何额外(或非常少)编译的情况下运行。
见
答案 2 :(得分:1)
您可能会尝试减少可用的堆内存,以便垃圾收集器更快地预热。内存管理在“正常”条件下运行需要一段时间,减小内存大小可以加快速度。
答案 3 :(得分:1)
tl; dr :不,JVM完成的大多数优化都不能用Java字节码表示。
就其本质而言,Java字节代码在很高的层次上定义了您的代码。
这是设计上的原因之一,也是JVM可以完成所有优化的原因之一:字节代码描述了相对较高级别的操作,并将实际执行细节留给了JVM。
由于这一事实,大多数优化不能以字节代码本身表示。
例如,JVM规范指定超出数组边界的每个数组访问都必须抛出ArrayIndexOutOfBoundsException
(参见VM Spec 2.5.14)。
然而,如果智能JVM知道它们已经发生(例如,当迭代数组时,那么JVM可以在迭代之前实际执行边界检查并在每次迭代期间跳过它),它可以优化掉许多检查。 )。
此优化根本无法用字节代码表示,因为边界检查隐式且无法避免表示数组访问的字节码。
答案 4 :(得分:0)
你可以通过调用`System.gc();'
强制垃圾收集在“重”操作之前,比如主循环答案 5 :(得分:0)
JVM与JIT(即时)编译器一起使用:它可以动态地将Java字节码编译为本机机器代码。它进行了大量复杂的分析,以确定何时以及如何编译到本机代码。它所看到的一件事是运行不止一次的代码 - 如果你运行一些代码10.000次,就像你正在做的那样,JVM很可能决定将它编译为本机代码然后运行它是有利的每次再次执行代码时。
据我所知,JVM无法控制何时执行此操作。 (编辑:可能不是真的,正如mdma在他的回答中指出的那样 - 有一些高级-XX
选项。
Sun有两个版本的JVM:客户端JVM,它被调整为快速启动但执行不太激进的优化,以及服务器JVM,它具有较慢的启动时间,但会进行更积极的优化。
您可以尝试使用服务器JVM运行程序:
java -server com.mypackage.MyProgram
请注意,据我所知,只有64位版本的Sun Java for Windows包含服务器JVM。
答案 6 :(得分:0)
您应该检查编译器生成的字节码,并检查是否存在一些无法优化的区域。否则将是一次命中和未命中操作。
我建议在过于集中搜索其他优化之前仔细检查您的算法;)
答案 7 :(得分:0)
我知道这不是你想要的,但我会质疑比赛的方法(假设它是什么)。所有主要基准(SPEC等)都明确考虑了预热期(通常是几分钟),以确保测量工作负载本身,而不是JIT编译等临时效果......
另外,请注意,在一个工作台上运行2.5ms的东西是毫无意义的 - 像Windows这样的操作系统上的许多JVM都不能很好地利用System.currentTimeMillis()之类的东西来测量时间。你需要多次重复它才有意义......我建议在30到60年代范围内变得具有合理的统计相关性。
至于被问到的问题 - 你可以走这条路,但这根本不值得。仅供参考,您最大的成功将是内联,虚拟化和逃逸分析。所有这些都消除了重要的路径长度和潜在的内存带宽挑战。
答案 8 :(得分:0)
嗯,你可以尝试的一些技巧是:
1)强化减少,例如而不是除以2,你可以只使用右移
int i = 10 / 2; // division by 2
i = 10 >> 1; // does the same
2)使用原始类型而不是引用类型,例如int over Integer
3)首选数组到集合
4)使用复合语句,例如i + = 2而不是i = i + 2
5)计数循环而不是向上,例如
for (int i = 10; i >= 0; i--) {
System.out.println(i);
}
(only useful if you can compare against zero)
6)如果可能,在实例变量上使用final关键字