gpt4 book ai didi

java - 为什么IO的Thread甜蜜点是20倍? [以前是: Which ExecutionContext to use in playframework?]

转载 作者:行者123 更新时间:2023-12-04 17:01:39 26 4
gpt4 key购买 nike

我确实知道如何创建自己的ExecutionContext或导入全局的play框架。但是我必须承认,我远不是后面如何处理多个context/executionServices的专家。

所以我的问题是,为了提高服务的性能/行为,应该使用哪个ExecutionContext?

我测试了两个选项:

import play.api.libs.concurrent.Execution.defaultContext


implicit val executionContext = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()))

两者均具有可比的性能。

我使用的 Action 是在playframework 2.1.x中实现的。 SedisPool是我自己的对象,带有普通的sedis/jedis客户端池的额外Future包装。
def testaction(application: String, platform: String) = Action {
Async(
SedisPool.withAsyncClient[Result] { client =>
client.get(StringBuilder.newBuilder.append(application).append('-').append(platform).toString) match {
case Some(x) => Ok(x)
case None => Results.NoContent
}
})
}

与Node.js和Go中完全相同的功能相比,这种性能优势表现得好或稍慢。但仍然比Pypy慢。但是比Java中的相同方法要快(在这种情况下,使用jedis使用对redis的阻塞调用)。我们使用加特林进行负载测试。我们在Redis的基础上为简单服务进行技术的“竞争”,其标准是“编码人员付出了相同的努力”。
我已经使用fyrie进行了测试(除了我不喜欢API的事实),它的行为与此Sedis实现几乎相同。

但这是我的问题。我只想了解有关Playframework/scala的这一部分的更多信息。

有建议的行为吗?还是有人可以指点我一个更好的方向?我现在开始使用scala,距离专家还很远,但是我可以带自己逐步找到代码答案。

谢谢你的帮助。

更新-更多问题!

篡改池中的线程数后,我发现:
Runtime.getRuntime()。availableProcessors()* 20

使我的服务性能提高约15%到20%(以每秒请求数和平均响应时间来衡量),实际上使它比node.js和go略好(尽管很少)。所以我现在有更多问题:
-我测试了15倍和25倍,而20倍似乎是一个不错的选择。为什么?有任何想法吗?
-是否会有其他更好的设置?其他“最佳景点”吗?
-最佳点是20倍,还是取决于我正在运行的机器/jvm的其他参数?

更新-有关此主题的更多文档

在播放框架文档中找到更多信息。
http://www.playframework.com/documentation/2.1.0/ThreadPools

对于IO,他们确实会建议我所做的事情,但是提供了一种通过Akka.dispatchers进行操作的方法,该Akka.dispatchers可通过* .conf文件进行配置(这应该使我的操作感到高兴)。

所以现在我正在使用
implicit val redis_lookup_context: ExecutionContext = Akka.system.dispatchers.lookup("simple-redis-lookup")

通过由配置的调度程序
akka{
event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
loglevel = WARNING
actor {
simple-redis-lookup = {
fork-join-executor {
parallelism-factor = 20.0
#parallelism-min = 40
#parallelism-max = 400
}
}
}
}

一旦JVM变得“热”,它给我带来了大约5%的提升(现在将目光投向了),并提高了性能的稳定性。我的系统管理员很乐意使用这些设置而无需重建服务。

我的问题仍然存在。 为什么使用此数字?

最佳答案

