C#Threading,多个线程调用同一个方法访问同一个数组会导致缓慢吗?

时间:2012-06-20 21:59:15

标签: c# multithreading performance

背景 我希望我的问题不要太模糊。我将尝试尽可能多地解释,并且不能发布太多代码,因为受影响的方法非常复杂和冗长。所以我的问题是我试图加快多线程的处理速度。该应用程序用于我的2D游戏引擎中的照明,在那里我在我的场景上绘制不同透明度级别的黑色矩形(目前导致滞后)。

对我来说,第一步是将相同亮度的相邻矩形批处理在一起,以减少一些渲染工作。这很好(也许它可以做得更好,但现在不是重点),现在我还实现了一个粗略的线程系统,它将每个批处理一起照明一个单独的屏幕部分。

我的问题的关键:在分析上述特定批处理方法的时间时(针对不同数量的线程处理单独的,不相交的数据集),我发现了一些奇怪的峰值。以前,当使用一个线程时,该方法需要大约12ms才能执行,奇数跳转到15ms。我没想太多。然而,当处理2或3个线程时,我或多或少地得到4~5ms,跳跃到10ms,有时甚至高达20ms。

现在我意识到,如果不检查我的代码,任何人都不可能知道原因是什么,所以我不希望如此。相反,我试图得出一些结论,并希望现在确认它们。如前所述,每个线程都对我的数据集的某些部分起作用,彼此完全不相交(它们不重叠)。但是,数据数组的入口点是通过特定类的相同实例的相同方法。所以线程都通过相同的方法访问相同的数组(但它的各个部分 - 因此我也没有使用任何锁)。这会导致意外的减速吗?

或者线程以这种方式行动可能是正常行为(执行时间变化超过规范的两倍)?另外请注意,我在需要时创建所有线程他们,让他们完成。

数据集的访问方法:

public short GetLightLevelAt(int x, int y)
{
   if (inLightingBounds(x, y))
   {
      return lightData[x, y];
   }
   else
   {
      return GuessLightLevelAt(x,y); //This won't ever happen currently, guaranteed
   }
}

4 个答案:

答案 0 :(得分:2)

也许您的数据集与垃圾CPU缓存一样大,因此执行不一致?由于存在多级缓存且无法控制它,您将永远无法确切知道将采取多少措施。

例如,如果您的数据与处理器上大多数私密缓存大小一样大,并且您从一个线程移动到三个,那么您将有效地放弃它可能带给您的任何性能优势。有时,按顺序一个接一个地做事情真的更好。

答案 1 :(得分:2)

在您的情况下,由于高峰值,它可能与上面提到的垃圾收集或上下文切换有关。

但是,在同一阵列上运行多线程可能仍会因错误共享而出现速度下降。 (即使是非重叠的分区)

例如,以下代码可能存在错误共享问题:

int[] array = new int[100000];
int a = 1;
int b = 0;

//The following loops run concurrently

//Thread A
for( int i = 0; i < 50000; i++ )
    array[i] = a;

//Thread B
for( int j = 50000; j < 100000; j++ )
    array[j] = b;

由于int a和b被声明为彼此相邻,并且很可能在彼此相同的高速缓存行上,因此发生了错误共享。

如果同时运行,此代码可能根本没有加速,可能会减慢速度。

答案 2 :(得分:1)

您遇到的的开销可能来自上下文切换或资源争用。一旦最大化可用内核,操作系统将启动上下文切换(基本上从一个进程/线程转移到另一个进程/线程)。

然而,如果不发布代码,很难分辨。

答案 3 :(得分:1)

你问过是否从几个线程切换到多个线程来运行该方法可能会导致延迟。简短的回答是没有

你没有锁定那个数组,所以与争论无关。

GC会随机显示一段代码,这样你就会看到尖峰,而不是特定的地方。

我敢打赌这不是一个高峰。这是上下文切换。你的时钟分辨率很可能很高,你的量子尺寸也很高。因此,单个上下文切换将长时间停止您的线程。

如果这是您遇到麻烦的原因,那么您需要减少线程数。你做得比它慢。