Java 中最快的循环同步是什么(ExecutorService、CyclicBarrier、X)?

2024-04-09

哪种 Java 同步结构可能提供最好的 并发、迭代处理场景的性能 像下面概述的那样固定数量的线程?实验后 我自己呆了一段时间(使用 ExecutorService 和 CyclicBarrier)并且 对结果有些惊讶,我会感激一些 专家建议,也许还有一些新想法。这里现有的问题 似乎并不主要关注性能,因此有了这个新的。 提前致谢!

该应用程序的核心是一个简单的迭代数据处理算法, 并行化,将计算负载分散到 8 个内核上 Mac Pro,运行 OS X 10.6 和 Java 1.6.0_07。待处理的数据 被分成8个块,每个块被送入一个Runnable来执行 由固定数量的线程之一。算法的并行化是 相当简单,并且它的功能按预期工作,但是 它的表现还没有达到我的预期。该应用程序似乎 花费大量时间在系统调用同步上,所以经过一些 profiling 我想知道我是否选择了最合适的 同步机制。

该算法的一个关键要求是它需要继续进行 阶段,因此线程需要在每个阶段结束时同步。 主线程准备工作(非常低的开销),将其传递给 线程,让它们处理它,然后当所有线程 完成后,重新安排工作(同样非常低的开销)并重复 循环。机器专门负责这个任务,垃圾收集 通过使用预分配项的每线程池来最小化,并且 线程数量可以是固定的(没有传入请求等, 每个 CPU 核心只有一个线程)。

V1 - 执行服务

我的第一个实现使用了带有 8 个工作线程的 ExecutorService 线程。该程序创建 8 个任务来保存工作,然后 让他们继续工作,大致如下:

// create one thread per CPU
executorService = Executors.newFixedThreadPool( 8 );
...
// now process data in cycles
while( ...) {
    // package data into 8 work items
    ...

    // create one Callable task per work item
    ...

    // submit the Callables to the worker threads
    executorService.invokeAll( taskList );
}

这在功能上运作良好(它做了它应该做的事情),并且对于 非常大的工作项确实所有 8 个 CPU 都变得高负载,因为 正如处理算法预期允许的那样(一些 工作项目将比其他工作项目完成得更快,然后闲置)。然而, 随着工作项目变得更小(这并不是真正的 程序的控制),用户CPU负载急剧下降:

blocksize | system | user | cycles/sec
256k        1.8%    85%     1.30
64k         2.5%    77%     5.6
16k         4%      64%     22.5
4096        8%      56%     86
1024       13%      38%     227
256        17%      19%     420
64         19%      17%     948
16         19%      13%     1626

传奇: - 块大小=工作项的大小(=计算步骤) - system = 系统负载,如 OS X 活动监视器(红条)所示 - user = 用户负载,如 OS X 活动监视器(绿色条)中所示 - 周期/秒 = 主 while 循环的迭代次数,越多越好

这里主要关注的是花费的时间比例很高 在系统中,似乎是由线程同步驱动的 来电。正如预期的那样,对于较小的工作项,ExecutorService.invokeAll() 需要相对更多的努力来同步线程 与每个线程中执行的工作量。但 因为 ExecutorService 比它需要的更通用 对于这个用例(如果有的话,它可以为线程排队任务 任务多于核心),我想也许会有更精简的 同步构造。

V2 - 循环屏障

下一个实现使用 CyclicBarrier 来同步 接收工作之前和完成之后的线程, 大致如下:

main() {
    // create the barrier
    barrier = new CyclicBarrier( 8 + 1 );

    // create Runable for thread, tell it about the barrier
    Runnable task = new WorkerThreadRunnable( barrier );

    // start the threads
    for( int i = 0; i < 8; i++ )
    {
        // create one thread per core
        new Thread( task ).start();
    }

    while( ... ) {
        // tell threads about the work
        ...

        // N threads + this will call await(), then system proceeds
        barrier.await();

        // ... now worker threads work on the work...

        // wait for worker threads to finish
        barrier.await();
    }
}

class WorkerThreadRunnable implements Runnable {
    CyclicBarrier barrier;

    WorkerThreadRunnable( CyclicBarrier barrier ) { this.barrier = barrier; }

