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

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

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

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

您无法轻易区分由于线程切换导致的浪费以及由于内存缓存争用导致的浪费。 你可以测量线程争用。也就是说,在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对象是否避免了这种情况,但你仍然在利用赢得的操作系统停顿帮助你的吞吐量。 显然,大多数OutputStream操作会触发此类同步调用(当然通常也会填充公共流缓冲区)。

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

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

要测量上下文切换花费的时间,我将运行如下所示的操作:

public static void main(String[] args) { Object theLock = new Object(); long startTime; long endtime; synchronized( theLock ){ Thread task = new TheTask( theLock ); task.start(); try { theLock.wait(); endTime = System.currentTimeMillis(); } catch( InterruptedException e ){ // do something if interrupted } } System.out.println("Context Switch Time elapsed: " + endTime - startTime); } class TheTask extends Thread { private Object theLock; public TheTask( Object theLock ){ this.theLock = theLock; } public void run(){ synchronized( theLock ){ startTime = System.currentTimeMillis(); theLock.notify(); } } } 

您可能希望多次运行此代码以获得平均值,并确保这两个线程是唯一在您的机器中运行的线程(上下文切换只发生在这两个线程中)。

cpu用于切换线程而不是运行它们的时间

  • 假设您有1亿FPU可以执行。
  • 将它们加载到同步队列中(即线程必须在轮询时锁定队列)
  • 设n是您设备上可用的处理器数量(二重奏= 2等)

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

理论上,您拥有的线程越多,切换的次数就越多,处理所有FPU所需的时间就越多。 它将为您提供切换开销的大致概念,但这很难衡量。

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

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

在它的路上,每个线程可以成功推送和偷看/拉动与不成功的数量。 然后,您将大致了解有用工作与同步流量中的无用工作。 同样,这很难衡量。

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