Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C+中学习多线程+;:添加线程不会';I don’尽管看起来应该加快执行速度_C++_Multithreading - Fatal编程技术网

C++ 在C+中学习多线程+;:添加线程不会';I don’尽管看起来应该加快执行速度

C++ 在C+中学习多线程+;:添加线程不会';I don’尽管看起来应该加快执行速度,c++,multithreading,C++,Multithreading,长话短说,我遇到了一个问题,我有兴趣把一些东西放在一起,这样我就可以进行计算测试了。这很好,但在这个过程中,我对C++中的多线程应用感到好奇。我是一名计算机科学专业的学生,但我只是用另一种语言简要地介绍了这个话题。我想看看我是否可以利用一些额外的CPU内核来加快蒙特霍尔模拟的速度 看起来我让它工作起来了,但遗憾的是,它实际上没有任何性能提升。该程序在一个简单函数上执行大量迭代,该函数本质上归结为几个rand_r()调用和几个比较。我希望这是一个可以在线程之间分割的简单示例,基本上只是让每个线程处

长话短说,我遇到了一个问题,我有兴趣把一些东西放在一起,这样我就可以进行计算测试了。这很好,但在这个过程中,我对C++中的多线程应用感到好奇。我是一名计算机科学专业的学生,但我只是用另一种语言简要地介绍了这个话题。我想看看我是否可以利用一些额外的CPU内核来加快蒙特霍尔模拟的速度

看起来我让它工作起来了,但遗憾的是,它实际上没有任何性能提升。该程序在一个简单函数上执行大量迭代,该函数本质上归结为几个rand_r()调用和几个比较。我希望这是一个可以在线程之间分割的简单示例,基本上只是让每个线程处理总迭代的相等部分

我只是想理解这一点,我想知道我是否犯了一个错误,或者在后台是否有什么事情在多线程执行,即使我在代码中只指定了一个线程

无论如何,看一看并分享你的想法。还请记住,我这样做只是作为一种学习经验,并没有计划让其他人阅读:D

#include <cstdlib>
#include <climits>
#include <ctime>
#include <iostream>
#include <thread>
#include <chrono>

enum strategy {STAY = 0, SWITCH = 1};
unsigned ITERATIONS = 1;
unsigned THREADS = 5;

struct counts
{
    unsigned stay_correct_c;
    unsigned switch_correct_c;
};

void simulate (struct counts&, unsigned&);
bool game (enum strategy, unsigned&);

int main (int argc, char **argv)
{
    if (argc < 2)
        std::cout << "Usage: " << argv[0] << " -i [t|s|m|l|x] -t [1|2|4|5|10]\n", exit(1);

    if (argv[1][1] == 'i') {
        switch (argv[2][0]) {
    case 's':
            ITERATIONS = 1000;
            break;
        case 'm':
            ITERATIONS = 100000;
            break;
        case 'l':
            ITERATIONS = 10000000;
            break;
        case 'x':
            ITERATIONS = 1000000000;
            break;
        default:
            std::cerr << "Invalid argument.\n", exit(1);
        }
    }

    if (argv[3][1] == 't') {
        switch (argv[4][0])
        {
        case '1':
            if (argv[4][1] != '0')
                THREADS = 1;
            else if (argv[4][1] == '0')
                THREADS = 10;
            break;
        case '2':
            THREADS = 2;
            break;
        case '4':
            THREADS = 4;
            break;
        case '5':
            THREADS = 5;
            break;
        }
    }

    srand(time(NULL));

    auto start = std::chrono::high_resolution_clock::now();
    struct counts total_counts;
    total_counts.stay_correct_c = 0;
    total_counts.switch_correct_c = 0;
    struct counts per_thread_count[THREADS];
    std::thread* threads[THREADS];
    unsigned seeds[THREADS];

    for (unsigned i = 0; i < THREADS; ++i) {
        seeds[i] = rand() % UINT_MAX;
        threads[i] = new std::thread (simulate, std::ref(per_thread_count[i]), std::ref(seeds[i]));
    }

    for (unsigned i = 0; i < THREADS; ++i) {
        std::cout << "Waiting for thread " << i << " to finish...\n";
        threads[i]->join();
    }

    for (unsigned i = 0; i < THREADS; ++i) {
        total_counts.stay_correct_c += per_thread_count[i].stay_correct_c;
        total_counts.switch_correct_c += per_thread_count[i].switch_correct_c;
    }

    auto stop = std::chrono::high_resolution_clock::now();
    std::cout <<
        "The simulation performed " << ITERATIONS <<
        " iterations on " << THREADS << " threads of both the stay and switch strategies " <<
        "taking " << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count() <<
        " ms." << std::endl <<
        "Score:" << std::endl <<
        "  Stay Strategy: " << total_counts.stay_correct_c << std::endl <<
        "  Switch Strategy: " << total_counts.switch_correct_c << std::endl << std::endl <<
        "Ratios:" << std::endl <<
        "  Stay Strategy: " << (double)total_counts.stay_correct_c / (double)ITERATIONS << std::endl <<
        "  Switch Strategy: " << (double)total_counts.switch_correct_c / (double)ITERATIONS << std::endl << std::endl;
}

