Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/72.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++_C_Multithreading - Fatal编程技术网

C++ 为什么多线程速度较慢?

C++ 为什么多线程速度较慢?,c++,c,multithreading,C++,C,Multithreading,所以我正试图写一个程序来寻找素数。该项目的真正目的只是学习多线程。首先,我编写了一个单线程程序,它在一分钟内找到13633943。我的多线程版本只有10025627 这是我的单线程程序代码 #include <iostream> using namespace std; bool isprime(long num) { long lim = num/2; if(num == 1) { return 0; } for(long

所以我正试图写一个程序来寻找素数。该项目的真正目的只是学习多线程。首先,我编写了一个单线程程序,它在一分钟内找到13633943。我的多线程版本只有10025627

这是我的单线程程序代码

#include <iostream>

using namespace std;

bool isprime(long num)
{
    long lim = num/2;
    if(num == 1)
    {
        return 0;
    }
    for(long i = 2; i <= lim; i++)
    {
        if (num % i == 0)
        {
            return 0;
        }
        else{ lim = num/i; }
    }
    return 1;
}

int main()
{
    long lim;
    cout << "How many numbers should I test: ";
    cin >> lim;
    for(long i = 1; i <= lim || lim == 0; i++)
    {
        if(isprime(i))
        {
            cout << i << endl;
        }
    }
}
#包括
使用名称空间std;
bool isprime(长数值)
{
长lim=num/2;
如果(num==1)
{
返回0;
}
对于(长i=2;i lim;

对于(long i=1;i我相当肯定
cout
扮演着一个共享资源的角色——即使它确实正确地按正确的顺序打印每个数字,它也会大大降低速度

我也做过类似的事情(它更灵活,使用原子操作“选择下一个数字”),在我的四核机器上几乎快了4倍。但这只是在我不打印任何东西的情况下。如果它打印到控制台上,速度会慢得多,因为很多时间都是用来洗牌像素而不是实际计算的


注释掉
cout这取决于操作系统给你的代码分配了多少CPU来运行。这些线程中的每一个都是CPU限制的,所以如果你只有一个CPU,它将运行一个线程一段时间,时间片,运行下一个线程,等等,这不会更快,也可能会更慢,这取决于线程的开销至少在solaris上,告诉它您希望所有线程同时运行是值得的

我没有遇到过像另一张海报所建议的那样将输出序列化的实现

235 iisi s  ppprririimmme
ee
因此,您的输出很可能表明O/S没有为您分配多个线程


您可能遇到的另一个问题是,与输出到文件相比,输出到控制台的速度非常慢。将程序的输出发送到一个文件,看看它的速度有多快可能是值得的。

我相信Oli Charlesworth在超读问题上击中了它的头部。我认为超读实际上就像hav使用两个内核。事实并非如此。我将它改为只使用两个线程,我得到了2227421个线程,速度几乎是原来的两倍。

而@matstpeterson是正确的(至少对于基于POSIX的系统,
stdout
是一个共享资源),他没有提供解决该问题的方法,所以下面介绍如何消除这些讨厌的锁

POSIX C定义了一个函数,
putc\u unlocked
,该函数将执行与
putc
完全相同的操作,但没有锁定(出乎意料)。使用该函数,我们可以定义自己的函数,该函数将不锁定地打印整数,并且在多线程场景中比
cout
printf
更快:

void printint_unlocked(FILE *fptr, int i) {
    static int digits[] = {
        1,
        10,
        100,
        1000,
        10000,
        100000,
        1000000,
        10000000,
        100000000,
        1000000000,
    };

    if (i < 0) {
        putc_unlocked('-', fptr);
        i = -i;
    }

    int ndigits = (int) log10(i);
    while (ndigits >= 0) {
        int digit = (i / (digits[ndigits])) % 10;

        putc_unlocked('0' + digit, fptr);

        --ndigits;
    }
}
void printint\u解锁(文件*fptr,int i){
静态整数位数[]={
1.
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
};
if(i<0){
putc_解锁('-',fptr);
i=-i;
}
int ndigits=(int)log10(i);
而(ndigits>=0){
整数位数=(i/(位数[ndigit])%10;
putc_解锁('0'+位,fptr);
--NDIGIT;
}
}
请注意,此方法完全可能存在竞争条件,从而导致输出中的数字发生冲突。如果您的算法最终没有任何冲突,则您仍应获得多线程代码的性能提升


第三个也是最后一个选项(可能对您的用例来说太复杂了)就是在另一个线程上创建一个事件队列,并从该线程执行所有打印操作,从而不会产生争用条件,线程之间也不会出现锁定问题。

我认为当前的许多问题是,您正在参与真正可以操作多线程(查找素数)的工作,并将其隐藏在噪音中(将输出写入控制台的时间)

为了了解这有多大的影响,我重写了一点main,将打印素数与查找素数分开。为了更方便地计时,我还让它从命令行中接受限制,而不是以交互方式,如下所示:

int main(int argc, char **argv) {
    if (argc != 2) {
        std::cerr << "Usage: bad_prime <limit:long>\n";
        return 1;
    }
    std::vector<unsigned long> primes;

    unsigned long lim = atol(argv[1]);

    clock_t start = clock();

    for(unsigned long i = 1; i <= lim; i++)
        if(isprime(i))
            primes.push_back(i);
    clock_t stop = clock();

    for (auto a : primes)
        std::cout << a << "\t";

    std::err << "\nTime to find primes: " << double(stop-start)/CLOCKS_PER_SEC << "\n";
}
因此——大约半秒找到素数,47秒以上打印它们。假设我们的目的真的是将输出写入控制台,我们不妨到此为止。即使多线程可以完全消除查找素数的时间,我们仍然只需将最终时间从~48.2秒改为~47.6秒——unl我认为这是值得的

因此,就目前而言,我假设真正的目的是将输出写入类似于文件的内容。由于使代码多线程化似乎毫无意义,但在每个线程中运行效率极低的代码,因此我认为我应该优化(或至少消除悲观情绪)单线程代码作为起点

首先,我删除了
endl
,并将其替换为
“\n”
。当输出指向一个文件时,这将运行时间从0.968秒减少到0.678秒--
endl
除了写入换行符外,还会刷新缓冲区,而缓冲区刷新大约占整个程序所用时间的三分之一

在同样的基础上,我冒昧地将您的
isprime
改写为至少效率稍低一点的内容:

bool isprime(unsigned long num) {
    if (num == 2)
        return true;

    if(num == 1 || num % 2 == 0)
        return false;

    unsigned long lim = sqrt(num);

    for(unsigned long i = 3; i <= lim; i+=2)
        if (num % i == 0)
            return false;

    return true;
}
在与以前相同的机器上运行(相当旧的双核处理器),我得到:

这似乎可以很好地扩展。如果我们从多核计算中获得的只是多核计算,那么我们希望看到找到素数除以2的时间(我在双核处理器上运行此操作),并且将数据写入磁盘的时间保持不变(多线程不会加快我的硬盘速度).基于此,完美缩放应该给我们0.59/2+0.1=0.40秒

(无可否认)我们看到的微小改进是b
void printint_unlocked(FILE *fptr, int i) {
    static int digits[] = {
        1,
        10,
        100,
        1000,
        10000,
        100000,
        1000000,
        10000000,
        100000000,
        1000000000,
    };

    if (i < 0) {
        putc_unlocked('-', fptr);
        i = -i;
    }

    int ndigits = (int) log10(i);
    while (ndigits >= 0) {
        int digit = (i / (digits[ndigits])) % 10;

        putc_unlocked('0' + digit, fptr);

        --ndigits;
    }
}
int main(int argc, char **argv) {
    if (argc != 2) {
        std::cerr << "Usage: bad_prime <limit:long>\n";
        return 1;
    }
    std::vector<unsigned long> primes;

    unsigned long lim = atol(argv[1]);

    clock_t start = clock();

    for(unsigned long i = 1; i <= lim; i++)
        if(isprime(i))
            primes.push_back(i);
    clock_t stop = clock();

    for (auto a : primes)
        std::cout << a << "\t";

    std::err << "\nTime to find primes: " << double(stop-start)/CLOCKS_PER_SEC << "\n";
}
Time to find primes: 0.588


Real    48.206
User    1.68481
Sys     3.40082
bool isprime(unsigned long num) {
    if (num == 2)
        return true;

    if(num == 1 || num % 2 == 0)
        return false;

    unsigned long lim = sqrt(num);

    for(unsigned long i = 3; i <= lim; i+=2)
        if (num % i == 0)
            return false;

    return true;
}
#include <iostream>
#include <vector>
#include <time.h>
#include <math.h>
#include <thread>

using namespace std;

bool isprime(unsigned long num) {
    // same as above
}

typedef unsigned long UL;

struct params { 
    unsigned long lower_lim;
    unsigned long upper_lim;
    std::vector<unsigned long> results;

    params(UL l, UL u) : lower_lim(l), upper_lim(u) {}
};

long thread_func(params *p) { 
    for (unsigned long i=p->lower_lim; i<p->upper_lim; i++)
        if (isprime(i))
            p->results.push_back(i);
    return 0;
}

int main(int argc, char **argv) {
    if (argc != 2) {
        std::cerr << "Usage: bad_prime <limit:long>\n";
        return 1;
    }

    unsigned long lim = atol(argv[1]);

    params p[] = {
        params(1, lim/4),
        params(lim/4, lim/2),
        params(lim/2, 3*lim/4),
        params(3*lim/4, lim)
    };

    std::thread threads[] = {
        std::thread(thread_func, p), 
        std::thread(thread_func, p+1),
        std::thread(thread_func, p+2),
        std::thread(thread_func, p+3)
    };

    for (int i=0; i<4; i++) {
        threads[i].join();
        for (UL p : p[i].results)
            std::cout << p << "\n";
    }
}
Real    0.35
User    0.639604
Sys     0