我有一个Java应用运行在Sun 1.6 32位VM/Solaris 10 (x86)/Nahelem 8核(每核2线程)上。
应用中的一个具体用例是响应一些外部消息。在我的性能测试环境中,当我在接收外部输入的同一线程中准备并发送响应时,比将消息移交给单独的线程来发送响应要快约50微秒。我使用了ThreadPoolExecutor
和SynchronousQueue
来完成移交。
根据您的经验,在将任务调度到线程池并被执行之间预期的延迟是多少?过去,您尝试过哪些方法来改善这种情况?
我有一个Java应用运行在Sun 1.6 32位VM/Solaris 10 (x86)/Nahelem 8核(每核2线程)上。
应用中的一个具体用例是响应一些外部消息。在我的性能测试环境中,当我在接收外部输入的同一线程中准备并发送响应时,比将消息移交给单独的线程来发送响应要快约50微秒。我使用了ThreadPoolExecutor
和SynchronousQueue
来完成移交。
根据您的经验,在将任务调度到线程池并被执行之间预期的延迟是多少?过去,您尝试过哪些方法来改善这种情况?
"可接受的延迟"完全取决于您的应用程序。如果您有非常严格的延迟要求,那么在同一个线程上处理所有内容确实可以帮助。幸运的是,大多数应用程序的要求并没有那么严格。
当然,如果只有一个线程能够接收请求,那么将该线程绑定以计算响应意味着您无法接受任何其他请求。根据您所做的工作,您可以使用异步IO等技术来避免"每个请求一个线程"的模型,但我认为这要难得多,并且仍然涉及线程上下文切换。
有时候,为了避免有太多线程处理请求,排队请求是合适的选择:如果您的处理过程是CPU密集型的,那么拥有数百个线程就没有太多意义——最好是有一个生产者/消费者任务队列,大约分配一个任务给每个核心的线程。当然,如果正确设置了ThreadPoolExecutor
,那么它基本上就是这样做的。如果您的请求大量时间都在等待外部服务(包括磁盘,但主要是其他网络服务)...此时,您需要在潜在的阻塞调用中使用异步执行模型,或者承担线程上下文切换的负担并拥有大量线程,依靠线程调度程序使其足够高效。
总之,延迟要求可能很困难-根据我的经验,它们比吞吐量要求更难以扩展。但这确实取决于具体情况。
在我的经验中(Solaris 10/Opteron),50微秒对于移交来说有点高,LBQ通常在30-35微秒的范围内,而LTQ(LinkedTransferQueue)比这还要快大约5微秒。正如其他回复所述,SynchronousQueue可能会稍慢一些,因为提供不会返回直到另一个线程已经取走。
根据我的结果,在这方面Solaris 10明显比Linux慢,Linux的时间少于10微秒。
这实际上取决于几件事,在峰值负载下:
如果您知道这些问题的答案,那么基于性能考虑,您应该清楚是否应该在接收线程中处理或移交给处理线程。
你为什么不使用LinkedBlockingQueue
让生产者可以排队几个项目,而不是使用SynchronousQueue
?至少有一个包含1个项目的队列,这样您可以获得更好的并行性。
"准备"过程与"响应"过程的速度如何?如果它们太昂贵,您可以使用线程池来处理多个线程的响应吗?
不是同一个任务,但是 "是" - 队列过于普遍而不能用于时间关键任务。我们已经集中精力避免同步以处理所有事件。请查看以下提示