2010-11-15 39 views
2

我试图改变一些代码,所以它可以使用多线程。当围绕代码放置一个Runnable时,我偶然发现了一个性能损失。线程内部的代码比外部线程慢?

对于澄清:原代码,让我们把它

//doSomething 

了一个Runnable围绕这样的:

Runnable r = new Runnable() 
{ 
    public void run() 
    { 
     //doSomething 
    } 
} 

然后我提交了可运行的ChachedThreadPool的ExecutorService。这是我对多线程代码的第一步,以查看代码是否像原始代码一样以一个线程运行。

但是,情况并非如此。在大约2秒内执行// doSomething,Runnable执行约2.5秒。 我需要一提的是一些其他的代码,说:// doSomethingElse,里面一个Runnable 没有性能损失相比原来// doSomethingElse

我的猜测是,// DoSomething的有一些操作在一个线程工作时不那么快,但我不知道它可能还是什么,在方面是区别//doSomethingElse

难道是使用final int []/float []数组使Runnable慢得多吗? // doSomethingElse代码也使用了一些决赛,但// doSomething使用更多。这是我能想到的唯一的事情。

不幸的是,代码很长且不在上下文中,但我会在这里发布它。对于那些知道Mean Shift分段算法的人来说,这是代表每个像素正在计算均值漂移向量的代码的一部分。通过每个像素中的for循环

for(int i=0; i<L; i++) 

运行。

timer.start(); // this is where I start the timer 
// Initialize mode table used for basin of attraction 
char[] modeTable = new char [L]; // (L is a class property and is about 100,000) 
Arrays.fill(modeTable, (char)0); 
int[] pointList = new int [L]; 

// Allcocate memory for yk (current vector) 
double[] yk = new double [lN]; // (lN is a final int, defined earlier) 

// Allocate memory for Mh (mean shift vector) 
double[] Mh = new double [lN]; 

int idxs2 = 0; int idxd2 = 0; 
for (int i = 0; i < L; i++) { 
    // if a mode was already assigned to this data point 
    // then skip this point, otherwise proceed to 
    // find its mode by applying mean shift... 
    if (modeTable[i] == 1) { 
     continue; 
    } 

    // initialize point list... 
    int pointCount = 0; 

    // Assign window center (window centers are 
    // initialized by createLattice to be the point 
    // data[i]) 
    idxs2 = i*lN; 
    for (int j=0; j<lN; j++) 
    yk[j] = sdata[idxs2+j]; // (sdata is an earlier defined final float[] of about 100,000 items) 

    // Calculate the mean shift vector using the lattice 
    /*****************************************************/ 
    // Initialize mean shift vector 
    for (int j = 0; j < lN; j++) { 
     Mh[j] = 0; 
    } 
    double wsuml = 0; 
    double weight; 

    // find bucket of yk 
    int cBucket1 = (int) yk[0] + 1; 
    int cBucket2 = (int) yk[1] + 1; 
    int cBucket3 = (int) (yk[2] - sMinsFinal) + 1; 
    int cBucket = cBucket1 + nBuck1*(cBucket2 + nBuck2*cBucket3); 
    for (int j=0; j<27; j++) { 
     idxd2 = buckets[cBucket+bucNeigh[j]]; // (buckets is a final int[] of about 75,000 items) 
     // list parse, crt point is cHeadList 
     while (idxd2>=0) { 
      idxs2 = lN*idxd2; 
      // determine if inside search window 
      double el = sdata[idxs2+0]-yk[0]; 
      double diff = el*el; 
      el = sdata[idxs2+1]-yk[1]; 
      diff += el*el; 
      //... 
      idxd2 = slist[idxd2]; // (slist is a final int[] of about 100,000 items) 
     } 
    } 
    //... 
} 
timer.end(); // this is where I stop the timer. 

有更多的代码,但最后while循环是我第一次注意到,在性能上的差异。

任何人都可以想到为什么这段代码在Runnable内运行得比原来慢吗?

谢谢。

编辑:测量的时间是内部的代码,所以不含启动的线程的。

+1

如果你拿一些运行260ms的东西,把它包装在一个Runnable中,并且只运行一次,那么是的,由于线程的启动开销会花费更长的时间。一次运行一些东西并不是一个很好的线程使用。请解释你为什么要运行这个多线程。 – 2010-11-15 14:34:51

