【发布时间】:2011-08-08 19:20:11
【问题描述】:
假设每个线程都在做一些 FP 计算,我有兴趣
- CPU 用于切换线程而不是运行线程的时间
- 在共享内存总线上创建了多少同步流量 - 当线程共享数据时,它们必须使用同步机制
我的问题:如何设计一个测试程序来获取这些数据?
【问题讨论】:
标签: java multithreading testing
假设每个线程都在做一些 FP 计算,我有兴趣
我的问题:如何设计一个测试程序来获取这些数据?
【问题讨论】:
标签: java multithreading testing
您无法轻松区分线程切换和内存缓存争用造成的浪费。您可以测量线程争用。也就是说,在 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 中,算了吧——共享写内存比单线程更糟糕。
【讨论】:
要测量上下文切换需要多少时间,我会运行如下代码:
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用于切换线程而不是运行 他们
然后创建 n 个线程吸在队列上以执行所有 FPU。您可以使用 System.currentTimeMillis() 前后计算总时间。然后尝试使用 n+1 线程,然后尝试使用 n+2、n+3 等...
理论上,你拥有的线程越多,切换的次数就越多,处理所有 FPU 所需的时间就越多。它会让您对切换开销有一个非常粗略的了解,但这很难衡量。
在共享内存总线上创建了多少同步流量 - 线程共享数据时,必须使用同步机制
我将创建 10 个线程,通过使用 100 条消息的同步阻塞队列,将每 10000 条消息随机发送到另一个线程。每个线程都会查看阻塞队列以检查消息是否是给他们的,如果为真则将其拉出。然后,他们会尝试在不阻塞的情况下推送消息,然后重复 peek 操作等......直到队列为空并且所有线程都返回。
在其过程中,每个线程可以计算成功推送和窥视/拉取的次数与不成功的次数。然后,您将大致了解同步流量中有用的工作与无用的工作。同样,这很难衡量。
当然,您也可以考虑线程数或阻塞队列的大小。
【讨论】: