- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我想了解 Java 对连续 for 循环做了哪些优化。更准确地说,我正在尝试检查是否执行了循环融合。从理论上讲,我期望这种优化不是自动完成的,并且期望确认融合版本比具有两个循环的版本更快。
但是,在运行基准测试后,结果显示两个独立(连续)循环比一个循环完成所有工作要快。
我已经尝试使用 JMH 创建基准测试并获得了相同的结果。
我使用了javap
命令,它显示为具有两个循环的源文件生成的字节码实际上对应于正在执行的两个循环(没有执行循环展开或其他优化)。
正在测量 BenchmarkMultipleLoops.java
的代码:
private void work() {
List<Capsule> intermediate = new ArrayList<>();
List<String> res = new ArrayList<>();
int totalLength = 0;
for (Capsule c : caps) {
if(c.getNumber() > 100000000){
intermediate.add(c);
}
}
for (Capsule c : intermediate) {
String s = "new_word" + c.getNumber();
res.add(s);
}
//Loop to assure the end result (res) is used for something
for(String s : res){
totalLength += s.length();
}
System.out.println(totalLength);
}
正在测量 BenchmarkSingleLoop.java
的代码:
private void work(){
List<String> res = new ArrayList<>();
int totalLength = 0;
for (Capsule c : caps) {
if(c.getNumber() > 100000000){
String s = "new_word" + c.getNumber();
res.add(s);
}
}
//Loop to assure the end result (res) is used for something
for(String s : res){
totalLength += s.length();
}
System.out.println(totalLength);
}
这里是 Capsule.java
的代码:
public class Capsule {
private int number;
private String word;
public Capsule(int number, String word) {
this.number = number;
this.word = word;
}
public int getNumber() {
return number;
}
@Override
public String toString() {
return "{" + number +
", " + word + '}';
}
}
caps
是 ArrayList<Capsule>
一开始有 2000 万个元素是这样填充的:
private void populate() {
Random r = new Random(3);
for(int n = 0; n < POPSIZE; n++){
int randomN = r.nextInt();
Capsule c = new Capsule(randomN, "word" + randomN);
caps.add(c);
}
}
在测量之前,执行预热阶段。
我对每个基准运行了 10 次,换句话说,work()
每个基准测试执行 10 次方法,完成的平均时间如下所示(以秒为单位)。每次迭代后,GC 都会执行一些 hibernate :
在 Intel i7-7500U (Kaby Lake) 上运行的 OpenJDK 1.8.0_144。
为什么 MultipleLoops 版本比 SingleLoop 版本更快,即使它必须遍历两种不同的数据结构?
如评论中所建议,如果我更改实现以计算 totalLength
在生成字符串时,避免创建 res
list ,单循环版本变得更快。
但是,该变量仅被引入,以便在创建结果列表后完成一些工作,以避免在未对它们进行任何操作时丢弃元素。
换句话说,预期的结果是生成最终列表。但这个建议有助于更好地理解正在发生的事情。
结果:
这是我用于 JMH 基准测试的代码的链接: https://gist.github.com/FranciscoRibeiro/2d3928761f76e4f7cecfcfcdf7fc96d5
结果:
最佳答案
我调查了这个“现象”,看起来像是得到了答案。
让我们将 .jvmArgs("-verbose:gc")
添加到 JMHs OptionsBuilder
。 1 次迭代的结果:
Single Loop: [Full GC (Ergonomics) [PSYoungGen: 2097664K->0K(2446848K)] [ParOldGen: 3899819K->4574771K(5592576K)] 5997483K->4574771K(8039424K), [Metaspace: 6208K->6208K(1056768K)], 5.0438301 secs] [Times: user=37.92 sys=0.10, real=5.05 secs] 4.954 s/op
Multiple Loops: [Full GC (Ergonomics) [PSYoungGen: 2097664K->0K(2446848K)] [ParOldGen: 3899819K->4490913K(5592576K)] 5997483K->4490913K(8039424K), [Metaspace: 6208K->6208K(1056768K)], 3.7991573 secs] [Times: user=26.84 sys=0.08, real=3.80 secs] 4.187 s/op
JVM 为 GC 花费了大量的 CPU 时间。每 2 次测试运行一次,JVM 必须进行 Full GC(将 600Mb 移动到 OldGen 并从之前的周期中收集 1.5Gb 的垃圾)。两个垃圾收集器都完成了相同的工作,但多循环测试用例的应用时间减少了约 25%。如果我们将 POPSIZE
减少到 10_000_000 或添加到 bh.consume()
Thread.sleep(3000)
之前,或者添加 - XX:+UseG1GC
到 JVM args,然后多循环提升效果消失了。我用 .addProfiler(GCProfiler.class)
再次运行它。主要区别:
Multiple Loops: gc.churn.PS_Eden_Space 374.417 ± 23 MB/sec
Single Loop: gc.churn.PS_Eden_Space 336.037 MB/sec ± 19 MB/sec
我认为,我们在这种特定情况下看到了加速,因为旧的比较和交换 GC 算法在多次测试运行时存在 CPU 瓶颈,并且从早期运行中收集垃圾使用了额外的“无意义”循环。如果您有足够的 RAM,则使用 @Threads(2)
进行复制会更容易。如果您尝试分析 Single_Loop 测试,它看起来像这样:
关于java - 为什么两个独立的循环比一个快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48952494/
我是 PHP 新手。我一直在脚本中使用 for 循环、while 循环、foreach 循环。我想知道 哪个性能更好? 选择循环的标准是什么? 当我们在另一个循环中循环时应该使用哪个? 我一直想知道要
我在高中的编程课上,我的作业是制作一个基本的小计和顶级计算器,但我在一家餐馆工作,所以制作一个只能让你在一种食物中读到。因此,我尝试让它能够接收多种食品并将它们添加到一个价格变量中。抱歉,如果某些代码
这是我正在学习的一本教科书。 var ingredients = ["eggs", "milk", "flour", "sugar", "baking soda", "baking powder",
我正在从字符串中提取数字并将其传递给函数。我想给它加 1,然后返回字符串,同时保留前导零。我可以使用 while 循环来完成此操作,但不能使用 for 循环。 for 循环只是跳过零。 var add
编辑:我已经在程序的输出中进行了编辑。 该程序要求估计给定值 mu。用户给出一个值 mu,同时还提供了四个不等于 1 的不同数字(称为 w、x、y、z)。然后,程序尝试使用 de Jaeger 公式找
我正在编写一个算法,该算法对一个整数数组从末尾到开头执行一个大循环,其中包含一个 if 条件。第一次条件为假时,循环可以终止。 因此,对于 for 循环,如果条件为假,它会继续迭代并进行简单的变量更改
现在我已经习惯了在内存非常有限的情况下进行编程,但我没有答案的一个问题是:哪个内存效率更高;- for(;;) 或 while() ?还是它们可以平等互换?如果有的话,还要对效率问题发表评论! 最佳答
这个问题已经有答案了: How do I compare strings in Java? (23 个回答) 已关闭 8 年前。 我正在尝试创建一个小程序,我可以在其中读取该程序的单词。如果单词有 6
这个问题在这里已经有了答案: python : list index out of range error while iteratively popping elements (12 个答案) 关
我正在尝试向用户请求 4 到 10 之间的整数。如果他们回答超出该范围,它将进入循环。当用户第一次正确输入数字时,它不会中断并继续执行 else 语句。如果用户在 else 语句中正确输入数字,它将正
我尝试创建一个带有嵌套 foreach 循环的列表。第一个循环是循环一些数字,第二个循环是循环日期。我想给一个日期写一个数字。所以还有另一个功能来检查它。但结果是数字多次写入日期。 Out 是这样的:
我想要做的事情是使用循环创建一个数组,然后在另一个类中调用该数组,这不会做,也可能永远不会做。解决这个问题最好的方法是什么?我已经寻找了所有解决方案,但它们无法编译。感谢您的帮助。 import ja
我尝试创建一个带有嵌套 foreach 循环的列表。第一个循环是循环一些数字,第二个循环是循环日期。我想给一个日期写一个数字。所以还有另一个功能来检查它。但结果是数字多次写入日期。 Out 是这样的:
我正在模拟一家快餐店三个多小时。这三个小时分为 18 个间隔,每个间隔 600 秒。每个间隔都会输出有关这 600 秒内发生的情况的统计信息。 我原来的结构是这样的: int i; for (i=0;
这个问题已经有答案了: IE8 for...in enumerator (3 个回答) How do I check if an object has a specific property in J
哪个对性能更好?这可能与其他编程语言不一致,所以如果它们不同,或者如果你能用你对特定语言的知识回答我的问题,请解释。 我将使用 c++ 作为示例,但我想知道它在 java、c 或任何其他主流语言中的工
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
我是 C 编程和编写代码的新手,以确定 M 测试用例的质因数分解。如果我一次只扫描一次,该功能本身就可以工作,但是当我尝试执行 M 次时却惨遭失败。 我不知道为什么 scanf() 循环有问题。 in
这个问题已经有答案了: JavaScript by reference vs. by value [duplicate] (4 个回答) 已关闭 3 年前。 我在使用 TSlint 时遇到问题,并且理
我尝试在下面的代码中添加 foreach 或 for 循环,以便为 Charts.js 创建多个数据集。这将允许我在此折线图上创建多条线。 我有一个 PHP 对象,我可以对其进行编码以稍后填充变量,但
我是一名优秀的程序员,十分优秀!