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

时间:2011-08-08 19:20:12

标签: java multithreading testing

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

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

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

3 个答案:

答案 0 :(得分:9)

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

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

所以关键是绝对最小化任何共享修改的内存结构。尽可能使所有内容都是只读的。这包括共享FIFO缓冲区(包括Executor池)..即如果你使用了同步队列 - 那么每个同步-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中,忘掉它 - 共享写存储器比单线程更差。

答案 1 :(得分:2)

要测量上下文切换所花费的时间,我会运行如下所示的内容:

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(); 
        }
    }
}

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

答案 2 :(得分:1)

  

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

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

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

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

  

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

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

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

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