gpt4 book ai didi

.NET 客户端并发性能

转载 作者:行者123 更新时间:2023-12-03 12:44:23 28 4
gpt4 key购买 nike

我正在编写一个客户端 .NET 应用程序,该应用程序预计将使用大量线程。我被警告说 .NET 性能在并发方面非常糟糕。虽然我不是在编写实时应用程序,但我想确保我的应用程序是可扩展的(即允许多个线程)并且是 不知何故与等效的 C++ 应用程序相当。

你的经验是什么?什么是相关基准?

最佳答案

我使用素数生成器在 C# 中编写了一个快速而肮脏的基准测试作为测试。测试使用简单的 Eratosthenes 实现生成一个恒定限制(我选择 500000)的素数,并重复测试 800 次,在特定数量的线程上并行,或者使用 .NET ThreadPool或独立线程。

该测试在运行 Windows Vista (x64) 的四核 Q6600 上运行。这不是使用任务并行库,只是简单的线程。它针对以下场景运行:

  • 串行执行(无线程)
  • 4 个线程(即每个核心一个),使用 ThreadPool
  • 40 个线程使用 ThreadPool (测试池本身的效率)
  • 4 个独立线程
  • 40 个独立线程,模拟上下文切换压力

  • 结果是:
    Test | Threads | ThreadPool | Time
    -----+---------+------------+--------
    1 | 1 | False | 00:00:17.9508817
    2 | 4 | True | 00:00:05.1382026
    3 | 40 | True | 00:00:05.3699521
    4 | 4 | False | 00:00:05.2591492
    5 | 40 | False | 00:00:05.0976274

    从中可以得出以下结论:
  • 并行化并不完美(正如预期的那样 - 无论环境如何,它永远不会完美),但是将负载分摊到 4 个内核会导致吞吐量增加大约 3.5 倍,这几乎没有什么可提示的。
  • 使用 ThreadPool 的 4 和 40 个线程之间的差异可以忽略不计,这意味着池不会产生大量费用,即使您用请求轰炸它也是如此。
  • ThreadPool 之间的差异可以忽略不计和自由线程版本,这意味着 ThreadPool没有任何重大的“持续”开支;
  • 4 线程和 40 线程自由线程版本之间的差异可以忽略不计,这意味着 .NET 的性能不会比人们预期的在大量上下文切换时更差。

  • 我们甚至需要一个 C++ 基准来比较吗?结果非常清楚:.NET 中的线程并不慢。除非 ,程序员,编写糟糕的多线程代码并最终导致资源匮乏或锁车队,您真的不必担心。

    使用 .NET 4.0 和 TPL 以及对 ThreadPool 的改进,工作窃取队列和所有那些很酷的东西,你有更多的余地来编写“有问题的”代码,并且仍然让它有效地运行。您根本无法从 C++ 中获得这些功能。

    作为引用,这里是测试代码:
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime.CompilerServices;
    using System.Threading;

    namespace ThreadingTest
    {
    class Program
    {
    private static int PrimeMax = 500000;
    private static int TestRunCount = 800;

    static void Main(string[] args)
    {
    Console.WriteLine("Test | Threads | ThreadPool | Time");
    Console.WriteLine("-----+---------+------------+--------");
    RunTest(1, 1, false);
    RunTest(2, 4, true);
    RunTest(3, 40, true);
    RunTest(4, 4, false);
    RunTest(5, 40, false);
    Console.WriteLine("Done!");
    Console.ReadLine();
    }

    static void RunTest(int sequence, int threadCount, bool useThreadPool)
    {
    TimeSpan duration = Time(() => GeneratePrimes(threadCount, useThreadPool));
    Console.WriteLine("{0} | {1} | {2} | {3}",
    sequence.ToString().PadRight(4),
    threadCount.ToString().PadRight(7),
    useThreadPool.ToString().PadRight(10),
    duration);
    }

    static TimeSpan Time(Action action)
    {
    Stopwatch sw = new Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    return sw.Elapsed;
    }

    static void GeneratePrimes(int threadCount, bool useThreadPool)
    {
    if (threadCount == 1)
    {
    TestPrimes(TestRunCount);
    return;
    }

    int testsPerThread = TestRunCount / threadCount;
    int remaining = threadCount;
    using (ManualResetEvent finishedEvent = new ManualResetEvent(false))
    {
    for (int i = 0; i < threadCount; i++)
    {
    Action testAction = () =>
    {
    TestPrimes(testsPerThread);
    if (Interlocked.Decrement(ref remaining) == 0)
    {
    finishedEvent.Set();
    }
    };

    if (useThreadPool)
    {
    ThreadPool.QueueUserWorkItem(s => testAction());
    }
    else
    {
    ThreadStart ts = new ThreadStart(testAction);
    Thread th = new Thread(ts);
    th.Start();
    }
    }
    finishedEvent.WaitOne();
    }
    }

    [MethodImpl(MethodImplOptions.NoOptimization)]
    static void IteratePrimes(IEnumerable<int> primes)
    {
    int count = 0;
    foreach (int prime in primes) { count++; }
    }

    static void TestPrimes(int testRuns)
    {
    for (int t = 0; t < testRuns; t++)
    {
    var primes = Primes.GenerateUpTo(PrimeMax);
    IteratePrimes(primes);
    }
    }
    }
    }

    这是主要的生成器:
    using System;
    using System.Collections.Generic;
    using System.Linq;

    namespace ThreadingTest
    {
    public class Primes
    {
    public static IEnumerable<int> GenerateUpTo(int maxValue)
    {
    if (maxValue < 2)
    return Enumerable.Empty<int>();

    bool[] primes = new bool[maxValue + 1];
    for (int i = 2; i <= maxValue; i++)
    primes[i] = true;

    for (int i = 2; i < Math.Sqrt(maxValue + 1) + 1; i++)
    {
    if (primes[i])
    {
    for (int j = i * i; j <= maxValue; j += i)
    primes[j] = false;
    }
    }

    return Enumerable.Range(2, maxValue - 1).Where(i => primes[i]);
    }
    }
    }

    如果您在测试中发现任何明显缺陷,请告诉我。除非测试本身存在任何严重问题,否则我认为结果不言自明,信息很明确:

    不要听任何人就 .NET 或任何其他语言/环境在某些特定领域的性能如何“糟糕”做出过于宽泛和无条件的陈述,因为他们可能是在谈论他们的......后端。

    关于.NET 客户端并发性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2495062/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com