何时使用 Parallel.For?

发布于 2024-09-19 13:17:05 字数 173 浏览 6 评论 0原文

我最近转向了 C#.net 4。

我喜欢 Parallel.For,但不确定何时使用、何时不使用。 我知道当顺序对我来说不重要时 - 我会使用它。

但是是否有关于使用 Parallels 的开销的测试?意思是,如果我的循环只运行 10 次(并且执行很少的逻辑)——我应该避免并行吗?有什么经验法则吗?

I have recently moved to C#.net 4.

I love Parallel.For, but not sure when to use and when not to.
I know that when order is not important for me - I'll use it.

But are there any tests regarding the overhead of working with Parallels? Meaning, if my loop runs only 10 times (and performs very little logic) - should I avoid Parallels? Are there any thumb rules?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

情魔剑神 2024-09-26 13:17:05

我会避免使用 Parallel.For 除非性能是一个问题。

编写并发运行的代码通常比编写单线程代码更困难。此外,如果由于并发问题而出错,则可能很难对其进行调试。例如,该错误可能只是有时发生并且不容易重现。除非您有提高性能的特定需求,否则我建议您保持简单并在单个线程上使用普通循环。

I would avoid using Parallel.For unless performance is an issue.

Writing code that runs concurrently is in general harder than writing single threaded code. Furthermore if you make an error due to a concurrency issue it can be difficult to debug it. For example the bug might occur only sometimes and not be easily reproducible. Unless you have a specific need for increased performance I would suggest that you keep it simple and use an ordinary loop on a single thread.

ぺ禁宫浮华殁 2024-09-26 13:17:05

Parallel.For 循环使用 ThreadPool 通过每次循环迭代调用一次委托来执行循环中的工作。

Parallel.For 工作原理的总体思路如下所示:

public static void MyParallelFor(int inclusiveLowerBound, int exclusiveUpperBound, Action<int> body)
{
    // Get the number of processors, initialize the number of remaining
    // threads, and set the starting point for the iteration.
    int numProcs = Environment.ProcessorCount;
    int remainingWorkItems = numProcs;
    int nextIteration = inclusiveLowerBound;
    using (ManualResetEvent mre = new ManualResetEvent(false))
    {
        // Create each of the work items.
        for (int p = 0; p < numProcs; p++)
        {
            ThreadPool.QueueUserWorkItem(delegate
            {
                int index;
                while ((index = Interlocked.Increment(ref nextIteration) - 1) < exclusiveUpperBound)
                    body(index);

                if (Interlocked.Decrement(ref remainingWorkItems) == 0)
                    mre.Set();
            });
        }
        // Wait for all threads to complete.
        mre.WaitOne();
    }
}

Parallel.For 返回 ParallelLoopResult 值类型,其中包含已完成循环的详细信息。它的重载之一如下:

public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int> body);

重要的是要认识到并行执行并不总是比串行执行更快。要决定是否使用并行,您必须估计循环每次迭代所需的工作负载。如果循环执行的实际工作相对于线程同步成本较小,则最好使用普通循环。

这是串行 for 循环性能比并行更快的示例之一:

static void Main(string[] args)
{
    Action<int> action = new Action<int>(SimpleMethod);

    // ordinary For loop performance estimation
    var sw = Stopwatch.StartNew();

    for(int i = 0; i < 1000; i++)
        action(i);

    Console.WriteLine("{0} sec.", sw.Elapsed.TotalSeconds);

    // parallel For loop performance estimation
    sw = Stopwatch.StartNew();

    Parallel.For(0, 1000, action);

    Console.WriteLine("{0} sec.", sw.Elapsed.TotalSeconds);
}

static void SimpleMethod(int index)
{
    int d = 1;
    int result = index / d;
}

输出:

0.0001963 sec.
0.0346729 sec.

The Parallel.For loop uses ThreadPool to execute work in a loop by invoking a delegate once per each iteration of a loop.

The general idea of how Parallel.For works can be presented as follows:

public static void MyParallelFor(int inclusiveLowerBound, int exclusiveUpperBound, Action<int> body)
{
    // Get the number of processors, initialize the number of remaining
    // threads, and set the starting point for the iteration.
    int numProcs = Environment.ProcessorCount;
    int remainingWorkItems = numProcs;
    int nextIteration = inclusiveLowerBound;
    using (ManualResetEvent mre = new ManualResetEvent(false))
    {
        // Create each of the work items.
        for (int p = 0; p < numProcs; p++)
        {
            ThreadPool.QueueUserWorkItem(delegate
            {
                int index;
                while ((index = Interlocked.Increment(ref nextIteration) - 1) < exclusiveUpperBound)
                    body(index);

                if (Interlocked.Decrement(ref remainingWorkItems) == 0)
                    mre.Set();
            });
        }
        // Wait for all threads to complete.
        mre.WaitOne();
    }
}