+0

我提到其他代码在Runnable内部和在外部一样快。但是的确,总的运行时间比那个长。此代码完成后,Runnable内部需要约3.5秒,而外部则需要2秒。我想以多线程的形式运行这个代码,作为应该实现的实时应用程序的一部分。巨大的for-loop(从0到L)可以分成几部分。 – RemiX 2010-11-15 14:43:21

+0

根据您的编辑,并行代码(和测试)的外观如何? – gustafc 2010-11-15 14:55:55

回答

3

所有的代码总是运行在“线程内”。

您看到的减速很可能是由多线程增加的开销引起的。尝试对代码的不同部分进行并行化 - 任务不应该太大,也不要太小。例如,你可能会更好地将每个外部循环作为一个单独的任务运行,而不是最内部的循环。

有分裂的任务没有唯一正确的办法,但是,这一切都取决于数据的容貌和目标机的样子(2个核,8个核,512个核心?)。

编辑:如果反复运行测试,会发生什么?例如,如果你这样做:

Executor executor = ...; 
for (int i = 0; i < 10; i++) { 
    final int lap = i; 
    Runnable r = new Runnable() { 
     public void run() { 
      long start = System.currentTimeMillis(); 
      //doSomething 
      long duration = System.currentTimeMillis() - start; 
      System.out.printf("Lap %d: %d ms%n", lap, duration); 
     } 
    }; 
    executor.execute(r); 
} 

你是否注意到结果有什么不同?

+0

实际上,完成的任务相当大(需要2秒)。对不起,我没有提到。那时开销会是500毫秒,我不相信。另外,我在代码中测量时间。 – RemiX 2010-11-15 14:57:00

0

当你创建一个新的线程,你总是有一个开销。如果你有一小段代码,你可能会遇到性能损失。 一旦你有更多的代码(更大的任务),你让你的并行化获得的性能提升(线程的代码不一定会跑得更快,但你同时做两件事)。

只是一个细节:这到底有多大小可任务是如此并行还是值得的决定是在并行计算:)

+0

其实这个完整的任务是非常大的,因为我已经编辑到我原来​​的文章。花费2秒完成(使用Runnable 2.5秒)。我想,500毫秒的开销太大了。 – RemiX 2010-11-15 15:02:13

+0

我认为你应该做的数学运算是:在一个线程中执行整个程序需要多长时间,并行执行需要多长时间。 这额外500ms可能是由其他线程抢占“你的”,我猜想:) – JSBach 2010-11-16 08:19:20

1

我个人看不出有任何理由已知的话题。任何程序至少有一个线程。所有线程都是平等的。所有线程默认创建为中等优先级(5)。因此,代码应该在主应用程序线程和您打开的其他线程中显示相同的性能。

您确定您正在测量“执行某些操作”的时间,而不是程序运行的总时间?我相信你正在测量操作时间以及创建和启动线程所需的时间。

+0

是的,我很确定,因为我已经编辑到我原来​​的职位;)谢谢。 – RemiX 2010-11-15 14:58:07

0

你还没有解释你如何测量所花的时间。显然有线程启动成本,但我推断你正在使用一些机制,确保这些成本不扭曲你的图片。

一般来说,在测量性能时,测量小块工件时容易误导。我希望能够延长至少1000倍的运行时间,将整个过程放在一个循环或其他任何环境中。

在这里,一个“无主题”和“线程”的情况下,实际上是你不必一个线程消失(如已经指出的那样,你总是有一个线程)和2个线程之间的不同所以现在JVM必须在两个线程之间进行调解。对于这种工作,我不明白为什么这应该有所作为,但这是一个区别。

我想使用一个很好的分析工具来真正挖掘到这一点。

+0

我已将我的计时方法添加到原始帖子中。它位于大循环之外,总计大约2秒(使用Runnable的时间为2.5秒)。关于一对两个线程,如果第一个线程(启动第二个线程的线程)等待第二个线程完成,这仍然会成为问题吗? – RemiX 2010-11-15 15:01:02

+0

无论如何,我怀疑它甚至是一个问题,我想我们可以很容易想象,如果两个线程都很忙,那么还有额外的工作,但我不明白你的情况如何适用。 – djna 2010-11-15 15:17:22