Java 增强型 For-Loop 比传统的更快?

2024-05-25

所以我的理解是,增强的 for 循环应该更慢,因为它们必须使用迭代器。但是我的代码提供了混合结果。(是的,我知道循环逻辑占用了循环中花费的大部分时间)

对于少量迭代(100-1000),增强的 for 循环在使用和不使用 JIT 的情况下似乎都要快得多。相反,当迭代次数较多(100000000 次)时,传统循环要快得多。这里发生了什么?

public class NewMain {

    public static void main(String[] args) {

        System.out.println("Warming up");

        int warmup = 1000000;
        for (int i = 0; i < warmup; i++) {
            runForLoop();
        }
        for (int i = 0; i < warmup; i++) {
            runEnhancedFor();
        }

        System.out.println("Running");
        int iterations = 100000000;
        long start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            runForLoop();
        }
        System.out.println((System.nanoTime() - start) / iterations + "nS");

        start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            runEnhancedFor();
        }
        System.out.println((System.nanoTime() - start) / iterations + "nS");
    }

    public static final List<Integer> array = new ArrayList(100);

    public static int l;

    public static void runForLoop() {
        for (int i = 0; i < array.size(); i++) {
            l += array.get(i);
        }
    }

    public static void runEnhancedFor() {
        for (int i : array) {
            l += i;
        }
    }
}

基准测试错误。错误的非详尽列表:

  • 没有适当的热身:单次测量几乎总是错误的;
  • 在单个方法中混合多个代码路径:我们可能开始使用仅适用于方法中第一个循环的执行数据来编译该方法;
  • 来源是可预测的:如果循环编译,我们实际上可以预测结果;
  • 结果已消除死代码:如果循环可以编译,我们可以扔掉循环

花点时间听这些谈话 http://shipilev.net/pub/#benchmarking,并经过这些样本 http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/.

这就是你可以说正确的做法jmh:

@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 3, time = 1)
@Fork(3)
@State(Scope.Thread)
public class EnhancedFor {

    private static final int SIZE = 100;

    private List<Integer> list;

    @Setup
    public void setup() {
        list = new ArrayList<Integer>(SIZE);
    }


    @GenerateMicroBenchmark
    public int enhanced() {
        int s = 0;
        for (int i : list) {
            s += i;
        }
        return s;
    }

    @GenerateMicroBenchmark
    public int indexed() {
        int s = 0;
        for (int i = 0; i < list.size(); i++) {
            s += list.get(i);
        }
        return s;
    }

    @GenerateMicroBenchmark
    public void enhanced_indi(BlackHole bh) {
        for (int i : list) {
            bh.consume(i);
        }
    }

    @GenerateMicroBenchmark
    public void indexed_indi(BlackHole bh) {
        for (int i = 0; i < list.size(); i++) {
            bh.consume(list.get(i));
        }
    }

}

...这会产生以下内容:

Benchmark                         Mode   Samples      Mean   Mean error    Units
o.s.EnhancedFor.enhanced          avgt         9     8.162        0.057    ns/op
o.s.EnhancedFor.enhanced_indi     avgt         9     7.600        0.067    ns/op
o.s.EnhancedFor.indexed           avgt         9     2.226        0.091    ns/op
o.s.EnhancedFor.indexed_indi      avgt         9     2.116        0.064    ns/op

现在,增强循环和索引循环之间存在微小差异,并且通过采用不同的代码路径来访问后备存储来简单地解释这种差异。然而,解释实际上要简单得多:OP 忘记填写列表,这意味着循环体从未被执行过,而基准实际上是衡量成本size() vs iterator()!

解决这个问题:

@Setup
public void setup() {
    list = new ArrayList<Integer>(SIZE);
    for (int c = 0; c < SIZE; c++) {
        list.add(c);
    }
}

...然后产生:

Benchmark                         Mode   Samples       Mean   Mean error    Units
o.s.EnhancedFor.enhanced          avgt         9    171.154       25.892    ns/op
o.s.EnhancedFor.enhanced_indi     avgt         9    384.192        6.856    ns/op
o.s.EnhancedFor.indexed           avgt         9    148.679        1.357    ns/op
o.s.EnhancedFor.indexed_indi      avgt         9    465.684        0.860    ns/op

请注意,即使在纳米尺度上,差异也非常微小,并且非平凡的循环体将消耗差异(如果有)。这里的差异可以用我们内联的幸运来解释get() and Iterator方法,以及我们在这些内联之后可以享受的优化。

请注意indi_*测试,这会否定循环展开优化。虽然indexed成功展开时性能更好,但展开失败时则相反!

