如何在多任务环境中测量多线程处理时间?

时间:2013-09-06 18:14:23

标签: multithreading language-agnostic multicore time-measurement

由于我在(抢占式)多任务,多核环境中运行多线程程序的性能评估测试,因此可以定期更换该进程。我想计算延迟,即只计算进程处于活动状态的持续时间。这将允许我推断性能在非多任务环境中的表现,即,只有一个程序正在运行(大多数时间),或者在不同的工作负载上。

通常会测量两种时间:

  • 挂钟时间(即流程开始后的时间),但这包括交换流程的时间。
  • 处理器时间(即所有线程使用的CPU时间总和),但这对计算进程的延迟没有用。

我相信我需要的是单个线程的完成时间,这可能与任何线程由于线程之间的任务依赖性结构所使用的最大CPU时间不同。例如,在具有2个线程的进程中,线程1在运行时的前三分之二(CPU时间t)中负载很重,而线程2在该进程的后三分之二中加载(同样,对于CPU时间t)。在这种情况下:

  • 挂钟时间将返回3t / 2 +上下文切换时间+其他进程在其间使用的时间,
  • 所有线程的最大CPU时间将返回接近t的值,并且
  • 总CPU时间接近2t。
  • 我希望收到的测量结果是完工时间,即3t / 2。

此外,多线程本身带来了不确定性。这个问题可能需要多次运行测试并总结结果。

此外,延迟还取决于操作系统如何调度线程;如果进程的某些线程等待CPU而其他线程运行,则事情会变得更复杂。但是让我们忘记这一点。

有没有一种有效的方法来计算/估算这个完工时间?要提供代码示例,请使用任何编程语言,但最好使用Linux上的C或C ++。

PS:我理解这个makepan的定义与调度问题的定义不同。调度问题中使用的定义类似于挂钟时间。

1 个答案:

答案 0 :(得分:3)

问题的重新制定

我编写了一个多线程应用程序,需要X秒才能在我的K-core机器上执行。

如何估算程序在单核计算机上运行需要多长时间?

根据经验

显而易见的解决方案是让计算机具有一个核心,然后运行您的应用程序,并根据需要使用挂钟时间和/或CPU时间。

...哦,等等,你的电脑已经有一个核心(它还有一些核心,但我们不需要使用它们)。

如何做到这一点取决于操作系统,但我从谷歌找到的第一批结果之一解释了Windows XP和Vista的一些方法。

http://masolution.blogspot.com/2008/01/how-to-use-only-one-core-of-multi-core.html

接下来你可以:

  • 将您的应用程序流程分配给单核心的亲和力。 (您也可以在代码中执行此操作)。
  • 启动您的操作系统只知道您的一个核心。 (之后再切换回来)

独立并行

通过分析估计这一点需要了解您的程序,并行方法等等。

作为一个简单的例子,假设我编写了一个多线程程序,它计算pi的第十亿个十进制数和e的第十亿个十进制数。

我的代码如下:

public static int main()
{
    Task t1 = new Task( calculatePiDigit );
    Task t2 = new Task( calculateEDigit );
    t1.Start();
    t2.Start();
    Task.waitall( t1, t2 );
}

之前发生的图表如下:

enter image description here

显然这些是独立的。

在这种情况下

  • 时间计算PiDigit()本身。
  • 时间calculateEDigit()本身。
  • 将时间加在一起。

2阶段管道

当任务不是独立的时,您将无法将各个时间加在一起。

在下一个示例中,我创建了一个多线程应用程序:获取10个图像,将它们转换为灰度,然后运行线检测算法。由于某些外部原因,不允许每个图像不按顺序处理。因此,我创建了一个管道模式。

我的代码看起来像这样:

ConcurrentQueue<Image> originalImages = new ConcurrentQueue<Image>();
ConcurrentQueue<Image> grayscaledImages = new ConcurrentQueue<Image>();
ConcurrentQueue<Image> completedImages = new ConcurrentQueue<Image>();

public static int main()
{
     PipeLineStage p1 = new PipeLineStage(originalImages, grayScale, grayscaledImages);
     PipeLineStage p2 = new PipeLineStage(grayscaledImages, lineDetect, completedImages);

     p1.Start();
     p2.Start();

     originalImages.add( image1 );
     originalImages.add( image2 );
     //... 
     originalImages.add( image10 );

     originalImages.add( CancellationToken );

     Task.WaitAll( p1, p2 );
}

以数据为中心发生在图表之前:

enter image description here

如果此程序设计为开始的顺序程序,出于缓存原因,在移动到下一个图像之前,一次取一个图像并将它们移动到完成会更有效。

无论如何,我们知道GrayScale()将被调用10次而LineDetection()将被调用10次,所以我们可以单独计时,然后将它们乘以10。

但是推送/弹出/轮询ConcurrentQueues的成本呢?

假设图像很大,那个时间可以忽略不计。

如果有数百万个小图像,每个阶段都有许多消费者,那么当程序按顺序运行时,您可能会发现等待锁,互斥锁等的开销非常小(假设其数量为在关键部分中执行的工作很小,例如在并发队列中。

上下文切换的成本?

看看这个问题:

How to estimate the thread context switching overhead?

基本上,您将在多核环境和单核环境中进行上下文切换。

执行上下文切换的开销非常小,但每秒也会发生很多次。

危险在于上下文切换之间缓存完全中断。

例如,理想情况下:

  • 由于执行GrayScale
  • ,image1被加载到缓存中
  • LineItection在image1上运行得更快,因为它在缓存中

然而,这可能发生:

  • 由于执行GrayScale
  • ,image1被加载到缓存中 由于执行GrayScale ,
  • image2被加载到缓存中
  • 现在,管道阶段2在image1上运行LineDetection,但是image1不再在缓存中。

结论

在运行它的同一环境中没有什么能比计时更快。

接下来最好是尽可能地模拟这个环境。

无论如何,了解您的程序设计应该可以让您了解在新环境中会发生什么。