首页 > 解决方案 > C++ 上的并行并不像 C# 那样快

问题描述

我在C#上有这样的代码

private static Random random = new Random();
public static string RandomString(int length)
{
    const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return new string(Enumerable.Repeat(chars, length)
      .Select(s => s[random.Next(s.Length)]).ToArray());
}
     Task.Factory.StartNew(() => {       
                 System.Threading.Tasks.Parallel.For(0L, 10000000, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 }, n =>
                 {
                 Console.WirteLine(RandomString(12));
                 });
                }

添加并行方法,它可以在不到 8 秒的时间内运行生成 1000 万个随机字符串并使用所有 CPU 功率

在此处输入图像描述

我尝试在C++中再做一次

string NonRepeatChar(int max_len)
{
    std::string valid_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(valid_chars.begin(), valid_chars.end(), g);
    std::string rand_str(valid_chars.begin(), valid_chars.begin() + max_len);
    return rand_str;
}

将代码应用于一些推荐的C++并行方法

void multiply()
{
for (size_t i = 0; i < 10; i++)
{
    for (size_t j = 0; j < 10; j++)
    {           
        for (int k = 0; k < 10; k++)
        {
            printf("%s\n",NonRepeatChar(10));
        }           
    }
}
}

class Foo {
public:
    Foo() : counter_(0) {}

    std::pair<string, std::future<void>> a_function(std::future<void>& f) {
        // Ensure that the background task from the previous iteration
        // has completed
        f.wait();

        // Set the task for the next iteration
        std::future<void> fut = std::async(std::launch::async, &Foo::background_task, this);

        // Do some work
        string value = NonRepeatChar(12);

        // Return the result and the future for the next iteration
        return std::make_pair(value.c_str(), std::move(fut));
    }

    void background_task() {
        ++counter_;
    }

private:
    std::atomic<int> counter_;
};

记录运行时间

int main()
{   
    clock_t tStart = clock();
    std::future<void> bleak = std::async(std::launch::deferred, []() {});

    Foo foo;

    for (size_t i = 0; i < 10000; ++i) {
        // Call the function
        std::pair<string, std::future<void>> result = foo.a_function(bleak);    
        bleak = std::move(result.second);   
        std::cout << result.first << "\n";
    }
    printf("Time taken: %.2fs\n", (double)(clock() - tStart) / CLOCKS_PER_SEC);
    return 0;
    }

这是我的结果:

10.98s//正常循环

8.76s//相乘

8.88s//Foo

显然代码与原始循环相比没有任何区别,仅生成 10000 行,甚至没有像C#那样使用所有 CPU 能力。并行方法有问题吗?我该如何优化它?

标签: c++performanceparallel-processingparallel.foreach

解决方案


您的 C++ 代码根本不等同于您的 C# 代码。

在 C# 方面

您正在使用命名空间中Parallel.For的a System.Threading.Tasks。这是一个高级构造,它允许循环的迭代并行运行,而不必以低级方式控制线程,因为任务会自动为您创建并以最适合您的方式调度到您的处理器内核你的系统。

在您的特定代码的情况下,Parallel.For配置为允许一次Environment.ProcessorCount * 10安排最多的工作线程。虽然不能保证(库的调度程序将拥有最后的决定权),但这应该确保您的处理核心最佳地用于提交的工作负载,因为有足够的任务来占用所有核心,并且有足够大的工作队列来确保核心不会因为缺乏提前安排的工作而挨饿。

在 C++ 方面

您正在使用asyncand future,它们是允许您运行后台任务的较低级别的构造,但是您通过在每次迭代中强制同步来人为地限制并行度:

// Ensure that the background task from the previous iteration
// has completed
f.wait();

在 C++ 中实现与 C# 代码类似的行为的最简单(但不可移植)方法是使用 Microsoft 的Parallel Patterns Library。这提供了System.Threading.Tasks.Parallel.For与 C# 端提供的功能非常相似的功能,即concurrency::parallel_for.


推荐阅读