使用多线程std::thread发挥多核计算优势(解答)

news2024/11/27 14:40:58

使用多线程std::thread发挥多核计算优势(题目)

单核无能为力

如果我们的电脑只有一个核,那么我们没有什么更好的办法可以让我们的程序更快。

因为这个作业限制了你修改算法函数。你唯一能做的就是利用你电脑的多核。

使用多线程

由于我们的电脑有多个内核,所以,我们可以创建多线程来把任务“平均”分配给多个核来计算。

这样多个核在“同时”运算的时候就可以加速程序的执行。

多核的细节

关于我们创建多少个线程比较合适,多个线程真的可以各自分配到多个核而“同时”运行吗?

试一下就知道了。

双线程的效果

我们先用两个线程,把任务固定的分配给这两个线程,看看完成任务总的执行时间是不是变短了。

代码如下:

#include <iostream>
#include <cmath>//sqrt
#include <iostream>
#include <iomanip>//format output
#include <chrono>
#include <thread>//for faster code
#include <mutex>//for faster code
#include <sstream>//stringstream
using namespace std::chrono;//time_piont duration
using namespace std;

//test helper function begin 测试辅助代码开始
void check_do(bool b, int line = __LINE__)
{
    if (b) { cout << "line:" << line << " Pass" << endl; }
    else { cout << "line:" << line << " Ohh! not passed!!!!!!!!!!!!!!!!!!!!!!!!!!!" << " " << endl; exit(0); }
}
#define check(msg)  check_do(msg, __LINE__);
//test helper function end 测试辅助代码结束

//do not change this function! 不要修改这个函数
//if you want to check a number is prime number or not, you can use this function only.
//判断素数只能用这个函数
bool is_number_prime(int n)
{
    if (n == 2 || n == 3)//prime less than 5
    {
        return true;//is prime
    }
    if (n % 6 != 5 && n % 6 != 1)//is not prime
    {
        return false;
    }
    int cmb = (int)std::sqrt(n);
    for (int i = 5; i <= cmb; i += 6)
    {
        if (n % i == 0 || n % (i + 2) == 0)
        {
            return false;//is not prime
        }
    }
    return true;//is prime
}

/*
    100以内的素数 primes within 100
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
*/
//测试判断素数的函数是否正确
void test_is_prime_number(void)
{
    stringstream ss;
    for (int  i = 2; i < 100; i++)
    {
        if (is_number_prime(i))
        {
            ss << i << " ";
        }
    }
    check(ss.str() == "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ");
}

