一、为什么需要多核开发?
答案很简单,目前的芯片制造技术对CPU主频的提升已经达到一个极限了,也就是说性能的垂直伸缩已经不太可能了。因此通过多核的方法,可以让程序横向的伸缩,这就类似于用多台服务器实现负载均衡(水平伸缩),而不是简单的靠将服务器升级成小型机来提供处理能力(垂直伸缩)。
虽然多核并行计算的概念已经存在了几十年了,但直到最近多核CPU在PC上的普及,多核开发才不得不提引起程序员的重视。
多核开发的本质就是使用多线程进行程序开发,我们在学数据结构和算法的时候,写的所有的算法都是面向单线程的。而多核开发的目的就是将这些算法改造成多线程的支持,然后系统运行时将这些多线程平均分配到多核处理器上,以实现运行的加速。
多核开发可以应用的领域非常多,对于初学者,可以从优化现有的各种算法开始,例如对于搜索状态空间数的各种算法,例如各种棋类问题的求解等等。此外,在视频,音频或图形的编码,加密解密算法,数据分析统计,以及需要高性能计算的各种仿真学领域都可以进行应用。当然在Web开发领域,多核应用一般不需要程序员来关心,因为现在的主流Web服务器都是基于轻量级的进程池,即每个请求由一个进程处理,而服务器会自动将进程分配到不同的核上去处理。
二、如何进行多核开发
如果你很熟悉POSIX threads (pthreads) 或者 WinAPI threads,你就可以自己进行开发。
如果你不想设计过多底层的线程操作,那就选择一个并发开发平台,由平台来自动协调,调度和管理多核资源。并发开发平台包括各种线程池的库,例如
.NET的ThreadPool类
Java的Concurrent类
消息传递环境,例如MPI
data-parallel编程环境,例如NESL, RapidMind, ZPL
task-parallel编程环境, 例如Intel的Threading Building Blocks (TBB) 和 Microsoft的Task Parallel Library (TPL)
动态编程环境,例如Cilk or Cilk++或者业界标准OpenMP.
这些并发平台通过提供语言抽象,扩充注释或者提供库函数的方式来支持多核开发。
三、使用并发开发平台具体有哪些好处
我们从下面几个方面来看:
软件开发中最重要的三个考虑的要素就是
程序的性能 (使用多核就是为了提升程序的性能的)
开发的时间
程序的可靠性
而其中影响开发时间的三个要素是
伸缩性:如果你自己编写线程,你必须考虑用户是双核,四核还是八核。如何将线程自动适应用户的核数,并且在多核上将线程均衡的负载。
代码简洁:直接使用底层线程库操作代码是十分复杂的
模块化:直接使用底层线程库操作还会破坏代码的模块化
四、具体实例
下面以Fibonacci的例子来演示:它的递归算法经常被用来作为多核开发的例子,虽然我们知道该算法的迭代法效率最高,但是这里仅仅是为了说明如何使用多核开发库,所以请不要较真。
单核时代,我们写Fibonacci代码的方法如下:
-
intfib(intn)
- {
-
if(n<2)returnn;
-
else{
-
intx=fib(n-1);
-
inty=fib(n-2);
-
returnx+y;
- }
- }
-
intmain(intargc,char*argv[])
- {
-
intn=atoi(argv[1]);
-
intresult=fib(n);
-
printf("Fibonacciof%dis%d.\n",n,result);
-
return0;
- }
这个算法的核心就是f(n) = f(n-1) + f(n-2),当n很大时,我们希望计算f(n-1)和f(n-2)这两个任务能否分摊在一个双核处理器上同时执行。
如果直接使用WinAPI-threaded操作的代码如下:
-
intfib(intn)
- {
-
if(n<2)returnn;
-
else{
-
intx=fib(n-1);
-
inty=fib(n-2);
-
returnx+y;
- }
- }
-
typedefstruct{
-
intinput;
-
intoutput;
- }thread_args;
-
void*thread_func(void*ptr)
- {
-
inti=((thread_args*)ptr)->input;
- ((thread_args*)ptr)->output=fib(i);
-
returnNULL;
- }
-
intmain(intargc,char*argv[])
- {
-
pthread_tthread;
- thread_argsargs;
-
intstatus;
-
intresult;
-
intthread_result;
-
if(argc<2)return1;
-
intn=atoi(argv[1]);
-
if(n<30)result=fib(n);
-
else{
- args.input=n-1;
-
status=pthread_create(thread,
- NULL,thread_func,
-
(void*)&args);
-
- result=fib(n-2);
-
-
pthread_join(thread,NULL);
- result+=args.output;
- }
-
printf("Fibonacciof%dis%d.\n",n,result);
-
return0;
- }
注意main里面的if(n<30),当n在30以内时,计算非常快,就不需要使用多线程,当n大于30之后,我们生成一个线程用来计算f(n-1),而main的主线程将继续计算f(n-2),这样等两个线程都结束以后(pthread_join(thread, NULL);),我们将他们的结果相加。
从这个例子就可以看出,自己实现线程的缺点:
1 这个例子正好可以用两个线程分配在两个核上来实现,可如果一个任务需要16个线程同时执行,我们又不知道客户端到底是几核的CPU时,这个任务如何分配就成为一个问题。
2 这段代码非常不简洁
3 额外的结构和函数也破坏了算法本身的完整性。
下面我们使用多核支持库OpenMP来实现该代码,该代码通过GCC的编译(具体配置请参考我的上一篇关于Windows下安装MinGW的文章):
使用OpenMP
-
#include<stdio.h>
-
#include<omp.h>
-
#include<time.h>
-
usingnamespacestd;
-
intfib(intn)
- {
-
if(n<2)returnn;
-
else{
-
intx=fib(n-1);
-
inty=fib(n-2);
-
returnx+y;
- }
- }
-
intfib_parallel(intn)
- {
-
if(n<2)returnn;
-
else{
-
intx,y;
-
#pragmaompparallelsections
- {
-
#pragmaompsection
- x=fib(n-1);
-
#pragmaompsection
- y=fib(n-2);
- }
-
returnx+y;
- }
- }
-
intmain(intargc,char*argv[])
- {
-
intn=42;
-
intresult=0;
-
-
clock_tt1,t2;
- t1=clock();
- result=fib(n);
- t2=clock();
-
printf("Totaltimeoffib()=%u\n",t2-t1);
-
printf("Fibonacciof%dis%d.\n",n,result);
-
- t1=clock();
- result=fib_parallel(n);
- t2=clock();
-
printf("Totaltimeoffib_parallel()=%u\n",t2-t1);
-
printf("Fibonacciof%dis%d.\n",n,result);
-
-
return0;
- }
注意,和之前直接生成线程一样,我们仅在第一次递归的时候,拆分两个线程,之后的运算都不生成新的线程,也就是整个运算理论上的时间应该缩短50%。程序运行结果如下:
Total time of fib() = 10468
Fibonacci of 42 is 267914296.
Total time of fib_parallel() = 6500
Fibonacci of 42 is 267914296.
这里面的10468的单位是毫秒,也就是10.468秒。而使用了多核后(本人机器双核),时间是6.500秒。时间为前者的62%。
可以看到性能的确得到了提升。
并且在此过程中,你无需掌握任何创建线程的知识,只要调用简单的“注释”标签。
此外下面再列举几个其他的多核开发库的例子:
使用Cilk++
-
intfib(intn)
- {
-
if(n<2)returnn;
-
else{
-
intx=cilk_spawnfib(n-1);
-
inty=fib(n-2);
- cilk_sync;
-
returnx+y;
- }
- }
-
intmain(intargc,char*argv[])
- {
-
intn=atoi(argv[1]);
-
intresult=fib(n);
-
printf("Fibonacciof%dis%d.\n",n,result);
-
return0;
- }
.NET Task Parallel Library中相应的例子
-
PrivateFunctionFiboFullParallel(ByValNAsLong)AsLong
-
IfN<=0ThenReturn0
-
IfN=1ThenReturn1
-
Dimt1AsTasks.Future(OfLong)=Tasks.Future(OfLong).Create(Function()FiboFullParallel(N-1))
-
Dimt2AsTasks.Future(OfLong)=Tasks.Future(OfLong).Create(Function()FiboFullParallel(N-2))
-
Returnt1.Value+t2.Value
-
EndFunction
可以看到无论使用哪种并发平台,代码都非常简洁,没有破坏原有的算法封装,仅仅通过简单的改造就可以实现自动任务的分派。
五、什么情况下该使用多核编程呢?
如果一个任务的执行时间在10-100毫秒,那么就无需使用多核,因为将任务通过多线程分解到多核上计算,然后再将结果集合起来的开销大致需要100毫秒(当然具体多少依据机器的性能以及你所使用的编译器的性能),而且还需要消耗内存的空间。
在OpenMP里面我们可以使用"if clause"来给双核配置增加条件,例如下面的代码很明显,当n小于100000的时候,不使用多核,当n大于的时候再使用
-
#pragmaompparallelforif(n>100000)
-
for(i=0;i<n;,i++){
- ...
- }
六、后记
本文旨在告诉你为何要进行多核开发,以及简单展示了多核开发平台的使用。实际的多核开发要复杂的多,而且我们知道目前的PC机的多核系统都是基于共享内存的,虽然每个核都有自己的一级缓存。因此不同核上的线程在运行时就涉及到对资源竞争使用的问题。除此以外如果应用需要用到IO(硬盘,网络)的时候,也存在同样的问题。因此多核的设计的难点就在于需要具体情况具体分析,找出多核应用的瓶颈,通过改进数据结构或算法,消除或优化这个瓶颈。
分享到:
相关推荐
总体来看,《北大微软学院多核软件开发技术教程》既是一套全面的教学材料,也是多核软件开发领域的入门指南。通过这门课程的学习,开发者不仅能够掌握多核编程的基本技能,还能学会如何在实际项目中应用这些知识,...
### VASP软件包使用入门指南:关键知识点解析 #### 一、VASP简介与功能概述 VASP(Vienna Ab-initio Simulation Package)是一款广泛应用于材料科学领域的量子力学计算软件,由奥地利维也纳大学开发。它基于密度...
"编程指南"章节会指导读者如何使用C66x的嵌入式开发环境,如Code Composer Studio,以及如何编写高效的DSP代码。内容可能包括数据类型选择、优化技巧、中断处理以及并行算法的设计等。此外,还可能涉及编译器选项和...
- **MULTI开发环境**:入门指南详细指导了如何使用Green Hills的MULTI开发环境创建工程。MULTI是一个集成开发环境,专门设计用于多核和多处理器系统,支持多任务调试和性能分析。 - **工程创建**:入门指南介绍了...
《Go入门指南1》是一本面向初学者的Go语言教程,涵盖了从语言的起源、发展到实际编程的全面知识。本书特别关注Go语言在解决传统编程语言如C++所面临问题上的创新之处,旨在帮助读者快速掌握Go语言的精髓。 Go语言的...
总的来说,《Ivo Balbaert:Go 入门指南1》是一本全面且深入的Go语言教程,适合有一定编程基础,希望提升开发效率和性能的开发者学习。通过这本书,读者可以系统地学习Go语言,掌握其独特的并发模型和强大的工具链,...
总之,《Scala程序设计:Java虚拟机多核编程实战》是一本全面介绍Scala语言及其并发编程实践的指南,它将帮助你利用Scala的高级特性,编写出高效、可扩展且富有表现力的代码,适应日益复杂的多核计算环境。
本文通过对多核CPU环境下多线程并行计算的设计方案及发展趋势的探讨,旨在为初学者提供一个全面且实用的入门指南。 #### 多核CPU环境下的计算性能分析 多核CPU通过集成多个处理器核心在一个芯片上,实现了硬件层面...
### LabVIEW 入门指南知识点概述 #### 一、LabVIEW简介 - **定义**:LabVIEW(Laboratory Virtual Instrumentation Engineering Workbench)是一种图形化的编程环境,由美国国家仪器公司(National Instruments, NI...
### LabVIEW 2011 入门指南关键知识点解析 #### 一、LabVIEW 2011 概述 - **定义**: LabVIEW (Laboratory Virtual Instrument Engineering Workbench) 是一款图形化编程环境,由美国国家仪器公司(National ...
《RealView Development Suite 4.0入门指南》深入解析 一、RealView Development Suite 4.0概述 RealView Development Suite 4.0是ARM公司为嵌入式系统设计者和开发者提供的一套全面的软件开发工具包。该工具集...
本文档作为入门指南,将带领初学者逐步了解如何使用这个强大的模块。 首先,安装和配置Real-Time模块是开发过程的起点。安装过程包括安装Real-Time模块本身,这通常通过National Instruments的安装程序完成,确保...
### GeneXus快速入门指南知识点解析 #### 一、GeneXus概述 - **定义**:GeneXus是一款基于知识库的开发工具,主要用于快速构建业务应用系统。 - **核心功能**: - 自动数据模型设计:GeneXus能够根据用户的需求...
本资源“C#编程思想、初学入门指南”是一份详尽的教程,共计300多页,旨在深入浅出地讲解C#的核心概念和编程技巧,帮助初学者快速建立起对C#的理解并能够进行实际编程。 1. **基础语法**:C#的基础语法包括变量声明...
### XMOS入门指南知识点概述 #### 1. XMOS简介 - **定义**:XMOS是一种基于可编程逻辑架构的微控制器技术,以其高度灵活的并行处理能力和强大的实时性能著称。 - **特点**:XMOS技术的核心在于其独特的处理器内核...
### Keil for ARM 的入门指南 #### 1. 安装与配置 **1.1 安装 Keil μVision3** Keil μVision3 是一款广泛应用于 ARM 微控制器开发的集成开发环境(IDE)。用户可以通过 ULINK 仿真器产品光盘或 Keil 官网 ...
【JAVA开发入门】初学者在探索Java编程世界时,往往会面临许多挑战,而这份资源集合正是为了解决这些困扰而准备的。"初学者必看的JAVA开发入门(源代码)"是一个贴心的学习资料包,它包含了作者在学习Java过程中积累...
《Deep Learning Toolbox快速入门指南 (R2020b)_zh_CN.pdf》是一份面向深度学习领域初学者的使用手册,由Mark Hudson Beale、Martin T. Hagan和Howard B. Demuth共同编写。此份文档详细介绍了Deep Learning Toolbox...
安装Ubuntu系统是进入Linux世界的第一步,而入门则是学会使用各种开发和调试工具的基础。 在具体的开发过程中,C语言是编写Linux驱动的主要编程语言。文档中提及了Linux C编程入门,这是因为Linux内核几乎全是用...