并行.为了性能

本文关键字:性能 并行 | 更新日期: 2023-09-27 18:27:30

此代码来自Microsoft文章http://msdn.microsoft.com/en-us/library/dd460703.aspx,有小改动:

        const int size = 10000000;
        int[] nums = new int[size];
        Parallel.For(0, size, i => {nums[i] = 1;});
        long total = 0;
        Parallel.For<long>(
            0, size, () => 0,
            (j, loop, subtotal) =>
            {
                return subtotal + nums[j];
            },
            (x) => Interlocked.Add(ref total, x) 
        );
        if (total != size)
        {
            Console.WriteLine("Error");
        }

非并行循环版本为:

        for (int i = 0; i < size; ++i)
        {
            total += nums[i];
        }

当我使用StopWatch类测量循环执行时间时,我发现并行版本慢了10-20%。测试是在Windows 7 64位、英特尔i5-2400 CPU、4核、4GB RAM上完成的。当然,在Release配置中。

在我的实际程序中,我试图计算一个图像直方图,并行版本的运行速度慢了10倍。当每次循环调用都很快时,这种计算任务能否成功地与TPL并行?

编辑

最后,我设法用Parallel减少了50%以上的直方图计算执行时间。例如,当将整个图像分割成一定数量的块时。现在,每个循环体调用都处理整个块,而不是一个像素。

并行.为了性能

因为Parallel.For应该用于有点健康的事情,而不是简单的数字求和!仅仅使用委托(j, loop, subtotal) =>可能就足以多给10-20%的时间。我们甚至没有谈到线程开销。如果能在for周期中看到一些针对代表夏季的基准,并且不仅能看到"真实世界"时间,还能看到CPU时间,那将是一件有趣的事情。

我甚至添加了一个与"简单"委托的比较,该委托的作用与Parallel.For<>委托相同。

嗯。。。现在我有一些32位的数字,在我的电脑上(AMD六核)

32 bits
Parallel: Ticks:      74581, Total ProcessTime:    2496016
Base    : Ticks:      90395, Total ProcessTime:     312002
Func    : Ticks:     147037, Total ProcessTime:     468003

Parallel在墙时间稍微快一点,但在处理器时间慢8倍:-)

但在64位:

64 bits
Parallel: Ticks:     104326, Total ProcessTime:    2652017
Base    : Ticks:      51664, Total ProcessTime:     156001
Func    : Ticks:      77861, Total ProcessTime:     312002

修改代码:

Console.WriteLine("{0} bits", IntPtr.Size == 4 ? 32 : 64);
var cp = Process.GetCurrentProcess();
cp.PriorityClass = ProcessPriorityClass.High;
const int size = 10000000;
int[] nums = new int[size];
Parallel.For(0, size, i => { nums[i] = 1; });
GC.Collect();
GC.WaitForPendingFinalizers();
long total = 0;
{
    TimeSpan start = cp.TotalProcessorTime;
    Stopwatch sw = Stopwatch.StartNew();
    Parallel.For<long>(
        0, size, () => 0,
        (j, loop, subtotal) =>
        {
            return subtotal + nums[j];
        },
        (x) => Interlocked.Add(ref total, x)
    );
    sw.Stop();
    TimeSpan end = cp.TotalProcessorTime;
    Console.WriteLine("Parallel: Ticks: {0,10}, Total ProcessTime: {1,10}", sw.ElapsedTicks, (end - start).Ticks);
}
if (total != size)
{
    Console.WriteLine("Error");
}
GC.Collect();
GC.WaitForPendingFinalizers();
total = 0;
{
    TimeSpan start = cp.TotalProcessorTime;
    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < size; ++i)
    {
        total += nums[i];
    }
    sw.Stop();
    TimeSpan end = cp.TotalProcessorTime;
    Console.WriteLine("Base    : Ticks: {0,10}, Total ProcessTime: {1,10}", sw.ElapsedTicks, (end - start).Ticks);
}
if (total != size)
{
    Console.WriteLine("Error");
}
GC.Collect();
GC.WaitForPendingFinalizers();
total = 0;
Func<int, int, long, long> adder = (j, loop, subtotal) =>
{
    return subtotal + nums[j];
};
{
    TimeSpan start = cp.TotalProcessorTime;
    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < size; ++i)
    {
        total = adder(i, 0, total);
    }
    sw.Stop();
    TimeSpan end = cp.TotalProcessorTime;
    Console.WriteLine("Func    : Ticks: {0,10}, Total ProcessTime: {1,10}", sw.ElapsedTicks, (end - start).Ticks);
}
if (total != size)
{
    Console.WriteLine("Error");
}