    public void run()
    {
        while( true )
        {
            // wait for work
            barrier.await();

            // do the work
            ...

            // wait for everyone else to finish
            barrier.await();
        }
    }
}

同样,这在功能上运作良好(它做了它应该做的事情), 对于非常大的工作项目,实际上所有 8 个 CPU 都会变得高度 已加载,如前所述。然而,随着工作项目变得越来越小, 负载仍然急剧减少:

blocksize | system | user | cycles/sec
256k        1.9%     85%    1.30
64k         2.7%     78%    6.1
16k         5.5%     52%    25
4096        9%       29%    64
1024       11%       15%    117
256        12%        8%    169
64         12%        6.5%  285
16         12%        6%    377

对于大型工作项目,同步可以忽略不计,并且 性能与V1相同。但没想到结果 (高度专业化的)CyclicBarrier 似乎比 对于(通用)ExecutorService:吞吐量(周期/秒) 仅为V1的1/4左右。初步结论是 尽管这似乎是宣传的理想用途 对于 CyclicBarrier 来说,它的性能比 通用ExecutorService。

V3 - 等待/通知 + CyclicBarrier

似乎值得尝试替换第一个循环屏障await() 使用简单的等待/通知机制:

main() {
    // create the barrier
    // create Runable for thread, tell it about the barrier
    // start the threads

    while( ... ) {
        // tell threads about the work
        // for each: workerThreadRunnable.setWorkItem( ... );

        // ... now worker threads work on the work...

        // wait for worker threads to finish
        barrier.await();
    }
}

class WorkerThreadRunnable implements Runnable {
    CyclicBarrier barrier;
    @NotNull volatile private Callable<Integer> workItem;

    WorkerThreadRunnable( CyclicBarrier barrier ) { this.barrier = barrier; this.workItem = NO_WORK; }

    final protected void
    setWorkItem( @NotNull final Callable<Integer> callable )
    {
        synchronized( this )
        {
            workItem = callable;
            notify();
        }
    }

    public void run()
    {
        while( true )
        {
            // wait for work
            while( true )
            {
                synchronized( this )
                {
                    if( workItem != NO_WORK ) break;

                    try
                    {
                        wait();
                    }
                    catch( InterruptedException e ) { e.printStackTrace(); }
                }
            }

            // do the work
            ...

            // wait for everyone else to finish
            barrier.await();
        }
    }
}

同样,这在功能上运作良好(它做了它应该做的事情)。

blocksize | system | user | cycles/sec
256k        1.9%     85%    1.30
64k         2.4%     80%    6.3
16k         4.6%     60%    30.1
4096        8.6%     41%    98.5
1024       12%       23%    202
256        14%       11.6%  299
64         14%       10.0%  518
16         14.8%      8.7%  679

小工作项的吞吐量还是差很多 ExecutorService 的大小,但大约是 CyclicBarrier 的 2 倍。 消除一个 CyclicBarrier 就消除了一半的间隙。

V4 - 忙等待而不是等待/通知

由于此应用程序是系统上运行的第一个应用程序,并且 如果核心不忙于工作项目,它们无论如何都会闲置, 为什么不在每个线程中尝试繁忙等待工作项,即使 这会导致 CPU 不必要地旋转。工作线程代码发生变化 如下:

class WorkerThreadRunnable implements Runnable {
    // as before

    final protected void
    setWorkItem( @NotNull final Callable<Integer> callable )
    {
        workItem = callable;
    }

    public void run()
    {
        while( true )
        {
            // busy-wait for work
            while( true )
            {
                if( workItem != NO_WORK ) break;
            }

            // do the work
            ...

            // wait for everyone else to finish
            barrier.await();
        }
    }
}

功能上也运行良好(它做了它应该做的事情)。

blocksize | system | user | cycles/sec
256k        1.9%     85%    1.30
64k         2.2%     81%    6.3
16k         4.2%     62%     33
4096        7.5%     40%    107
1024       10.4%     23%    210
256        12.0%    12.0%   310
64         11.9%    10.2%   550
16         12.2%     8.6%   741

对于小型工作项目,这进一步提高了吞吐量 比 CyclicBarrier + wait/notify 变体提高 10%,这不是 微不足道。但它的吞吐量仍然比 V1 低很多 与ExecutorService。

V5 - ?

