2

此代码来自 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 位、Intel i5-2400 CPU、4 核、4 GB RAM 上完成。当然,在 Release 配置中。

在我的真实程序中,我试图计算图像直方图,并行版本的运行速度要慢 10 倍。这样的计算任务,在每次循环调用都非常快的情况下,能否成功地与 TPL 并行化?

编辑。

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

4

1 回答 1

4

因为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");
}
于 2013-08-13T07:56:46.240 回答