我在 Linux 机器(AMD 6 核,16 GB RAM)上使用 JVM(Oracle 1.7 64 位)来查看应用程序中的线程数如何影响性能。我希望测量上下文切换在哪一点会降低性能。
我创建了一个创建线程执行池的小应用程序:
Executors.newFixedThreadPool(numThreads)
我numThreads
每次运行程序时都会进行调整,以查看它的效果。
然后我将numThread
作业(的实例java.util.concurrent.Callable
)提交到池中。每个人增加一个AtomicInteger
,做一些工作(创建一个随机整数数组并对其进行洗牌),然后睡一会儿。这个想法是模拟 Web 服务调用。最后,作业重新提交到池中,因此我始终有numThreads
作业工作。
我正在测量吞吐量,如每分钟处理的作业数量。
有了几千个线程,我每分钟可以处理多达 400,000 个作业。超过 8000 个线程,结果开始变化很大,这表明上下文切换正在成为一个问题。但是我可以继续将线程数增加到 30,000 并且仍然获得更高的吞吐量(每分钟 420,000 到 570,000 个作业之间)。
现在的问题是:我得到了一个java.lang.OutOfMemoryError: Unable to create new native thread
拥有超过 31,000 个工作岗位的职位。我试过设置-Xmx6000M
这没有帮助。我试过玩,-Xss
但这也无济于事。
我读过这ulimit
可能很有用,但增加ulimit -u 64000
并没有改变任何东西。
有关信息:
[root@apollo ant]# ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 127557
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
所以问题 #1:我必须做什么才能创建更大的线程池?
问题 #2:我应该在什么阶段看到上下文切换真正降低吞吐量并导致进程停止?
这是一些结果,在我对其进行修改以进行更多处理(如建议的那样)并开始记录平均响应时间(也如建议的那样)之后。
// ( (n_cores x t_request) / (t_request - t_wait) ) + 1
// 300 ms wait, 10ms work, roughly 310ms per job => ideal response time, 310ms
// ideal num threads = 1860 / 10 + 1 = 187 threads
//
// results:
//
// 100 => 19,000 thruput, 312ms response, cpu < 50%
// 150 => 28,500 thruput, 314ms response, cpu 50%
// 180 => 34,000 thruput, 318ms response, cpu 60%
// 190 => 35,800 thruput, 317ms response, cpu 65%
// 200 => 37,800 thruput, 319ms response, cpu 70%
// 230 => 42,900 thruput, 321ms response, cpu 80%
// 270 => 50,000 thruput, 324ms response, cpu 80%
// 350 => 64,000 thruput, 329ms response, cpu 90%
// 400 => 72,000 thruput, 335ms response, cpu >90%
// 500 => 87,500 thruput, 343ms response, cpu >95%
// 700 => 100,000 thruput, 430ms response, cpu >99%
// 1000 => 100,000 thruput, 600ms response, cpu >99%
// 2000 => 105,000 thruput, 1100ms response, cpu >99%
// 5000 => 131,000 thruput, 1600ms response, cpu >99%
// 10000 => 131,000 thruput, 2700ms response, cpu >99%, 16GB Virtual size
// 20000 => 140,000 thruput, 4000ms response, cpu >99%, 27GB Virtual size
// 30000 => 133,000 thruput, 2800ms response, cpu >99%, 37GB Virtual size
// 40000 => - thruput, -ms response, cpu >99%, >39GB Virtual size => java.lang.OutOfMemoryError: unable to create new native thread
我将它们解释为:
1) 即使应用程序在 96.7% 的时间内处于休眠状态,仍然需要完成大量线程切换 2) 上下文切换是可测量的,并显示在响应时间中。
有趣的是,在调整应用程序时,您可能会选择可接受的响应时间,例如 400 毫秒,并增加线程数,直到获得该响应时间,在这种情况下,这将使应用程序处理大约 95,000 个请求分钟。
人们常说,理想的线程数接近核心数。在具有等待时间(阻塞线程,例如等待数据库或 Web 服务响应)的应用程序中,计算需要考虑到这一点(参见上面的公式)。但是,当您查看结果或调整到特定响应时间时,即使是理论上的理想也不是实际理想。