Parallel.For returns ParallelLoopResult value type, which contains details on the completed loop. One of its overloads is as follows:

public static ParallelLoopResult For(int fromInclusive, int toExclusive, Action<int> body);

It's important to realize that parallel execution is not always faster than serial execution. To decide whether to use parallel or not you have to estimate the workload that will do per iteration of a loop. If the actual work being performed by the loop is small relatively to thread synchronization cost, it's better to use ordinary loop.

This is one of example when serial for loop performance is faster that parallel:

static void Main(string[] args)
{
    Action<int> action = new Action<int>(SimpleMethod);

    // ordinary For loop performance estimation
    var sw = Stopwatch.StartNew();

    for(int i = 0; i < 1000; i++)
        action(i);

    Console.WriteLine("{0} sec.", sw.Elapsed.TotalSeconds);

    // parallel For loop performance estimation
    sw = Stopwatch.StartNew();

    Parallel.For(0, 1000, action);

    Console.WriteLine("{0} sec.", sw.Elapsed.TotalSeconds);
}

static void SimpleMethod(int index)
{
    int d = 1;
    int result = index / d;
}

Output:

0.0001963 sec.
0.0346729 sec.
旧街凉风 2024-09-26 13:17:05

引用 SQLite FAQ: '线程是邪恶的。避免它们'

并行化对于性能很有用。应用程序性能优化是软件设计中最违反直觉的事情之一,应该非常小心地使用正确的测量工具来完成,否则它会看起来很有趣。

有些人会优化 UI 代码以在微秒而不是毫秒内响应,这显然没有价值并造成大量损害。

Quoting SQLite FAQ: 'Threads are evil. Avoid them'

Parallelization is useful for performance. Application performance optimization is one of the most counter-intuitive things in software design, and should be done with extreme care, using the right measurement tools, or it will just look funny.

Some would optimize UI code to respond in a microsecond instead of milliseconds, clearly having no value and causing lots of damage.

苏别ゝ 2024-09-26 13:17:05

可组合性必须是答案的一部分。在库中使用并行性与在应用程序中使用并行性不同,在库中,可组合性必须是优先考虑的(因为您不知道客户将如何结束使用您的代码),在应用程序中,您知道如何组合各个部分。并行代码的可组合性对于 .NET 来说并不是那么糟糕,但是当您决定矩阵乘法必须是在所有可用核心中运行的代码段时,您就使库的用户更难使用以下命令运行两种不同的算法并行矩阵乘法。事实上,只要有可能,我更喜欢在库中使用 SIMD:它会产生更少的冲突……除非您在较旧的 CPU 中混合使用 SSE 和 AVX。

我举一个例子,因为机器人在抱怨:假设我们想编写一个矩阵乘法方法。如果您在通用执行模式下强制 Parallel.For 或任何其他类似机制(例如显式任务),则库的客户端将使用多个内核进行矩阵乘法。这真的是他们想要的吗?也许不是:它们的矩阵乘法可能是更大算法的一部分,需要在其其他最有效的部分中进行并行化。

另一方面,使用 AVX 或任何其他 SIMD 风格并行化代码时不会失败。您并没有剥夺图书馆消费者的选择权。

注意:机器人要求我提供额外的引文或文档。抱歉,博特先生,但这是我不久前遇到的事情。原创研究。有时,它会发生。

Composability must be part of the answer. It's not same using parallelism in a library, where composability must be a priority (since you don't know how your clients will end using your code) than in an application, where you have some idea of how to combine your parts. The composability of parallel code is not that bad with .NET, but when you decide your matrix multiplication must be the piece of code that run in all the available core, you are making harder for the user of the library to run two different algorithms using matrix multiplication in parallel. In fact, I prefer to use SIMD in libraries whenever possible: it creates less clashes... except when you mix SSE and AVX in older CPUs.

I'll put an example, because the bot is complaining: let's say we want to write a matrix multiplication method. If you force Parallel.For or any other similar mechanism (explicit Tasks, for instance) in the common execution mode, the clients of the library will be using several cores for a matrix multiplication. Is that really what they want? Maybe not: their matrix multiplication may be part of a larger algorithm that needs parallelization in other, most effective, part of it.

On the other hand, you can't fail when parallelizing code with AVX or any other SIMD flavor. You are not taking away options from you library's consumers.

Note: the bot asks me additional citations or documentations. Sorry, Mr. Bot, but this is something that caught me up sometime ago. Original research. Sometimes, it happens.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文