【问题标题】:java multithreading performance scalingjava多线程性能扩展
【发布时间】:2012-08-14 13:37:04
【问题描述】:

你能给我解释一下这个废话吗? 我有一个基本上用数学运算填充数组的方法。不涉及 I/O 或任何东西。现在,这个方法运行大约需要 50 秒,并且代码是完全可扩展的(理论上是 100%),所以我把它分成 4 个线程,等待它们完成,然后重新组装 4 个数组。现在,我在四核处理器上运行程序,预计需要大约 15 秒,而实际上需要 58 秒。没错:它需要更长的时间!我看到 cpu 工作 100%,我知道每个线程执行 1/4 的计算,创建线程和重新组装数组总共需要大约 1-2 毫秒。 是什么导致了这种性能损失?这段时间cpu到底在做什么? 代码:http://pastebin.com/cFUgiysw

【问题讨论】:

  • 你是如何定义你的数组的?
  • 你调用什么方法?线程访问的任何同步方法都可能成为瓶颈。同样没有一些代码我们只能猜测。
  • 问题一定出在这个类上,如果我用一些随机计算运行它,它比顺序计算快 4 倍。能否提供Randomatic的出处?
  • 哦,这可能是! randomatic 使用类向量,它具有同步方法。我没有想到 :D 让我尝试修复它,然后我会发布代码
  • 天哪,它是矢量。现在它快得要命了 非常感谢!

标签: java multithreading scalability cpu procedural-generation


【解决方案1】:

线程不是这样工作的。

线程仍然是同一进程的一部分(取决于操作系统),因此就操作系统而言 - 1 个进程中的 4 个线程的 CPU 时间将与 1 个进程中的 1 个线程的调度时间相同。

此外,由于值如此之少,您不会在开销中看到可伸缩性。在 java 中重新组装数组会很昂贵。

查看诸如“上下文切换开销”之类的内容 - 当您尝试将理论映射到实践时,此类内容总是会让您感到困惑:P

我会坚持单线程方式:)

~丹

http://en.wikipedia.org/wiki/Context_switch

【讨论】:

  • 如果你说的是正确的,那么我会看到 25% 的 CPU 使用率,而不是 100%。另外,这是我第一次遇到线程问题。
  • 这仅在不同进程尝试同时使用 100% CPU 时才相关。
【解决方案2】:

很大程度上取决于您在做什么以及如何分配工作。这个问题有很多可能的原因。

  • 最可能的原因是,您正在通过一个线程将 CPU 的所有带宽用于主内存总线。如果您的数据集大于 CPU 缓存,则可能会发生这种情况。尤其是如果您有一些随机访问行为。您可以考虑尝试重用原始数组,而不是复制多个副本以减少缓存流失。
  • 您的锁定开销大于性能增益。我怀疑您使用了非常课程锁定,所以这应该不是问题。
  • 开始停止线程花费的时间太长。由于您的代码是多秒的,我也对此表示怀疑。

【讨论】:

  • 恐怕它可能是第一个:数组有点大,Java 是该死的资源猪。然而,这些大多是顺序访问
  • double[] 的数组在 Java 中的使用并不比任何其他语言更多。如果您使用的是List<Double>,它会占用资源并且会影响性​​能,所以我不建议您不要使用它。
【解决方案3】:

打开新线程需要成本。我认为它不应该长达 8 秒,但这取决于您使用的线程。一些线程需要创建您正在处理的数据的副本以保证线程安全,这可能需要一些时间。这个成本通常被称为开销。如果您正在执行的执行是不可序列化的,例如读取相同的文件或需要访问共享资源,线程可能需要相互等待,这可能需要一些时间,并且在次优条件下,它可能需要比串行执行更多的时间.我的建议是尝试检查这些不可序列化的事件,如果可能的话,将它们从线程部分中删除。还可以尝试使用较少的线程数 4 个线程用于 4 个 CPU 并不总是最佳的。

希望对您有所帮助。

【讨论】:

  • 好的。好吧,我只是在学习 java,所以这至少对朋友有好处,我会删除我答案的那部分。
  • 谢谢,正如 josefx 所说,java 使用本机线程。创建它们需要不到一毫秒(我用时间戳测量它),并且重新组装 4 个数组(每个数组大约 100 万个元素)需要不到 2 毫秒。由于计算时间太长,我怀疑这是开销的问题(理论上 15 秒到 58 秒是一个巨大的差异)。此外,除了一个不被线程修改的小对象的实例外,没有共同使用的变量。
  • 那么我几乎没有想法了。贴一些代码,我们可以发现什么?
  • 我检查了你的代码。 java很快就会启动一个新线程。但是在构造函数中复制所有这些值确实需要时间,不是吗?
【解决方案4】:

除非您不断地创建和终止线程,否则线程开销应该不是问题。四个线程同时运行对调度器来说没什么大不了的。

正如 Peter Lawrey 所说,内存带宽可能是问题所在。您的 50 秒代码在 Java 引擎上运行,它们都在争夺可用的内存带宽。 Java 引擎需要内存带宽来执行您的代码,而您的代码需要它来进行计算。

您编写“完全可扩展”,如果您的代码已编译,就会出现这种情况。由于它在 Java 引擎上运行,因此并非如此。因此,16% 的总时间增加可以看作是一个线程的平滑度与四个内存访问冲突的混乱度之间的差异。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-18
    • 2011-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多