void simulate (struct counts& c, unsigned& seed)
{
    c.stay_correct_c = 0;
    c.switch_correct_c = 0;
    for (unsigned i = 0; i < (ITERATIONS / THREADS); ++i) {
        if (game (STAY, seed))
            ++c.stay_correct_c;
        if (game (SWITCH, seed))
            ++c.switch_correct_c;
    }
}

bool game (enum strategy player_strat, unsigned& seed)
{
    unsigned correct_door = rand_r(&seed) % 3;
    unsigned player_choice = rand_r(&seed) % 3;
    unsigned elim_door;
    do {
        elim_door = rand_r(&seed) % 3;
    }
    while ((elim_door != correct_door) && (elim_door != player_choice));
    seed = rand_r(&seed);
    if (player_strat == SWITCH) {
        do
            player_choice = (player_choice + 1) % 3;
        while (player_choice != elim_door);
        return correct_door == player_choice;
    }
    else
        return correct_door == player_choice;
}
有了像这个程序这样简单的划分,我本来希望随着线程的增加,总时间会线性减少,但我显然遗漏了一些东西。我的想法是,如果一个线程可以在y时间内执行x模拟,那么该线程应该能够在y/4时间内执行x/4模拟。我在这里误解了什么


编辑2:我应该补充一点,因为上面的代码存在,不同线程的时间差异不太明显,但我做了一些小优化,使增量稍微大一些。

感谢发布代码;它不能在我的机器上编译(Apple LLVM版本9.0.0(clang-900.0.39.2))。爱的标准

我把它改成了C版本,你的问题似乎是错误的分享;也就是说,每个线程都会多次命中其“种子”项,但由于内存缓存将相邻位置聚合为“行”,因此CPU会花费所有时间来回复制这些行。如果您将“种子”的定义更改为:

struct  myseed {
      unsigned seed;
      unsigned dont_share_me[15];
};
您应该看到预期的可伸缩性。您可能希望对结构计数执行相同的操作。
通常,malloc会为您进行此调整,因此如果您将“每线程”上下文标记到一个包中并malloc它,它会返回正确的缓存对齐位置。

您的问题有一个严重的问题:您说您希望性能提高,但没有得到提高。这是一种解释,但缺少实际数字!此外,执行参数也丢失了!最后,不执行的两个可能原因:第一,没有足够的CPU内核。在执行时观察系统监视器,确保它实际上是分布式的!第二,当您有多个由N个元素组成的数组,每个元素对应时,您的代码在将它们组合到结构中时很可能是清晰的,这使得CPU缓存更容易!说得好:多线程问题通常是从“似乎应该”起作用的事情开始的,但事实并非如此。理解多线程代码的最大障碍之一是,当您添加多线程的复杂性时,您的直觉(和我的直觉)往往是错误的。编辑我的帖子以共享chrono函数的实际数字以及htop的一些输出。谢谢你的建议!谢谢你的宝贵见解。我使用-std=c++11和-pthread标志在64位arch linux上使用clang进行编译。根据我做的非常有限的研究,多线程在C++系统中是很时髦的。在你的建议中,分享我会不会只是一个空的缓冲区,把种子隔离在内存中?我还应该问,为了我的继续教育和独立性,你使用了什么工具来分析我代码的执行?你怎么知道线程访问我的seed变量这么多?很抱歉,我的评论是垃圾邮件,但我还想说的是,我按照你的建议做了,并将seed更改为一个带空缓冲区的结构,这将10个线程的平均时间从~70秒减少到~25秒。工具:眼睛。我在并发系统上工作了30年。我验证了您的结果,然后用增量替换rand_r(),以防它是连锁的,这是我的第一个猜测。然后我在两个内部循环上添加了计数器,以验证它们没有使用#线程进行缩放。然后我去寻找缓存效果;注意到了种子阵列。
struct  myseed {
      unsigned seed;
      unsigned dont_share_me[15];
};