那么对于这样的情况,最好的同步机制是什么? (想必并不少见)问题?我厌倦了写我的 自己的同步机制完全替代ExecutorService (假设它太通用并且必须有一些东西 仍然可以将其取出以提高效率)。 这不是我的专业领域,我担心我会 花了很多时间调试它(因为我什至不确定 我的等待/通知和繁忙等待变体是正确的) 不确定的收益。

任何建议将不胜感激。


看来您确实不需要工作人员之间的任何同步。也许您应该考虑使用 Java 7 中提供的 ForkJoin 框架以及单独的库。一些链接:

  • Oracle 教程 http://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html
  • Doug Lea 的原始论文 http://gee.cs.oswego.edu/dl/papers/fj.pdf
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 中最快的循环同步是什么(ExecutorService、CyclicBarrier、X)? 的相关文章

  • 是否有任何简单(且最新)的 Java 框架可用于在 Swing 应用程序中嵌入电影?

    我正在构建一个小型 Swing 应用程序 我想在其中嵌入一部电影 重要的是 这个应用程序是一个 WebStart 应用程序 并且该库应该能够打包在我启动的 jnlp 中 即 不依赖于本机库 我知道并尝试过 JMF 但我认为与其他框架相比 其
  • GWT - 如何组织项目以拥有多个网页以及它们之间的导航

    我是 GET 的新手 顺便说一句 它给我留下了深刻的印象 并且发现它对于像我这样熟悉 C NET 桌面技术并愿意编写 Web 应用程序的人来说非常有吸引力 我根据 GWT Eclipse 向导生成的示例启动了自己的项目 该项目生成带有面板的
  • Spring Data JPA 选择不同

    我有一个情况 我需要建立一个select distinct a address from Person a 其中地址是 Person 内的地址实体 类型的查询 我正在使用规范动态构建我的 where 子句并使用findAll Specifi
  • 如何将 Mat (opencv) 转换为 INDArray (DL4J)?

    我希望任何人都可以帮助我解决这个任务 我正在处理一些图像分类并尝试将 OpenCv 3 2 0 和 DL4J 结合起来 我知道DL4J也包含Opencv 但我认为它没什么用 谁能帮我 如何转换成 INDArray 我尝试阅读一些问题here
  • Kotlin 未解决的参考:CLI 上 gradle 的 println

    放一个printlnkotlin 函数返回之前的语句会崩溃 堆栈跟踪 thufir dur NetBeansProjects kotlin thufir dur NetBeansProjects kotlin gradle clean bu
  • 来自十六进制代码的 Apache POI XSSFColor

    我想将单元格的前景色设置为十六进制代码中的给定颜色 例如 当我尝试将其设置为红色时 style setFillForegroundColor new XSSFColor Color decode FF0000 getIndexed 无论我在
  • Spring Security SAML2 使用 G Suite 作为 Idp

    我正在尝试使用 Spring Security 5 3 3 RELEASE 来处理 Spring Boot 应用程序中的 SAML2 身份验证 Spring Boot 应用程序将成为 SP G Suite 将成为 IDP 在我的 Maven
  • 需要使用 joda 进行灵活的日期时间转换

    我想使用 joda 解析电子邮件中的日期时间字符串 不幸的是我得到了各种不同的格式 例如 Wed 19 Jan 2011 12 52 31 0600 Wed 19 Jan 2011 10 15 34 0800 PST Wed 19 Jan
  • 自动生成Flyway的迁移SQL

    当通过 Java 代码添加新模型 字段等时 JPA Hibernate 的自动模式生成是否可以生成新的 Flyway 迁移 捕获自动生成的 SQL 并将其直接保存到新的 Flyway 迁移中 以供审查 编辑 提交到项目存储库 这将很有用 预
  • 是否可以通过编程方式查找 logback 日志文件?

    自动附加日志文件以支持电子邮件会很有用 我可以以编程方式设置路径 如以编程方式设置 Logback Appender 路径 https stackoverflow com questions 3803184 setting logback
  • 如何检测 Java 字符串中的 unicode 字符?

    假设我有一个包含 的字符串 我如何找到所有这些 un icode 字符 我应该测试他们的代码吗 我该怎么做呢 例如 给定字符串 A X 我想将其转换为 AYXY 我想对其他 unicode 字符做同样的事情 并且我不想将它们存储在某种翻译映
  • 使用 Guice 优化注册表

    你好 今天思考了一种优化 有一些疑问 语境 我正在使用 Guice 2 进行 Java 开发 在我的网络应用程序中 我有一个转换器注册表 可以即时转换为某种类型 转换器描述如下 public class StringToBoolean im
  • 迭代列表的奇怪速度差异

    我创建了两个重复两个不同值的长列表 在第一个列表中 值交替出现 在第二个列表中 一个值出现在另一个值之前 a1 object object 10 6 a2 a1 2 a1 1 2 然后我迭代它们 不对它们执行任何操作 for in a1 p
  • 红宝石接球和效率

    catch在 Ruby 中意味着跳出深度嵌套的代码 在 Java 中 例如用Java也可以达到同样的效果try catch用于处理异常 但它被认为是糟糕的解决方案 而且效率非常低 在 Ruby 中 我们有处理异常的方法begin raise
  • Java:如何为山区时间创建 TimeZone 对象?

    必须不禁用夏令时 嗯 在这个清单 http en wikipedia org wiki List of tz database time zones在 zoneinfo 时区名称中 有很多声称是 山地时间 找到最适合您想要的那个 然后使用它
  • 内部存储的安全性如何?

    我需要的 对于 Android 我需要永久保存数据 但也能够编辑 并且显然是读取 它 用户不应访问此数据 它可以包含诸如高分之类的内容 用户不得对其进行编辑 我的问题 我会 并且已经 使用过Internal Storage 但我不确定它实际
  • Freemarker 和 Struts 2,有时它计算为序列+扩展哈希

    首先我要说的是 使用 Struts2 Freemarker 真是太棒了 然而有些事情让我发疯 因为我不明白为什么会发生这种情况 我在这里问是因为也许其他人有一个想法可以分享 我有一个动作 有一个属性 说 private String myT
  • 在 C 中复制两个相邻字节的最快方法是什么?

    好吧 让我们从最明显的解决方案开始 memcpy Ptr const char a b 2 调用库函数的开销相当大 编译器有时不会优化它 我不会依赖编译器优化 但即使 GCC 很聪明 如果我将程序移植到带有垃圾编译器的更奇特的平台上 我也不
  • .NET UI 元素线程限制的原因

    我们知道 除了实例化元素的线程之外 不可能从任何线程执行操作任何 UI 元素属性的代码 我的问题是 为什么 我记得当我们使用 COM 用户界面元素时 在 COM Visual Basic 6 0 时代 所有 UI 元素都是使用 COM 类和
  • org.apache.commons.net.io.CopyStreamException:复制时捕获 IOException

    我正在尝试使用以下方法中的代码将在我的服务器中创建的一些文件复制到 FTP 但奇怪的是我随机地低于错误 我无法弄清楚发生了什么 Exception org apache commons net io CopyStreamException

