0

我正在编写一个压力测试,它将向远程服务器发出许多调用。我想在测试后收集以下统计数据:

  1. 远程调用的延迟(以毫秒为单位)。
  2. 远程服务器每秒可以处理的操作数。

我可以成功获得(2),但我遇到了(1)的问题。我当前的实现与另一个 SO question中显示的非常相似。我在那个问题中描述了同样的问题:System.currentTimeMillis()当使用多个线程运行测试时,使用报告的延迟比预期的要长。

我分析了这个问题,我很肯定问题来自线程交错(有关详细信息,请参阅我对上面链接的另一个问题的回答),这System.currentTimeMillis()不是解决这个问题的方法。

看来我应该可以使用 来做到这一点java.lang.management,它有一些有趣的方法,例如:

  1. ThreadMXBean.getCurrentThreadCpuTime()
  2. ThreadMXBean.getCurrentThreadUserTime()
  3. ThreadInfo.getWaitedTime()
  4. ThreadInfo.getBlockedTime()

我的问题是,即使我已经阅读了 API,我仍然不清楚这些方法中的哪一种可以给我想要的东西。在我链接的另一个 SO 问题的背景下,这就是我需要的:

long start_time = **rightMethodToCall()**;

result = restTemplate.getForObject("Some URL",String.class);
long difference = (**rightMethodToCall()** - start_time);

因此difference,即使在多线程环境中,这也给了我一个很好的远程调用时间的近似值。

限制:我想避免用块保护该代码synchronized块,因为我的程序有其他线程,我想允许继续执行。

编辑:提供更多信息。:

问题是这样的:我想为远程呼叫计时,而只是远程呼叫。如果我使用System.currentTimeMillisor System.nanoTime(),并且如果我的线程数多于内核数,那么我可以让这个线程交错:

  1. Thread1: 长 start_time ...
  2. 线程1:结果= ...
  3. Thread2: 长 start_time ...
  4. 线程2:结果= ...
  5. Thread2:长差...
  6. Thread1:长差...

如果发生这种情况,那么 Thread2 计算的差异是正确的,但 Thread1 计算的差异是不正确的(它会大于应有的值)。也就是说,为了测量Thread1的差异,我想排除第4行和第5行的时间。这个时间是线程WAITING吗?

以不同的方式总结问题,以帮助其他人更好地理解它(这句话是@jason-c 在他的评论中所说的。):

[我] 尝试为远程调用计时,但使用多个线程运行测试只是为了增加测试量。

4

1 回答 1

1

使用System.nanoTime()(但请参阅此答案末尾的更新)。

您绝对不想使用当前线程的 CPU 或用户时间,因为用户感知的延迟是挂钟时间,而不是线程 CPU 时间。您也不想使用当前线程的阻塞或等待时间,因为它测量每个线程的争用时间,这也不能准确地代表您要测量的内容。

System.nanoTime()将从高分辨率返回相对准确的结果(尽管从技术上讲粒度只能保证与 一样好或更好currentTimeMillis(),但实际上它往往要好得多,通常使用硬件时钟或其他性能计时器实现,例如QueryPerformanceCounter在 Windows 或clock_gettimeLinux 上)具有固定参考点的时钟,并将准确测量您要测量的内容。

long start_time = System.nanoTime();
result = restTemplate.getForObject("Some URL",String.class);
long difference = (System.nanoTime() - start_time);
long milliseconds = difference / 1000000;

System.nanoTime()确实有它自己的一系列问题,但要小心不要陷入偏执狂;对于大多数应用程序来说,它已经足够了。您只是不想将它用于例如将音频样本发送到硬件或其他东西时的精确计时(无论如何您都不会直接在 Java 中这样做)。

更新1:

更重要的是,你怎么知道测量值比预期的要长?如果您的测量显示真实的挂钟时间,并且某些线程比其他线程花费的时间更长,那么这仍然是用户感知延迟的准确表示,因为某些用户遇到更长的延迟时间。

更新 2(基于评论中的澄清):

那时我的大部分答案仍然有效;但出于不同的原因。

使用每线程时间并不能为您提供准确的表示,因为当远程请求仍在处理时,线程可能处于空闲/非活动状态,因此即使它是感知延迟的一部分,您也会从测量中排除该时间。

远程服务器需要更长的时间来处理您正在发出的同时请求会引入进一步的不准确性 - 这是您要添加的一个额外变量(尽管它可能是可以接受的,因为它代表远程服务器正忙)。

Wall time 也不完全准确,因为正如您所见,本地线程开销的变化可能会增加额外的延迟,这通常不会出现在单请求客户端应用程序中(尽管这仍然可以作为客户端应用程序的代表,即多线程,但它是您无法控制的变量)。

在这两者中,与每线程时间相比,挂墙时间仍然会让你更接近实际结果,这就是我在上面留下之前答案的原因。你有几个选择:

  • 您可以在单个线程上连续地进行测试——这最终是实现您所陈述的要求的最准确方法
  • 您不能创建比核心更多的线程,例如,一个固定大小的线程池与每个核心的绑定亲和性(棘手:Java 线程亲和性)以及作为每个核心上的任务运行的测量。当然,由于您无法控制的底层机制的同步,这仍然会增加任何变量。这可能会降低交错的风险(特别是如果您设置了关联性),但您仍然无法完全控制 JVM 正在运行的其他线程或系统上其他不相关的进程。
  • 您可以测量远程服务器上的请求处理时间;当然,这并没有考虑到网络延迟。
  • 您可以继续使用当前方法并对结果进行一些统计分析以消除异常值。
  • 您根本无法衡量这一点,只需进行用户测试并等待对其进行评论,然后再尝试对其进行优化(即与人们一起衡量它,无论如何,他们都是您正在开发的对象)。如果优化它的唯一原因是为了用户体验,那么很可能是用户体验愉快并且等待时间完全可以接受。

此外,这些都不能保证系统上其他不相关的线程不会影响您的时间,但这就是为什么 a)多次运行测试并平均(显然)和 b)设置一个可接受的您可以接受的定时误差要求(您真的需要知道这一点,例如 0.1ms 精度吗?)。

就个人而言,我要么采用第一种单线程方法,让它在一夜之间或一个周末运行,要么使用您现有的方法并从结果中删除异常值并接受时间误差。您的目标是在令人满意的误差范围内找到一个现实的估计。在决定什么是可接受的时,您还需要考虑最终将如何处理这些信息。

于 2013-11-05T22:53:16.747 回答