在之前的文章中我分享了三种从一个数组中随机取一个值的方法,分别是:使用随机数、使用 int 递增,使用 atomicinteger 递增。其中后两者都是循序取,并非真的随机。从上次的测试结果中,随机数方案相对后两者性能差异比较大。但是当时多线程的测试都是在达到了 CPU 性能瓶颈的情况下测得,旧闻如下:性能测试中的随机数性能问题探索。
最近又遇到相同的问题,为了更加准确反映三个方案的性能差异,我打算在压力相对偏低的情况下重新测试。毕竟之前测试的都是几百万的 QPS,日常使用中根本不会用的这么高的场景。
用例设计
使用固定 QPS 模型进行测试,这样可以一次测试中获取多组测试数据,然后每次执行增加 10ms 的延迟,模拟接口响应耗时。这次没有覆盖从数组中取值,因为对于所有的实现方案,这个步骤是相同的,只是获取一个数组的索引。
用例如下:
import com.funtester.frame.SourceCode
import com.funtester.frame.execute.FunQpsConcurrent
import groovy.util.logging.Log4j2
import java.util.concurrent.atomic.AtomicInteger
@Log4j2
class Ts extends SourceCode {
static void main(String[] args) {
def total = 1000_0000
def index = new AtomicInteger()
int i = 0
def test = {
i++ % total
// index.getAndIncrement() % total
getRandomInt(total)
sleep(0.01)
}
new FunQpsConcurrent(test, "测试随机性能").start()
}
}
测试结果
本次数据统计使用 Intellij 自带的 profiler 获取,之前常用的 jconsole 和 jvisualvm 突然不好用,连接不上 JVM 了。
下面分享一下测试结果
方案 | TPS(万) | CPU(百分比) |
---|---|---|
random | 1 | 13 |
int | 1 | 15 |
atomic | 1 | 12 |
random | 2 | 26 |
int | 2 | 30 |
atomic | 2 | 24 |
random | 3 | 38 |
int | 3 | 46 |
atomic | 3 | 38 |
random | 4 | 51 |
int | 4 | 60 |
atomic | 4 | 51 |
random | 5 | 64 |
int | 5 | 75 |
atomic | 5 | 64 |
从以上数据看出,随机数的 CPU 消耗量是非常大的,这里我没有把内存列出来,因为前两项测试中并没有看到内存大较大差异。但是在执行 atomic 方案的方案的时候内存升高较多。随机数和 int 方案堆内存使用最大量约 50M,而 atomic 使用量最大 130M,不过这个增量可以接受的。
除了以上,我还发现一个有趣的现象,如果我先测 1 万,后测试 5 万。或者我先测 5 万,然后降低到 1 万。两个差距还是挺大的。下面分享一下数据,按照测试时间记录数据。
方案 | TPS(万) | CPU(百分比) |
---|---|---|
atomic | 0.5 | 1 |
atomic | 1 | 3 |
atomic | 2 | 10 |
atomic | 3 | 24 |
atomic | 4 | 40 |
atomic | 5 | 64 |
atomic | 4 | 50 |
atomic | 3 | 38 |
atomic | 2 | 26 |
atomic | 1 | 12 |
atomic | 0.5 | 6 |
看了监控,怀疑是后面活跃线程持续增长导致的,那么是否可以认为,这里 CPU 使用率差异都是在线程的切换导致的,我现在倾向于是的。后面有机会我会对线程数这个参数进行对比测试,继续优化 FunTester 框架。