我认为优化的方法是:

  • 先看一下单线程性能,然后再看
  • 看看事物如何并行化,然后
  • 冲洗并重复操作,直到获得所需的性能或您放弃为止。

  • 单线程优化

    通常,单个线程的性能将在代码的单个组件或部分上进行控制,并且可能是:
  • 绑定(bind)到 CPU的节,实际上可以从 RAM (这不是分页)中读取时绑定(bind)。 JVM和更高级别的工具通常无法区分CPU和RAM。性能分析器(例如JProfiler)对于定位代码热点非常有用)
  • 您可以通过优化代码来降低CPU使用率或RAM读写速率,从而提高性能
  • 一个分页问题,其中应用程序已用尽内存并且正在分页到磁盘或从磁盘分页
  • 您可以通过添加RAM,减少内存使用,为进程分配更多的物理RAM或减少操作系统上的内存负载来提高性能。
  • 延迟问题,其中线程正在等待从套接字,磁盘或类似设备读取数据,或者正在等待将数据提交到磁盘。
  • 您可以通过使用更快的磁盘(例如,旋转锈病-> SSD),使用更快的网络(1GE-> 10GE)或通过提高所使用的网络应用程序的响应性(调整数据库)来提高单线程性能。

  • 但是,如果可以运行多个线程,则单线程中的 延迟并不那么令人担忧。当一个线程被阻止时,另一个线程可以使用CPU(用于换出上下文并替换CPU缓存中的大多数项目的开销)。那么您应该运行多少个线程?

    多线程

    假设线程在CPU上花费了大约50%的时间,在IO上花费了50%的时间。在这种情况下,每个CPU可以被2个线程充分利用,并且您看到吞吐量提高了2倍。如果线程花费大约1%的时间使用CPU,则应该(在所有条件相同的情况下)能够同时运行100个线程。

    但是,这是可能发生许多奇怪效果的地方:
  • 上下文切换具有(一些)成本,因此理想情况下,您需要将其最小化。如果您的等待时间很少且不多,而不是那么频繁,那么您将获得更高的整体系统性能。这种效果意味着通过n x增加线程,您将永远无法获得n x吞吐量的提高。在临界点之后,随着n的增加,性能将会下降。
  • 同步,信号量和互斥锁。通常,代码的一小部分会获得信号量或互斥量,以确保一次只能输入一个(或数量有限)线程。尽管只有几个线程,但这很少会影响性能。但是,如果此代码块花费任何可观的时间,并且有很多线程,这将成为影响系统性能的主要因素。例如,假设有一个 protected 单线程块需要10毫秒来执行,例如通过查询数据库来执行。由于一次只能输入一个线程,因此您实际可以执行的最大线程数为1000ms/10ms或100。其他所有线程最终将在此块上的队列中排在后面。
  • 资源:随着并行度的提高,您将加载以前轻度加载的所有组件。随着这些负载变得越来越重,其他线程最终将阻塞,等待来自它们的数据。最终,额外的并行性最终会在计算机上的所有线程中造成延迟。这些组件包括:
  • RAM
  • 磁盘 channel
  • 网络
  • 网络服务(例如您的数据库)。我无法告诉您我已经优化Java几次,以至于DB限制了吞吐量。

  • 如果发生这种情况,则您需要重新考虑算法,更改服务器,网络或网络服务或降低并行度。

    影响您可以运行多少个线程的因素

    从上面可以看到,其中涉及大量的因素。因此,线程/核心的最佳结合点是多种原因引起的事故,包括:
  • 您使用的CPU的性能,尤其是:
  • 内核数
  • SMT或不SMT
  • 缓存量
  • 速度
  • 您有多少RAM和内存总线的速度
  • 操作系统和环境:
  • 处理器
  • 上正在执行多少其他工作
  • Windows/Linux/BSD/etc都具有不同的多任务处理特性
  • JVM版本(每个版本具有不同的特性,其中一些特性与其他版本不同)
  • 网络中的流量和拥塞以及对交换机和路由器的影响
  • 您的代码
  • 您的算法
  • 您使用的


  • 根据经验,没有一个神奇的公式可以计算出先验最佳线程数。正如您所做的那样,最好凭经验解决此问题(如上所示)。如果需要一概而论,则需要在您选择的操作系统上通过不同的CPU体系结构,内存和网络进行性能采样。

    几个易于观察的指标在这里很有用:
  • 每个内核的CPU利用率-帮助检测进程是否受CPU限制
  • 平均负载-这报告进程(或线程,如果使用LWP)如何等待CPU。如果这个数字上升到大于CPU核心数的数字,则说明您的CPU核心肯定受CPU限制。

  • 如果需要优化,请获取最佳的性能分析工具。您将需要一种特定的工具来监视操作系统(例如,DTrace用于Solaris),以及一个用于JVM(我个人很喜欢JProfiler)。这些工具可让您精确放大我在上面描述的区域。

    结论

    碰巧在特定的Scala库版本,JVM版本,OS,服务器和Redis服务器上运行的特定代码使每个线程大约有95%的时间在等待I/O。 (如果运行单线程,您会发现CPU负载约为5%)。

    在这种配置下,这允许大约20个线程最佳地共享每个CPU。

    这是最好的地方,因为:
  • 如果运行的线程较少,那么您将浪费CPU周期来等待数据
  • 如果您运行更多线程:
  • 架构的一个组件(例如磁盘或CPU <-> RAM总线)已饱和,从而阻止了额外的吞吐量(在这种情况下,您会发现CPU利用率低于或远低于〜90%),或者
  • 线程上下文切换成本开始超过添加线程的增量 yield (您将看到CPU利用率达到> 95%)
  • 关于java - 为什么IO的Thread甜蜜点是20倍? [以前是: Which ExecutionContext to use in playframework?],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16211649/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com