【问题标题】:how can you measure the time spent in a context switch under java platformjava平台下如何测量上下文切换所花费的时间
【发布时间】:2011-08-08 19:20:11
【问题描述】:

假设每个线程都在做一些 FP 计算,我有兴趣

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

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

【问题讨论】:

    标签: java multithreading testing


    【解决方案1】:

    您无法轻松区分线程切换和内存缓存争用造成的浪费。您可以测量线程争用。也就是说,在 linux 上,您可以 cat /proc/PID/XXX 并获得大量详细的每个线程统计信息。但是,由于抢先式调度程序不会让自己陷入困境,因此无论您使用多少线程,您每秒都不会得到超过 30 次 ctx 开关......而且那个时间将是相对的小vs你正在做的工作量。上下文切换的真正成本是缓存污染。例如一旦您重新切换回上下文,您很可能会出现大部分缓存未命中。因此,操作系统时间和上下文切换计数的价值很小。

    真正有价值的是线程间缓存线脏的比率。根据 CPU 的不同,对等 CPU 读取之后的缓存线脏比缓存未命中要慢 - 因为您必须强制对等 CPU 将其值写入主内存,然后才能开始读取。. 一些CPU 可让您从对等缓存行中提取而无需访问 main-mem。

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

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

    一般来说,我的经验是,对于常见的字节数组/对象数组等而言,单线程比多线程更快。至少对于我尝试过的简单排序/过滤算法而言。根据我的经验,这在 Java 和 C 中都是如此。我没有尝试过 FPU 集成操作(如除法、sqrt),其中缓存行可能不是一个因素。

    基本上,如果您是单个 CPU,则不会出现缓存线问题(除非操作系统总是在共享线程中刷新缓存),但多线程可以为您带来收益。在超线程中,情况相同。在单 CPU 共享 L2/L3 缓存配置(例如 AMD)中,您可能会发现一些好处。在多 CPU Intel BUS 中,算了吧——共享写内存比单线程更糟糕。

    【讨论】:

    • 如果您不是要设计应用程序,而是要简单地衡量性能差异(重读您的问题)。然后希望该算法可以线性划分,然后传递到可配置数量的线程中,其中 1 可能具有特殊的备用代码路径。然后运行每一个(可能有预先退出 zip up /proc/self/*)。还使用记录/报告每个线程的开始/结束的纳米时间(而不是其增量)。
    【解决方案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(); 
            }
        }
    }
    

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

    【讨论】:

      【解决方案3】:

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

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

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

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-07
        • 2016-07-04
        • 2013-12-11
        • 2012-11-22
        • 1970-01-01
        • 1970-01-01
        • 2019-05-09
        • 1970-01-01
        相关资源
        最近更新 更多