随机推荐

  • Jackson JSON - 解组时出现“无单字符串构造函数/工厂方法”错误

    最简单的情况给我带来了问题 我第一次遇到这种情况 我能够解组稍微复杂一点的 json 但是这个简单的失败了 是什么导致了这种情况 为什么杰克逊只用一根弦就遇到麻烦 一个简单的类 保存用户角色的名称 public class UpdateUs
  • javascript排序数组双重排序

    我有一个数组MyArrayOfItems of Item具有如下所示对象的对象 Item ContainerID i int ContainerName SomeName string ItemID j int ItemName SomeO
  • 程序或函数!!!指定的参数太多

    我正在 SQL Server 2008 中开发我的第一个存储过程 需要有关错误消息的建议 过程或函数 xxx 指定了太多参数 执行存储过程后得到的 dbo M UPDATES 调用另一个名为的存储过程etl M Update Promo 打
  • 减少负载后 Kubernetes HPA 不会缩小规模

    当 pod 负载增加时 Kubernetes HPA 可以正常工作 但负载减少后 部署规模不会改变 这是我的 HPA 文件 apiVersion autoscaling v2beta2 kind HorizontalPodAutoscale
  • React Typescript Material-UI useStyles 不可调用

    由于某种原因 我无法调用 useStyles 因为它会出错 This expression is not callable Type never has no call signatures ts 2349 const useStyles
  • 工具栏后退单击在 Xamarin android 中不起作用

    我创建了带有后退箭头的工具栏 单击不起作用 toolbar FindViewById
  • 是否可以在 Access 2010 中创建视图?

    正如标题所说 能做到吗 我在 MSDN 页面上读到 这不受支持 但我的作业请求如何支持 可以吗 怎样才能做到呢 视图只不过是保存的 SQL SELECT 语句 这就是 Access 中保存的查询 当然 您也可以保存 DML SQL 语句 在
  • session.clear() 在 Hibernate 中如何工作

    我参考了很多文章 但我仍然不清楚什么session clear在休眠状态下执行 据我目前了解到的情况来看 当我们使用批量保存 更新时如下图 Session session SessionFactory openSession Transac
  • 由此可以得出 lambda 表达式吗?

    I know from f in list where f bar someVar select f 可以写成 list Where f gt f bar someVar 是否可以创建类似的表达式 from f in foo from b
  • 对对象列表进行排序的最简单方法

    我有一个 A 类型的对象列表 在第一次迭代中 我为每个对象分配一个双精度值 0 目前 我使用一个包装类来存储对象及其 x 值来制作可比较的列表 Scala 是否提供了一种数据类型 允许我执行以下操作 var result new Sorte
  • 附加到条形码应用程序中多个按钮的侦听器中出现异常

    好的 我正在开发一款内部条形码扫描仪 供我的公司在移动计算机和设备时使用 经过一番尝试和错误后 我目前几乎已经通过 Intent 设置了 Zxing 条码扫描仪 这就是我正在尝试做的事情 旁边三个EditText我有三个领域ImageBut
  • android AsyncTask xml解析

    我尝试在 AsyncTask 中解析一个大的 xml 文档 FeinstaubActivity 启动 但我只看到黑屏 然后返回到 RSSReaderActivity 从我启动另一个 Activity 的地方开始 Log DEBUG Sntp
  • 如何在gradle java构建脚本中访问环境变量

    如何在 gradle java 构建脚本中访问环境变量 用户级别或系统级别 我是 gradle 新手 我正在尝试使用 gradle 构建我的项目 目前我已经硬编码了第三方 jar 的路径 如下面的脚本所示 repositories flat
  • 首先在子级的父级上关闭alertDialog Android removeView()

    我想关闭 关闭警报对话框 但是当我单击按钮时valider我遇到了这个错误 我不知道哪些视图给我带来了问题 即使我的视图中没有方法删除视图 layout or v java lang IllegalStateException The sp
  • Imagick PHP 扩展 -- Montage 有帮助吗?

    我在使用 Imagick PHP 扩展生成图像时遇到了一些问题 一切工作正常 除了我的以下 蒙太奇 有白色背景 因此我不能将其覆盖在其他东西之上 如何生成具有透明背景的蒙太奇 Montage Icons gt montageImage ne
  • 如何在多租户系统中的 RabbitMQ 中使队列私有/安全?

    我已阅读开始使用 http www rabbitmq com getstarted htmlRabbitMQ 提供的指南 甚至还贡献了第六个示例暴风雨 amqp https github com paolo losi stormed amq
  • 如何计算不在列表中的日期

    我有一个位于客户的两个日期 date1 date2 之间的数据框以及到达日期 date1 lt 2019 07 29 date2 lt 2019 08 08 clients lt data frame id c 1 10 arrive c
  • 在 Python 中使用多个列表的 For 循环[重复]

    这个问题在这里已经有答案了 我正在寻找解决我的问题的方法 目前我有两个元素列表 column width 3 3 6 8 4 4 4 4 fade 100 200 300 我想要实现的是创建 for 循环 它将给出以下输出 column 3
  • 随机生成密码 Rails 3.1

    为了开发一个新的网络应用程序 我的注册页面 仅限管理员 只需要一个电子邮件字段 问题是我对 Rails 完全陌生 所以即使是像这样的基础知识对我来说也非常困难 我使用 Railscast 270 创建了我的身份验证 它使用有安全密码方法 现
  • Java 中最快的循环同步是什么(ExecutorService、CyclicBarrier、X)?

    哪种 Java 同步结构可能提供最好的 并发 迭代处理场景的性能 像下面概述的那样固定数量的线程 实验后 我自己呆了一段时间 使用 ExecutorService 和 CyclicBarrier 并且 对结果有些惊讶 我会感激一些 专家建议