//do not change this function!
//不要修改此函数
long long test_the_sum_of_all_primes_within(long long scale)
{
    auto start = system_clock::now();

    long long sum = 0;

    for (int n = 2; n <= scale; n++) {
        if (is_number_prime(n)) {
            sum += n;
        }
    }

    cout << "the sum of all primes from 2~"<< setw(10) << scale << " is : " << setw(15) << sum
        << ", elapled " << setw(10) << static_cast<long long>(duration<double, milli>(system_clock::now() - start).count()) << " milliseconds"
        << endl;
    return sum;
}
//please change this function to let your program faster by use multi core in your CPU.
//请重新实现此函数以让你的CPU多核优势得到发挥
//hint: maybe you can use multi thread technology to let your code faster.
//提示:你可以使用多线程来发挥多核的计算优势从而让你的程序跑的更快
long long faster_test_the_sum_of_all_primes_within(long long scale)
{
    auto start = system_clock::now();
    long long sum = 0;

    std::mutex sum_mutex;

    auto fun = [&sum, &sum_mutex](long long scaleStart, long long scaleLast) {
        for (int n = scaleStart; n <= scaleLast; n++) {
            if (is_number_prime(n)) {
                std::lock_guard<std::mutex> lock(sum_mutex);//如果没有多线程互斥访问sum,那么sum的值就可能是错的。
                sum += n;
            }
        }
    };

    //区间平分,这样后面的第二个线程的计算量还是偏大,因为都是在处理更大的数字
    std::thread t1(fun, 2, scale / 2);
    std::thread t2(fun, scale / 2 + 1, scale);

    t1.join();//线程开始运行直到结束
    t2.join();//线程开始运行直到结束

    cout << "the sum of all primes from 2~" << setw(10) << scale << " is : " << setw(15) << sum
        << ", elapled " << setw(10) << static_cast<long long>(duration<double, milli>(system_clock::now() - start).count()) << " milliseconds"
        << endl;
    return sum;
}
//do not change the code in this function 
//不要修改此函数中的内容
int main()
{
    
    test_is_prime_number();
    long long sum = 0;
    cout << "base slow version:" << endl;
    sum = test_the_sum_of_all_primes_within(10000 * 10);
    check(sum == 454396537);
    sum = test_the_sum_of_all_primes_within(10000 * 100);
    check(sum == 37550402023);
    sum = test_the_sum_of_all_primes_within(10000 * 1000);
    check(sum == 3203324994356);
    sum = test_the_sum_of_all_primes_within(10000 * 10000);
    check(sum == 279209790387276);

    cout << endl << "my faster version:" << endl;
    sum = faster_test_the_sum_of_all_primes_within(10000 * 10);
    check(sum == 454396537);
    sum = faster_test_the_sum_of_all_primes_within(10000 * 100);
    check(sum == 37550402023);
    sum = faster_test_the_sum_of_all_primes_within(10000 * 1000);
    check(sum == 3203324994356);
    sum = faster_test_the_sum_of_all_primes_within(10000 * 10000);
    check(sum == 279209790387276);

    cout << "please enter enter for exit." << endl;
    cin.get();

    return 0;
}

运行结果:

代码分析

如同代码注释中所说,我们把求解区间一分为二,后面的一个线程整体上任务还是偏重。因为处理的都是大数据。

但即便这样简单的划分,两个线程比一个线程耗时还是大幅度降低的。

在一百万个整数求解的时候时间降低了50%;

在一千万个整数求解的时候时间降低了50%;

在一亿个整数求解的时候时间降低了30%;这是因为后面一个线程的计算量过大,两个线程的任务没有起到平分导致的。

可以预见,随着数据量的继续增大,这种平分区间的算法,会导致第二个线程完全占据计算量的大头。这时候会导致这种算法的优势降低,甚至减少的时间可以忽略不计。

但是我们的目的达到了。那就是我们已经验证了多线程多核在计算速度上的确是可以完胜单线程的,只要我们合理分配计算任务给多个线程。

继续增加线程数量

下面我们把区间3等分,创建3个线程,看看是不是耗时会不会继续降低:

long long faster_test_the_sum_of_all_primes_within(long long scale)
{
    auto start = system_clock::now();
    long long sum = 0;

    std::mutex sum_mutex;

    auto fun = [&sum, &sum_mutex](long long scaleStart, long long scaleLast) {
        for (int n = scaleStart; n <= scaleLast; n++) {
            if (is_number_prime(n)) {
                std::lock_guard<std::mutex> lock(sum_mutex);//如果没有多线程互斥访问sum,那么sum的值就可能是错的。
                sum += n;
            }
        }
    };

    //区间平分,这样后面的第二个线程的计算量还是偏大,因为都是在处理更大的数字
    std::thread t1(fun, 2, scale / 3);
    std::thread t2(fun, scale / 3 + 1, scale / 3 * 2);
    std::thread t3(fun, scale / 3 * 2 + 1, scale);

    t1.join();//线程开始运行直到结束
    t2.join();//线程开始运行直到结束
    t3.join();//线程开始运行直到结束

    cout << "the sum of all primes from 2~" << setw(10) << scale << " is : " << setw(15) << sum
        << ", elapled " << setw(10) << static_cast<long long>(duration<double, milli>(system_clock::now() - start).count()) << " milliseconds"
        << endl;
    return sum;
}

运行输出:

代码分析2

正如我们预期,时间继续下降,尤其是数据量达到一亿的时候,总耗时再次变为了原来的一半。

至此,多线程多核可以降低计算总时长已经被我们验证完毕。

