如何衡量在java平台下上下文切换所花费的时间

Sec*_*ish 20 java testing multithreading

让我们假设每个线程正在进行一些FP计算,我感兴趣

  • CPU在切换线程时使用了多长时间而不是运行它们
  • 共享内存总线上创建了多少同步流量 - 当线程共享数据时,它们必须使用同步机制

我的问题:如何设计测试程序来获取这些数据?

小智 10

您无法轻易区分由于线程切换导致的浪费以及由于内存缓存争用导致的浪费.你可以测量线程争用.也就是说,在linux上,你可以cat/proc/PID/XXX并获得大量详细的每线程统计信息.但是,由于先发制人的调度程序不会在脚下射击,无论你使用多少线程,你都不会超过每秒30个ctx开关.而且这个时间相对较长与您正在进行的工作量相比较小..上下文切换的实际成本是缓存污染.例如,一旦您重新进行上下文切换,您很可能会遇到大多数缓存未命中.因此,操作系统时间和上下文切换计数的值最小.

真正有价值的是线程间缓存线污垢的比例.根据CPU的不同,高速缓存行脏,然后是对等CPU读取是SLOWER而不是高速缓存未命中 - 因为你必须强制对等CPU将它的值写入main-mem才能开始读取. CPU允许您从对等缓存行中提取而无需访问main-mem.

所以关键是绝对最小化任何共享的修改内存结构.尽可能将所有内容设置为只读.这包括共享FIFO缓冲区(包括执行程序池)..即如果你使用了同步队列 - 那么每个sync-op都是共享的脏内存区域.而且,如果速率足够高,它可能会触发操作系统陷阱停止,等待对等线程的互斥锁.

理想的做法是分割RAM,将固定数量的工作人员分配给一个大的工作单元,然后使用倒计时锁存器或其他一些内存屏障(这样每个线程只会触摸一次).理想情况下,任何临时缓冲区都是预先分配的,而不是进出共享内存池(然后导致缓存争用).Java'synchronized'块利用(幕后)共享的哈希表内存空间,从而触发不需要的脏读,我还没有确定java 5 Lock对象是否避免了这种情况,但你仍然在利用赢得的OS停顿帮助你的吞吐量.显然,大多数OutputStream操作会触发此类同步调用(当然通常也会填充公共流缓冲区).

一般来说,我的经验是单线程比常规字节数组/对象数组等的多线程更快.至少我已经尝试过简单的排序/过滤算法.根据我的经验,这在Java和C中都是如此.我没有尝试过FPU intesive ops(比如divides,sqrt),其中缓存行可能不是一个因素.

基本上,如果你是一个单独的CPU,你就没有缓存行问题(除非操作系统总是在共享线程中刷新缓存),但多线程购买的东西比什么都没有.在超线程中,这是同样的交易.在单CPU共享L2/L3缓存配置(例如AMD)中,您可能会发现一些好处.在多CP​​U Intel BUS中,忘掉它 - 共享写内存比单线程更差.


Jér*_*nge 1

cpu 有多少时间用于切换线程而不是运行线程

  • 假设您有 1 亿个 FPU 需要执行。
  • 将它们加载到同步队列中(即线程在轮询时必须锁定队列)
  • 令 n 为设备上可用的处理器数量(duo=2,等等...)

然后创建n个线程吸吮队列来执行所有FPU。您可以计算System.currentTimeMillis()之前和之后的总时间。然后尝试使用 n+1 个线程,然后是 n+2、n+3,等等...

理论上,线程越多,切换就越多,处理所有 FPU 所需的时间就越多。它会让您对交换开销有一个非常粗略的了解,但这很难测量。

在共享内存总线上创建了多少同步流量 - 当线程共享数据时,它们必须使用同步机制

我将创建 10 个线程,通过使用包含 100 条消息的同步阻塞队列,将每 10 000 条消息随机发送到另一个线程。每个线程都会查看阻塞队列以检查消息是否是给他们的,如果是则将其拉出。然后,他们会尝试在不阻塞的情况下推送消息,然后重复查看操作等......直到队列为空且所有线程返回。

在其过程中,每个线程都可以计算成功推送和查看/拉取与不成功的次数。然后,您将大致了解同步流量中的有用工作和无用工作。同样,这很难衡量。

当然,您也可以调整线程数或阻塞队列的大小。