有了这样的标题,之间的区别indexed and enhanced无非是学术兴趣。找出确切生成的代码-XX:+PrintAssembly所有案例都留给读者作为练习:)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 增强型 For-Loop 比传统的更快? 的相关文章

  • 为什么 i++ 不是原子的?

    Why is i Java 中不是原子的 为了更深入地了解 Java 我尝试计算线程中循环的执行频率 所以我用了一个 private static int total 0 在主课中 我有两个线程 主题 1 打印System out prin
  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • Java - 将节点添加到列表的末尾?

    这是我所拥有的 public class Node Object data Node next Node Object data Node next this data data this next next public Object g
  • 使用 Android 发送 HTTP Post 请求

    我一直在尝试从 SO 和其他网站上的大量示例中学习 但我无法弄清楚为什么我编写的示例不起作用 我正在构建一个小型概念验证应用程序 它可以识别语音并将其 文本 作为 POST 请求发送到 node js 服务器 我已确认语音识别有效 并且服务
  • 基于代理的模拟:性能问题:Python vs NetLogo & Repast

    我正在 Python 3 中复制一小段 Sugarscape 代理模拟模型 我发现我的代码的性能比 NetLogo 慢约 3 倍 这可能是我的代码的问题 还是Python的固有限制 显然 这只是代码的一个片段 但 Python 却花费了三分
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 磁模拟

    假设我在 n m 像素的 2D 表面上有 p 个节点 我希望这些节点相互吸引 使得它们相距越远吸引力就越强 但是 如果两个节点之间的距离 比如 d A B 小于某个阈值 比如 k 那么它们就会开始排斥 谁能让我开始编写一些关于如何随时间更新
  • Draggable JS Bootstrap 模式 - 性能问题

    对于工作中的项目 我们在 JavaScript 中使用 Bootstrap Modal 窗口 我们想让一些窗口可移动 但我们遇到了 JQuery 的性能问题 myModal draggable handle modal header Exa
  • Mockito when().thenReturn 不必要地调用该方法

    我正在研究继承的代码 我编写了一个应该捕获 NullPointerException 的测试 因为它试图从 null 对象调用方法 Test expected NullPointerException class public void c
  • 如何在PreferenceActivity中添加工具栏

    我已经使用首选项创建了应用程序设置 但我注意到 我的 PreferenceActivity 中没有工具栏 如何将工具栏添加到我的 PreferenceActivity 中 My code 我的 pref xml
  • 在两个活动之间传输数据[重复]

    这个问题在这里已经有答案了 我正在尝试在两个不同的活动之间发送和接收数据 我在这个网站上看到了一些其他问题 但没有任何问题涉及保留头等舱的状态 例如 如果我想从 A 类发送一个整数 X 到 B 类 然后对整数 X 进行一些操作 然后将其发送
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • 仅将 char[] 的一部分复制到 String 中

    我有一个数组 char ch 我的问题如下 如何将 ch 2 到 ch 7 的值合并到字符串中 我想在不循环 char 数组的情况下实现这一点 有什么建议么 感谢您花时间回答我的问题 Use new String value offset
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 捕获的图像分辨率太大

    我在做什么 我允许用户捕获图像 将其存储到 SD 卡中并上传到服务器 但捕获图像的分辨率为宽度 4608 像素和高度 2592 像素 现在我想要什么 如何在不影响质量的情况下获得小分辨率图像 例如我可以获取或设置捕获的图像分辨率为原始图像分
  • 当我从 Netbeans 创建 Derby 数据库时,它存储在哪里?

    当我从 netbeans 创建 Derby 数据库时 它存储在哪里 如何将它与项目的其余部分合并到一个文件夹中 右键单击Databases gt JavaDB in the Service查看并选择Properties This will
  • java.lang.IllegalStateException:驱动程序可执行文件的路径必须由 webdriver.chrome.driver 系统属性设置 - Similiar 不回答

    尝试学习 Selenium 我打开了类似的问题 但似乎没有任何帮助 我的代码 package seleniumPractice import org openqa selenium WebDriver import org openqa s
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐

  • SWIFT 在 while 循环步骤之间更新屏幕

    我正在构建一个模拟康威生命游戏的应用程序 我试图在按下 运行 按钮时运行无限动画 这是我的代码 When RUN button is clicked call run repeat IBAction func run sender AnyO
  • 为什么在从同一解决方案引用另一个项目时会出现 FileNotFound 异常?

    我正在学习如何使用 NUnit 我的解决方案中有我的主项目 并在同一解决方案中创建了一个单独的项目 该项目将保存我的单元测试 并具有自己的命名空间 从该项目中 我添加对主项目的引用并添加 using MainProjectNamespace
  • R strsplit:根据字符分割,除非后面有特定字符

    假设我有一个字符串向量 例如 split these c File Location C Documents File Location Pete s Computer File Location 我想根据 分割该向量中的每个元素 除非后面
  • Groovy HttpBuilder - 获取失败响应的正文

    我正在尝试使用 Groovy HTTPBuilder 编写一个集成测试 该测试将验证正文中是否返回正确的错误消息以及 HTTP 409 状态消息 但是 我无法弄清楚如何在失败情况下实际访问 HTTP 响应的正文 http request E
  • PHP 中如何处理重复的表单输入?

    我有一个通过电子邮件发送 php 脚本的表单 网站的上下文使我有必要在用户单击按钮时添加重复的表单字段 如何正确处理表单输入 例如 我有一个车辆表单 当用户单击 添加车辆 时 我会附加几个车辆表单组的副本 这些表单输入具有相同的 名称 并且
  • 从 NSArray 为 UITableView 创建索引

    我读过创建索引 uitableview 旁边的 a z 的最佳方法是设置一个 nsdictionaries 数组 其中每个字典对应一个部分 并且 rowValue 键包含一个行数组 NSDictionary headerTitle gt A
  • 指针上定义的方法仍然可以用值调用

    Effective Go 文档说明如下 关于接收者的指针与值的规则是 可以在指针和值上调用值方法 但只能在指针上调用指针方法 http tip golang org doc effective go html pointers vs val
  • 提交后如何重置表单?

    我有一个简单的表格 假设它需要一个电子邮件地址 提交表单后 消息堆栈会通知用户其地址已成功提交 问题是 提交地址后 带有电子邮件的表单字段仍然包含用户输入的电子邮件地址 我该如何重置该字段 我必须为此使用 JavaScript 吗 谢谢 w
  • 动态添加元素的指令不起作用

    我正在尝试构建一个简单的无限滚动 它可以很好地加载数据 但加载后 新添加的元素的指令不起作用 这是滚动检查和数据加载指令的相关部分 directive scrollCheck function window http return func
  • Angular - UI 路由器 - 状态重入

    如何配置 UI Router 默认重新进入或重新加载状态 例如 用户想要刷新页面 因此他单击该页面后面的链接 但目前该链接不可点击 因为它会转到同一页面并且状态不会改变 使用浏览器按钮刷新确实有效 因此它会再次重新加载整个 SPA 这是不可
  • 为什么我们需要检测链表中的循环

    我看到很多关于如何检测链表中的循环的问答 但我想了解的是我们为什么要这样做 换句话说 检测链表中的循环的实际用例是什么 在现实生活中 您可能永远不需要检测链表中的循环 但是执行此操作的算法很重要 我在现实生活中多次使用它们 例如 我经常会递
  • 当 ajax 请求验证失败时,如何使用 Primefaces 突出显示 UIInput?

    验证器类 FacesValidator br gov valec sicpd util CpfValidator public class CpfValidator implements Validator Override public
  • 如何使用带有进度条的 HttpClient 下载文件?

    我创建了一个名为SiteDownload并添加了一些下载图像的链接 using System Collections Generic using System Linq using System Net using System Threa
  • 如何在Python中重命名virtualenv?

    我拼错了名字virtualenv使用以下方法初始化它 virtualenv vnev 我实际上打算创建一个名为的环境venv 尝试重命名后vnev文件夹到venv 我发现这并没有提供太多帮助 激活环境的名称仍然重命名旧的vnev mv vn
  • 如何将 Struts 2 与 Velocity 和 Tiles 结合使用

    有人能够获得与 struts 2 一起使用的速度和图块吗 我在网上查找示例或教程时遇到一些问题 从我从邮件列表中收集到的信息来看 这似乎根本不可能 但邮件已经很旧了 https struts apache org docs tiles pl
  • 即使 WebPack 构建工作正常,VS Code 显示未找到模块

    我的 VS Code 说它找不到导入 即使我的 WebPack 构建仍然有效 这是进口 import as tf from tensorflow tfjs 以及来自 VS Code 的消息 找不到模块 tensorflow tfjs 您的意
  • 使用 Nuxt 动态获取并编译模板

    我想从远程获取 svg 并通过编译使其栩栩如生 对于 赋予生命 我的意思是按类选择其中一些元素 并动态地将组件列表附加到其中 现在我只是使用 div div 我的组件如下所示 data return svg async created le
  • 如何从 lift webapp 读取文件

    我想在我的 lift 应用程序中读取 xml 文件 val data XML load new java io InputStreamReader new java io FileInputStream 文件名 编码 然而 我得到java
  • 如何从 docker 运行 Maven 包装器?

    我必须从 dockerfile 运行 Maven 包装器命令 但我不知道该怎么做 当我尝试这样写时 RUN mvnw s settings xml clean install 这个命令不起作用 我有错误mvnw not found 我的泊坞
  • Java 增强型 For-Loop 比传统的更快?

    所以我的理解是 增强的 for 循环应该更慢 因为它们必须使用迭代器 但是我的代码提供了混合结果 是的 我知道循环逻辑占用了循环中花费的大部分时间 对于少量迭代 100 1000 增强的 for 循环在使用和不使用 JIT 的情况下似乎都要