怎么样?你学到了吗?

欢迎点赞收藏转发。让其他感兴趣的人也可以看到。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/968833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言练习8(巩固提升)

C语言练习8 编程题 前言 奋斗是曲折的&#xff0c;“为有牺牲多壮志&#xff0c;敢教日月换新天”&#xff0c;要奋斗就会有牺牲&#xff0c;我们要始终发扬大无畏精神和无私奉献精神。奋斗者是精神最为富足的人&#xff0c;也是最懂得幸福、最享受幸福的人。正如马克思所讲&am…

无涯教程-JavaScript - CUBESETCOUNT函数

描述 CUBESETCOUNT函数返回集合中的项目数。 语法 CUBESETCOUNT (set)争论 Argument描述Required/Optionalset Microsoft Excel表达式的文本字符串,其输出为由CUBESET函数定义的集合。 OR CUBESET功能。 OR 对包含CUBESET函数的单元格的引用。 Required Notes 求值CUBESE…

【个人博客系统网站】统一处理 · 拦截器

【JavaEE】进阶 个人博客系统&#xff08;2&#xff09; 文章目录 【JavaEE】进阶 个人博客系统&#xff08;2&#xff09;1. 统一返回格式处理1.1 统一返回类common.CommonResult1.2 统一返回处理器component.ResponseAdvice 2. 统一异常处理3. 拦截器实现3.1 全局变量SESSI…

Mariadb高可用(四十)

目录 一、概述 &#xff08;一&#xff09;概念 &#xff08;二&#xff09;组成 &#xff08;三&#xff09;特点 &#xff08;四&#xff09;工作原理 二、实验要求 三、构建MHA &#xff08;一&#xff09;ssh免密登录 &#xff08;二&#xff09;安装mariadb数据库…

数学建模--一维插值法的多种插值方式的Python实现

目录 1.算法流程步骤 2.算法核心代码 3.算法效果展示 1.算法流程步骤 #算法的核心就是利用scipy中的interpolate来完成工作 #一共是5种一维插值算法形式: #插值方法&#xff1a;1.阶梯插值 2.线性插值 3.2阶样条插值 4.3阶样条插值 #"nearest"阶梯插值 #"zero&…

硬盘数据恢复的基础操作方法分享

确定硬盘故障类型&#xff1a;在进行硬盘数据恢复之前&#xff0c;首先需要确定故障类型是硬件故障还是软件故障。如果是软件故障&#xff0c;可以尝试使用数据恢复软件进行恢复&#xff1b;如果是硬件故障&#xff0c;则需要求助专业数据恢复公司进行处理。 使用数据恢复软件…

JavaScript基础05——字面量、变量介绍及变量基本使用

哈喽&#xff0c;大家好&#xff0c;我是雷工&#xff01; 说起变量感觉很熟悉&#xff0c;但要让解释什么是变量时&#xff0c;却有点语塞&#xff0c;就像解释下为啥112一样&#xff0c;感觉非常熟悉&#xff0c;就是知道&#xff0c;但确解释不出来。 不过虽然在其他场景比较…

机器学习——线性回归/岭回归

0、前言&#xff1a; 线性回归会用到python第三方库&#xff1a;sklearn.linear_model中的LinearRegression导入第三方库的方法&#xff1a;from sklearn.linear_model import LinearRegression使用LinearRegression(二维数据&#xff0c;一维数据)进行预测&#xff0c;其中数…

MySQL中的索引事务(2)事务----》数据库运行的原理知识+面试题~

本篇文章建议读者结合&#xff1a;MySQL中的索引事务&#xff08;1&#xff09;索引----》数据库运行的原理知识面试题~_念君思宁的博客-CSDN博客此时&#xff0c;如果你根据name来查询&#xff0c;查到叶子节点得到的只是主键id&#xff0c;还需要通过主键id去主键的B树里面在…

源码角度看待线程池的执行流程

文章目录 前言一、线程池的相关接口和实现类1.Executor接口2.ExecutorService接口3.AbstractExecutorService接口4.ThreadPoolExecutor 实现类 二、ThreadPoolExecutor源码解析1.Worker内部类2.execute()方法3.addWorker()方法 总结 前言 线程池内部维护了若干个线程&#xff…

RT-Thread 内核移植

内核移植 内核移植就是将RTT内核在不同的芯片架构、不同的板卡上运行起来&#xff0c;能够具备线程管理和调度&#xff0c;内存管理&#xff0c;线程间同步等功能。 移植可分为CPU架构移植和BSP&#xff08;Board support package&#xff0c;板级支持包&#xff09;移植两部…

1783_CMD启动MATLAB同时执行一个脚本

全部学习汇总&#xff1a; GitHub - GreyZhang/g_matlab: MATLAB once used to be my daily tool. After many years when I go back and read my old learning notes I felt maybe I still need it in the future. So, start this repo to keep some of my old learning notes…

【数据结构】树和二叉树的概念及结构(一)

目录 一&#xff0c;树的概念及结构 1&#xff0c;树的定义 2&#xff0c;树结点的分类及关系 3&#xff0c;树的表示 二&#xff0c;二叉树的概念及结构 1&#xff0c;二叉树的定义 2&#xff0c;特殊的二叉树 3&#xff0c;二叉树的性质 4&#xff0c;二叉树的存储结构 1&…

Unity中Shader 纹理属性 Tilling(缩放度) 和 Offset(偏移度)

文章目录 前言一、Tilling(缩放度)&#xff0c;个人理解有点像减小周期函数的周期的效果&#xff08;在单位空间内&#xff0c;容得下重复的函数图像的多少&#xff09;二、Offset&#xff08;偏移度&#xff09;&#xff0c;个人理解是函数的平移三、在Shader中使用 Tilling 和…

如何批量查询所有德邦快递的物流信息

当我们需要查询多个德邦快递的物流信息时&#xff0c;我们可以使用固乔快递查询助手来批量查询。以下是具体的操作步骤&#xff1a; 1. 在浏览器中搜索并下载【固乔快递查询助手】软件。这款软件支持多种快递公司&#xff0c;包括德邦快递&#xff0c;而且可以批量查询物流信息…

洞发现-APP应用之漏洞探针利用修复(44)

主要分为三个部分&#xff0c;第一部分抓包是很重要的&#xff0c;第二部分是协议&#xff0c;第三部分是逆向&#xff08;讲的不会太多&#xff0c;介绍根据使用不介绍原理&#xff09;&#xff0c; 关于反编译&#xff0c;app就分为安卓和苹果系统&#xff0c;苹果系统的源码…

基于STM32的简易示波器设计

疫情期间闲来无事&#xff0c;正好学习STM32F407&#xff0c;因此设计、制作了简易示波器&#xff0c;以助学习。长话短说方案如下&#xff1a; &#xff08;1&#xff09;单片机&#xff0c;选择STM32F407VET6&#xff0c;采用SWD方式仿真及程序烧写。五路独立按键和两个LED指…

[国产MCU]-W801开发实例-用户报文协议(UDP)数据接收和发送

用户报文协议(UDP)数据接收和发送 文章目录 用户报文协议(UDP)数据接收和发送1、UDP简单介绍2、W801的UDP创建逻辑2.1 UDP使用步骤2.2 代码实现1、UDP简单介绍 用户数据报协议 (UDP) 是一种跨互联网使用的通信协议,用于对时间敏感的传输,例如视频播放或 DNS查找。它通过在数…

OTFS-ISAC通信最新进展

测试场景 Tx DD域帧结构导频区域 Rx DD域帧导频区域 原始星座图 信道估计及数据检测 经过MP算法后的星座图 误码率曲线

串行协议——USB驱动[基础]

多年前的学习记录&#xff0c;整理整理。 一、USB协议基础 二、Linux内核USB驱动源码分析 USB中不同类型设备使用的 设备描述符(设备类\设备子类\设备协议) 配置不同,典型的以下几种:1)HID设备: Human Input Device人工输入设备, 如鼠标\键盘\游戏手柄等.2)CDC设备: Communi…