单核性能编译
⑴ lammps 可以同时编译 并行 和 单核吗
并行技术可分为三类,分别是线程库、消息传递库和编译器支持。线程库(如 POSIX* 线程和 Windows* API 线程)可实现对线程的显性控制;如果需要对线程进行精细管理,可以考虑使用这些显性线程技术。借助消息传递库(如消息传递接口〔MPI〕),应用程序可同时利用多台计算机,它们彼此间不必共享同一内存空间。MPI 广泛应用于科学计算领域。第三项技术是在编译器中实现的线程处理支持,采用的形式自动并行化。一旦将线程处理引入到应用程序中,开发人员就可能要面对一系列新的编程缺陷(Bug)。其中许多缺陷是难以检测到的,需要付出额外的时间和关注以确保程序的正确运行。一些比较常见的线程处理问题包括:数据争用 ,同步,线程停顿 ,锁 ,共享错误.
并行技术可以分为多进程编程和多线程编程。人们总会用某种IPC(inter-process communication,进程间通信)的形式来实现进程间同步,如管道(pipes),信号量(semaphores),信息队列(message queues),或者共享存储(shared memory)。在所有的这些IPC形式中,共享存储器是最快的(除了门(doors)之外)。在处理进程间资源管理,IPC和同步时,你可以选择 POSIX或者System V的定义。
线程技术早在20世纪60年代就被提出,但真正应用多线程到操作系统中还是在20世纪80年代中期。现在,多线程技术已经被许多操作系统所支持,包括Windows NT/2000和Linux。
在1999年1月发布的Linux 2.2内核中,进程是通过系统调用fork创建的,新的进程是原来进程的子进程。需要说明的是,在Linux 2.2.x中,不存在真正意义上的线程,Linux中常用的线程Pthread实际上是通过进程来模拟的。
也就是说,Linux中的线程也是通过fork创建的,是“轻”进程。Linux 2.2缺省只允许4096个进程/线程同时运行,而高端系统同时要服务上千的用户,所以这显然是一个问题。它一度是阻碍Linux进入企业级市场的一大因素。
2001年1月发布的Linux 2.4内核消除了这个限制,并且允许在系统运行中动态调整进程数上限。因此,进程数现在只受制于物理内存的多少。在高端服务器上,即使只安装了512MB内存,现在也能轻而易举地同时支持1.6万个进程。
在Linux 2.5内核中,已经做了很多改进线程性能的工作。在Linux 2.6中改进的线程模型仍然是由Ingo Molnar 来完成的。它基于一个1:1的线程模型(一个内核线程对应一个用户线程),包括内核内在的对新NPTL(Native Posix Threading Library)的支持,这个新的NPTL是由Molnar和Ulrich Drepper合作开发的。
2003年12月发布的Linux 2.6内核,对进程调度经过重新编写,去掉了以前版本中效率不高的算法。进程标识号(PID)的数目也从3.2万升到10亿。内核内部的大改变之一就是Linux的线程框架被重写,以使NPTL可以运行其上。
在现代操作系统里,同一时间可能有多个内核执行流在执行,因此内核其实象多进程多线程编程一样也需要一些同步机制来同步各执行单元对共享数据的访问。尤其是在多处理器系统上,更需要一些同步机制来同步不同处理器上的执行单元对共享的数据的访问。在主流的Linux内核中包含了几乎所有现代的操作系统具有的同步机制,这些同步机制包括:原子操作、信号量(semaphore)、读写信号量(rw_semaphore)、spinlock、BKL(Big Kernel Lock)、rwlock、brlock(只包含在2.4内核中)、RCU(只包含在2.6内核中)和seqlock(只包含在2.6内核中)。
现在的随着现在计算机体系结构的发展,指令级的并行和线程级的并行都在日新月异地发展着.
⑵ 多核编程与单核编程的区别
多核对于单核的好处是可以真正地同时处理多件事情,因此如果程序想要在多核CPU上获得更好的性能的话,使用多线程技术是必需的。但是采用多线程涉及到线程间数据同步的问题,程序员必须在线程间协调好对数据的访问和处理。
不过我觉得多核编程与单核编程的区别并不是指线程同步问题,因为单核编程同样可以使用多线程,同样需要面对线程同步的问题。同样的代码不经过特别优化,均可以在多核CPU和单核CPU上运行得很好,只不过运行效率不同罢了。
所以我觉得多核编程和单核编程的区别在于对多线程技术需求的迫切程度。如果程序针对多核环境来编程,那么必然要采用多线程技术,以获得更好的性能;如果程序只针对单核环境,那么对多线程并不是那么敏感,但如果它采用了多线程,一旦在多核环境运行,它也能获得性能的提升。
⑶ 单核cpu的并行过程,求解答
CPU并行编程概述
并行编程的演化
一个自然而然的问题是:为什么要用并行编程?在20世纪70年代、80年代甚至90年代的一部分时间里,我们对单线程编程(或者称为串行编程)非常满意。你可以编写一个程序来完成一项任务。执行结束后,它会给你一个结果。任务完成,每个人都会很开心!虽然任务已经完成,但是如果你正在做一个每秒需要数百万甚至数十亿次计算的粒子模拟,或者正在对具有成千上万像素的图像进行处理,你会希望程序运行得更快一些,这意味着你需要更快的CPU。
在2004年以前,CPU制造商IBM、英特尔和AMD都可以为你提供越来越快的处理器,处理器时钟频率从16 MHz、20 MHz、66 MHz、100 MHz,逐渐提高到200 MHz、333 MHz、466 MHz⋯⋯看起来它们可以不断地提高CPU的速度,也就是可以不断地提高CPU的性能。但到2004年时,由于技术限制,CPU速度的提高不能持续下去的趋势已经很明显了。这就需要其他技术来继续提供更高的性能。CPU制造商的解决方案是将两个CPU放在一个CPU内,即使这两个CPU的工作速度都低于单个CPU。例如,与工作在300 MHz速度上的单核CPU相比,以200 MHz速度工作的两个CPU(制造商称它们为核心)加在一起每秒可以执行更多的计算(也就是说,直观上看2×200 > 300)。
听上去像梦一样的“单CPU多核心”的故事变成了现实,这意味着程序员现在必须学习并行编程方法来利用这两个核心。如果一个CPU可以同时执行两个程序,那么程序员必须编写这两个程序。但是,这可以转化为两倍的程序运行速度吗?如果不能,那我们的2×200 > 300的想法是有问题的。如果一个核心没有足够的工作会怎么样?也就是说,只有一个核心是真正忙碌的,而另一个核心却什么都不做?这样的话,还不如用一个300 MHz的单核。引入多核后,许多类似的问题就非常突出了,只有通过编程才能高效地利用这些核心。
核心越多,并行性越高
程序员不能简单地忽略CPU制造商每年推出的更多数量的核心。2015年,英特尔在市场上推出8核台式机处理器i7-5960X[11]和10核工作站处理器,如Xeon E7-8870 [14]。很明显,这种多核狂热在可预见的未来会持续下去。并行编程从2000年年初的一种奇异的编程模型转变为2015年唯一被接受的编程模型。这种现象并不局限于台式电脑。在移动处理器方面,iPhone和Android手机都有2个或4个核。预计未来几年,移动领域的核心数量将不断增加。
那么,什么是线程?要回答这个问题,让我们来看看8核INTEL CPU i7-5960X [11]。 INTEL的文档说这是一个8C/16T CPU。换句话说,它有8个核心,但可以执行16个线程。你也许听到过并行编程被错误地称为多核编程。正确的术语应该是多线程编程。这是因为当CPU制造商开始设计多核架构时,他们很快意识到通过共享一些核心资源(如高速缓存)来实现在一个核心中同时执行两项任务并不困难。
类比1.1:核心与线程
图1-1显示了两个兄弟Fred和Jim,他们是拥有两台拖拉机的农民。每天,他们开车从农舍到椰子树所在的地方,收获椰子并把它们带回农舍。他们用拖拉机内的锤子来收获(处理)椰子。整个收获过程由两个独立但有序的任务组成,每个任务需要30秒:任务1是从拖拉机走向椰子树,每次带回1颗椰子。任务2是用锤子敲碎(处理)它们,并将它们存放在拖拉机内。Fred每分钟可以处理1颗椰子,而Jim每分钟也可以处理1颗椰子。综合起来,他们俩每分钟可以处理2颗椰子。
一天,Fred的拖拉机发生了故障。他把拖拉机留在修理厂,并把椰子锤忘在了拖拉机内。回到农舍的时候已经太迟了,但他们仍然有工作要做。只使用Jim的拖拉机和里面的1把椰子锤,他们还能每分钟处理2颗椰子吗?
核心与线程
让我们来看看图1-1中描述的类比1.1。如果收获1颗椰子需要完成两个连续的任务(我们将它们称为线程):线程1从树上摘取1颗椰子并花费30秒将它带回拖拉机,线程2花费30秒用拖拉机内的锤子敲碎(处理)该椰子,这样可以在60秒内收获1颗椰子(每分钟1颗椰子)。如果Jim和Fred各自都有自己的拖拉机,他们可以简单地收获两倍多的椰子(每分钟2颗椰子),因为在收获每颗椰子时,他们可以共享从拖拉机到椰子树的道路,并且他们各自拥有自己的锤子。
在这个类比中,一台拖拉机就是一个核心,收获一颗椰子就是针对一个数据单元的程序执行。椰子是数据单元,每个人(Jim、Fred)是一个执行线程,需要使用椰子锤。椰子锤是执行单元,就像核心中的ALU一样。该程序由两个互相依赖的线程组成:在线程1执行结束之前,你无法执行线程2。收获的椰子数量意味着程序性能。性能越高,Jim和Fred销售椰子挣的钱就越多。可以将椰子树看作内存,你可以从中获得一个数据单元(椰子),这样在线程1中摘取一颗椰子的过程就类似于从内存中读取数据单元。
并行化更多的是线程还是核心
现在,让我们看看如果Fred的拖拉机发生故障后会发生什么。过去他们每分钟都能收获两颗椰子,但现在他们只有一台拖拉机和一把椰子锤。他们把拖拉机开到椰子树附近,并停在那儿。他们必须依次地执行线程1(Th1)和线程2(Th2)来收获1颗椰子。他们都离开拖拉机,并在30秒内走到椰子树那儿,从而完成了Th1。他们带回挑好的椰子,现在,他们必须敲碎椰子。但因为只有1把椰子锤,他们不能同时执行Th2。Fred不得不等Jim先敲碎他的椰子,并且在Jim敲碎后,他才开始敲。这需要另外的30+30秒,最终他们在90秒内收获2颗椰子。虽然效率不如每分钟2颗椰子,但他们的性能仍然从每分钟1颗提升至每分钟1.5颗椰子。
收获一些椰子后,Jim问了自己一个问题:“为什么我要等Fred敲碎椰子?当他敲椰子时,我可以立即走向椰子树,并摘获下1颗椰子,因为Th1和Th2需要的时间完全相同,我们肯定不会遇到需要等待椰子锤空闲的状态。在Fred摘取1颗椰子回来的时候,我会敲碎我的椰子,这样我们俩都可以是100%的忙碌。”这个天才的想法让他们重新回到每分钟2颗椰子的速度,甚至不需要额外的拖拉机。重要的是,Jim重新设计了程序,也就是线程执行的顺序,让所有的线程永远都不会陷入等待核心内部共享资源(比如拖拉机内的椰子锤)的状态。正如我们将很快看到的,核心内部的共享资源包括ALU、FPU、高速缓存等,现在,不要担心这些。
我在这个类比中描述了两个配置场景,一个是2个核心(2C),每个核心可以执行一个单线程(1T);另一个是能够执行2个线程(2T)的单个核心(1C)。在CPU领域将两种配置称为2C/2T与lC/2T。换句话说,有两种方法可以让一个程序同时执行2个线程:2C/2T(2个核心,每个核心都可以执行1个线程—就像Jim和Fred的两台单独的拖拉机一样)或者lC/2T(单个核心,能够执行2个线程—就像Jim和Fred共享的单台拖拉机一样)。尽管从程序员的角度来看,它们都意味着具有执行2个线程的能力,但从硬件的角度来看,它们是非常不同的,这要求程序员充分意识到需要共享资源的线程的含义。否则,线程数量的性能优势可能会消失。再次提醒一下:全能的INTEL i7-5960X [11] CPU是8C/l6T,它有8个核心,每个核心能够执行2个线程。
图1-2显示了三种情况:a)是具有2个独立核心的2C/2T情况;b)是具有糟糕编程的1C/2T情况,每分钟只能收获1.5颗椰子;c)是对椰子锤的需求永远不会同时发生的顺序正确版本,每分钟可以收获2颗椰子。
核心资源共享的影响
Jim为自己的发现感到自豪,他们的速度提高到每分钟2颗椰子,Jim希望继续创造一些方法来用一台拖拉机完成更多的工作。一天,他对Fred说:“我买了一把新的自动椰子锤,它在10秒内就能敲碎1颗椰子。”他们对这一发现非常满意,立即出发并将拖拉机停在椰子树旁。这次他们知道在开始收获前必须先做好计划⋯⋯
Fred问道:“如果我们的Th1需要30秒,而Th2需要10秒,并且我们唯一需要共享资源的任务是Th2(椰子锤),我们应该如何收获椰子?”答案对他们来说很清楚:唯一重要的是线程的执行顺序(即程序的设计),应确保他们永远不会遇到两人同时执行Th2并需要唯一的椰子锤(即共享核心资源)的情况。换句话说,它们的程序由两个互相依赖的线程组成:Th1需要30秒,并且不需要共享(内存)资源,因为两个人可以同时步行到椰子树。Th2需要10秒并且不能同时执行,因为他们需要共享(核心)资源:椰子锤。由于每颗椰子需要30+10=40秒的总执行时间,他们能够期望的最好结果是40秒收获2颗椰子,如
图1-2 d所示。如果每个人都按顺序执行Th1和Th2,且不等待任何共享资源,则会发生这种情况。所以,他们的平均速度将是每分钟3颗椰子(即每颗椰子平均20秒)。
内存资源共享的影响
用新的椰子锤实现了每分钟收获3颗椰子后,Jim和Fred第二天开始工作时看到了可怕的一幕。因为昨晚的一场大雨阻塞了半边道路,从拖拉机到椰子树的道路今天只能由一个人通行。所以,他们再次制订计划⋯⋯现在,他们有2个线程,每个线程都需要一个不能共享的资源。Th1(30秒—表示为30s)只能由一人执行,而Th2(10s)也只能由一人执行。怎么办?
考虑多种选择后,他们意识到其速度的限制因素是Th1,他们能达到的最好目标是30秒收获1颗椰子。当可以同时执行Th1(共享内存访问)时,每个人可以顺序地执行10+30s,并且两个人都可以持续运行而无须访问共享资源。但是现在没有办法对这些线程进行排序。他们能够期望的最好结果是执行10+30s并等待20s,因为在此期间两人都需要访问内存。他们的速度回到平均每分钟2颗椰子,如图1-2 e所示。
这场大雨使他们的速度降低到每分钟2颗椰子。Th2不再重要,因为一个人可以不慌不忙地敲椰子,而另一个人正在去摘取椰子的路上。Fred提出了这样一个想法:他们应该从农舍再拿一把(较慢)椰子锤来帮忙。然而,这对于此时的情况绝对没有帮助,因为收获速度的限制因素是Th1。这种来自于某个资源的限制因素被称为资源竞争。这个例子展示了当访问内存是我们程序执行速度的限制因素时会发生什么。处理数据的速度有多快(即核心运行速度)已无关紧要。我们将受到数据获取速度的限制。即使Fred有一把可以在1秒钟内敲碎椰子的椰子锤,但如果存在内存访问竞争,他们仍然会被限制为每分钟2颗椰子。在本书中,我们将区分两种不同类型的程序:核心密集型,该类型不大依赖于内存访问速度;存储密集型,该类型对内存访问速度高度敏感,正如我刚才提到的那样。
第一个串行程序
我们已经理解了椰子世界中的并行编程,现在是时候将这些知识应用于真实计算机编程了。我会先介绍一个串行(即单线程)程序,然后将其并行化。我们的第一个串行程序imf?lip.c读入图1-3(左)中的小狗图片并将其水平(中)或垂直(右)翻转。为了简化程序的解释,我们将使用Bitmap(BMP)图像格式,并将结果也输出为BMP格式。这是一种非常容易理解的图像格式,可以让我们专注于程序本身。不要担心本章中的细节,它们很快就会被解释清楚,目前可以只关注高层的功能。
imflip.c源文件可以在Unix提示符下编译和执行,如下所示:
gcc imflip.c -o imflip
./imflip dogL.bmp dogh.bmp V
在命令行中用“H”指定水平翻转图像(图1-3中),用“V”指定垂直翻转(图1-3右侧)。你将看到如下所示的输出(数字可能不同,取决于你电脑的速度):
Input BMP File name : dogL.bmp (3200×2400)
Output BMP File name : dogh.bmp (3200×2400)
Total execution time : 81.0233 ms (10.550 ns per pixel)
运行该程序的CPU速度非常快,以致我必须将原始的640×480的图像dog.bmp扩展为3200×2400的dogL.bmp,这样它的运行时间才能被测量出来;dogL.bmp的每个维度扩大到原来的5倍,因此比dog.bmp大25倍。统计时间时,我们必须在图像翻转开始和结束时记录CPU的时钟。
理解数据传输速度
从磁盘读取图像的过程(无论是SSD还是硬盘驱动器)应该从执行时间中扣除,这很重要。换句话说,我们从磁盘读取图像,并确保它位于内存中(在我们的数组中),然后只统计翻转操作所需的时间。由于不同硬件部件的数据传输速度存在巨大差异,我们需要分别分析在磁盘、内存和CPU上花费的时间。
在本书将要编写的众多并行程序中,我们重点关注CPU执行时间和内存访问时间,因为我们可以控制它们。磁盘访问时间(称为I/O时间)通常在单线程中就达到极限,因而几乎看不到多线程编程的好处。另外,请记住,当我们开始GPU编程时,较慢的I/O速度会严重困扰我们,因为I/O是计算机中速度最慢的部分,并且从CPU到GPU的数据传输要通过I/O子系统的PCI express总线进行,因此我们将面临如何将数据更快地提供给GPU的挑战。没有人说GPU编程很容易!为了让你了解不同硬件部件的传输速度,我在下面列举了一些:
典型的网卡(NIC)具有1 Gbps的传输速度(千兆比特每秒或一亿比特每秒)。这些卡俗称“千兆网卡”或“Gig网卡”。请注意,1 Gbps只是“原始数据”的数量,其中包括大量的校验码和其他同步信号。传输的实际数据量少于此数量的一半。我的目的是给读者一个大致的概念,这个细节对我们来说并不重要。
即使连接到具有6 Gbps峰值传输速度的SATA3接口,典型的硬盘驱动器(HDD)也几乎无法达到1 Gbps〜2 Gbps的传输速度。HDD的机械读写性质根本不能实现快速的数据访问。传输速度甚至不是硬盘的最大问题,最大问题是定位时间。HDD的机械磁头需要一段时间在旋转的金属柱面上定位需要的数据,这迫使它在磁头到达数据所在位置前必须等待。如果数据以不规则的方式分布(即碎片式的存放),则可能需要毫秒(ms)级的时间。因此,HDD的传输速度可能远远低于它所连接的SATA3总线的峰值速度。
连接到USB 2.0端口的闪存磁盘的峰值传输速度为480 Mbps(兆比特每秒或百万比特每秒)。但是,USB 3.0标准具有更快的5 Gbps传输速度。更新的USB 3.1可以达到10 Gbps左右的传输速率。由于闪存磁盘使用闪存构建,它不需要查找时间,只需提供地址即可直接访问数据。
典型的固态硬盘(SSD)可以连接在SATA3接口上,达到接近4 Gbps〜5 Gbps的读取速度。因此,实际上SSD是唯一可以达到SATA3接口峰值速度的设备,即以预期的6 Gbps峰值速率传输数据。
一旦数据从I/O(SDD、HDD或闪存磁盘)传输到CPU的内存中,传输速度就会大大提高。已发展到第6代的Core i7系列(i7-6xxx),更高端的Xeon CPU使用DDR2、DDR3和DDR4内存技术,内存到CPU的传输速度为20 GBps〜60 GBps(千兆字节每秒)。注意这个速度是千兆字节。一个字节有8个比特,为与其他较慢的设备进行比较,转换为存储访问速度时为160 Gbps〜480 Gbps(千兆比特每秒)。
正如我们将在第二部分及以后所看到的,GPU内部存储器子系统的传输速度可以达到100 GBps〜1000 GBps。例如,新的Pascal系列GPU就具有接近后者的内部存储传输速率。转换后为8000 Gbps,比CPU内部存储器快一个数量级,比闪存磁盘快3个数量级,比HDD快近4个数量级。
imflip.c中的main( )函数
代码1.1中所示的程序会读取一些命令行参数,并按照命令行参数垂直或水平地翻转输入图像。命令行参数由C放入argv数组中。
clock( )函数以毫秒为单位统计时间。重复执行奇数次(例如129次)操作可以提高时间统计的准确性,操作重复次数在"#define REPS 129"行中指定。该数字可以根据你的系统更改。
ReadBMP( )函数从磁盘读取源图像,WriteBMP( )将处理后的(即翻转的)图像写回磁盘。从磁盘读取图像和将图像写入磁盘的时间定义为I/O时间,我们从处理时间中去除它们。这就是为什么我在实际的图像翻转代码之间添加"start = clock( )"和"stop = c1ock( )"行,这些代码对已在内存中的图像进行翻转操作,因此有意地排除了I/O时间。
在输出所用时间之前,imf?lip.c程序会使用一些free( )函数释放所有由ReadBMP( )分配的内存以避免内存泄漏。
代码1.1:imflip.c的main( ){⋯}
imflip.c中的main( )函数读取3个命令行参数,用以确定输入和输出的BMP图像文件名以及翻转方向(水平或垂直)。该操作会重复执行多次(REPS)以提高计时的准确性。
垂直翻转行:FlipImageV( )
代码1.2中的FlipImageV( )遍历每一列,并交换该列中互为垂直镜像的两个像素的值。有关Bitmap(BMP)图像的函数存放在另一个名为ImageStuff.c的文件中,ImageStuff.h是对应的头文件,我们将在下一章详细解释它们。图像的每个像素都以“struct Pixel”类型存储,包含unsigned char类型的该像素的R、G和B颜色分量。由于unsigned char占用1个字节,所以每个像素需要3个字节来存储。
ReadBMP( )函数将图像的宽度和高度分别放在两个变量ip.Hpixels和ip.Vpixels中。存储一行图像需要的字节数在ip.Hbytes中。FlipImageV( )函数包含两层循环:外层循环遍历图像的ip.Hbytes,也就是每一列,内层循环一次交换一组对应的垂直翻转像素。
代码1.2:imflip.c⋯FlipImageV( ){⋯}
对图像的行做垂直翻转,每个像素都会被读取并替换为镜像行中的相应像素。
水平翻转列:FlipImageH( )
imf?lip.c的FlipImageH( )实现图像的水平翻转,如代码1.3所示。除了内层循环相反,该函数与垂直翻转的操作完全相同。每次交换使用“struct Pixel”类型的临时像素变量pix。
由于每行像素以3个字节存储,即RGB、RGB、RGB⋯⋯因此访问连续的像素需要一次读取3个字节。这些细节将在下一节介绍。现在我们需要知道的是,以下几行代码:
只是读取位于垂直的第row行和水平的第col列处的一个像素。像素的蓝色分量在地址img[row][col]处,绿色分量在地址img[row][col+1]处,红色分量在img[row][col+2]处。在下一章中我们将看到,指向图像起始地址的指针img由ReadBMP( )为其分配空间,然后由main( )传递给FlipImageH( )函数。
代码1.3:imflip.cFlipImageH( ){⋯}
进行水平翻转时,每个像素都将被读取并替换为镜像列中相应的像素。
⑷ 编程吃cpu单核还是多核
编程序使用的IDE工具,一般是多线程的,一边开发代码、一边自动自动分析语法和提示函数用法。这就用到CPU多核性能。编译程序时只会使用一颗CPU的单线程处理。
⑸ 如何使用ccache加快编译
C++代码一直以其运行时的高性能高调面对世人, 但是说起编译速度,却只有低调的份了。比如我现在工作的源代码,哪怕使用Incredibuild调动近百台机子,一个完整的build也需要四个小时,恐怖!!!虽然平时开发一般不需要在本地做完整的build,但编译几个相关的工程就够你等上好一段时间的了(老外管这个叫monkey around,相当形象)。想想若干年在一台单核2.8GHZ上工作时的场景 - 面前放本书,一点build按钮,就低头读一会书~~~往事不堪回首。
可以想象,如果不加以重视,编译速度极有可能会成为开发过程中的一个瓶颈。那么,为什么C++它就编译的这么慢呢?
我想最重要的一个原因应该是C++基本的"头文件-源文件"的编译模型:
每个源文件作为一个编译单元,可能会包含上百甚至上千个头文件,而在每一个编译单元,这些头文件都会被从硬盘读进来一遍,然后被解析一遍。
⑹ 多核与单核cpu用途比较
多核在多线程软件(一个任务同时利用多个核心运算,如杀毒,文件压缩,3D渲染,视频转换,科学运算,大型程序编译等)或者多个很占cpu资源的程序应用时性能比单核好很多,比如一边在转视频或压缩文件,再运行其他程序单核反应就会比较慢~如果有1G以上内存,单核性能也不错的话,日常多任务也不会很卡,如同时开很多网页,qq,影音,下载,文档等,这些程序只是启动时要用一下cpu,启动后占很少的cpu资源。
⑺ i73930K为什么比其它6核处理器便宜得多有什么缺点吗
因为主流应用没有这个数据量。多核心、多线程CPU要发挥性能,前提是必须要有足够多的线程。但多线程开发是个坑活,不是简单fork出来个线程就能多线程的。数据锁定、同步、线程间通信,全都是坑。
单核性能是上不去,但事实上是你不玩大型游戏、多开挂机,不剪视频,不搞3D渲染,不玩虚拟机集群,不三天两头编译一个GCC级别的应用,轻薄本上的4核CPU都没什么机会满载,除了屏幕小点外,很多人根本不觉得轻薄本和台式机使用上有什么区别。
只有数据量足够大了,例如1080P 24FPS规格的视频解码后一分钟有近9 GB数据,需要对如此大量的数据进行压缩编码的视频剪辑;根据3D模型和纹理光源设置渲染出这个数据量的CG渲染,类似这样的天然就是海量数据的应用,才会有大批数据可以用相同的处理过程去分批处理——然后很自然就可以用多个线程,每个线程处理一批,最后汇总结果即可。或者是超过10W个源码文件的GCC——通常是编译后还要用编译出来的二进制程序再编译一到两次,天然就是大量任务并且每个任务可以用独立线程处理。
需要说一下的是虽然大型3D游戏也是需要实时渲染出这个数据量的画面,而且往往数据更大量——毕竟24 FPS的游戏体验很差,60 FPS都不一定满意,144 FPS还没到头。但大部分计算是由GPU负担,CPU只是处理一些GPU不方便处理的计算以及运行GPU的驱动程序而已。这些GPU不方便处理的计算任务通常包括但不限于用户输入响应、网络数据传输、游戏AI等,每一项任务都可以分配给不同的线程去处理,但单项任务往往不适合再进一步拆分开来使用多个线程来处理——适合多线程处理的任务通常也适合用性能更强的GPU计算。这些任务的数量总是有限的,某些任务的计算量是很小的,因此CPU核心再多往往也无法发挥。
⑻ 浅谈怎样加快C++代码的编译速度
C++代码一直以其运行时的高性能高调面对世人, 但是说起编译速度,却只有低调的份了。比如我现在工作的源代码,哪怕使用Incredibuild调动近百台机子,一个完整的build也需要四个小时,恐怖!!!虽然平时开发一般不需要在本地做完整的build,但编译几个相关的工程就够你等上好一段时间的了(老外管这个叫monkey around,相当形象)。想想若干年在一台单核2.8GHZ上工作时的场景 - 面前放本书,一点build按钮,就低头读一会书~~~往事不堪回首。 可以想象,如果不加以重视,编译速度极有可能会成为开发过程中的一个瓶颈。那么,为什么C++它就编译的这么慢呢? 我想最重要的一个原因应该是C++基本的“头文件-源文件”的编译模型: 1.每个源文件作为一个编译单元,可能会包含上百甚至上千个头文件,而在每一个编译单元,这些头文件都会被从硬盘读进来一遍,然后被解析一遍。 2.每个编译单元都会产生一个obj文件,然后所以这些obj文件会被link到一起,并且这个过程很难并行。 这里,问题在于无数头文件的重复load与解析,以及密集的磁盘操作。 下面从各个角度给出一些加快编译速度的做法,主要还是针对上面提出的这个关键问题。 一、代码角度 1、在头文件中使用前置声明,而不是直接包含头文件。 不要以为你只是多加了一个头文件,由于头文件的“被包含”特性,这种效果可能会被无限放大。所以,要尽一切可能使头文件精简。很多时候前置申明某个namespace中的类会比较痛苦,而直接include会方便很多,千万要抵制住这种诱惑;类的成员,函数参数等也尽量用引用,指针,为前置声明创造条件。 2、使用Pimpl模式 Pimpl全称为Private Implementation。传统的C++的类的接口与实现是混淆在一起的,而Pimpl这种做法使得类的接口与实现得以完全分离。如此,只要类的公共接口保持不变,对类实现的修改始终只需编译该cpp;同时,该类提供给外界的头文件也会精简许多。 3、高度模块化 模块化就是低耦合,就是尽可能的减少相互依赖。这里其实有两个层面的意思。一是文件与文件之间,一个头文件的变化,尽量不要引起其他文件的重新编译;二是工程与工程之间,对一个工程的修改,尽量不要引起太多其他工程的编译。这就要求头文件,或者工程的内容一定要单一,不要什么东西都往里面塞,从而引起不必要的依赖。这也可以说是内聚性吧。 以头文件为例,不要把两个不相关的类,或者没什么联系的宏定义放到一个头文件里。内容要尽量单一,从而不会使包含他们的文件包含了不需要的内容。记得我们曾经做过这么一个事,把代码中最“hot”的那些头文件找出来,然后分成多个独立的小文件,效果相当可观。 其实我们去年做过的refactoring,把众多DLL分离成UI与Core两个部分,也是有着相同的效果的 - 提高开发效率。 4、删除冗余的头文件 一些代码经过上十年的开发与维护,经手的人无数,很有可能出现包含了没用的头文件,或重复包含的现象,去掉这些冗余的include是相当必要的。当然,这主要是针对cpp的,因为对于一个头文件,其中的某个include是否冗余很难界定,得看是否在最终的编译单元中用到了,而这样又可能出现在一个编译单元用到了,而在另外一个编译单元中没用到的情况。 之前曾写过一个Perl脚本用来自动去除这些冗余的头文件,在某个工程中竟然去掉多达了5000多个的include。 5、特别注意inline和template 这是C++中两种比较“先进”的机制,但是它们却又强制我们在头文件中包含实现,这对增加头文件的内容,从而减慢编译速度有着很大的贡献。使用之前,权衡一下。
⑼ 频率高线程少性能低和频率低线程多性能高那个好
IPC
初接触CPU的小白可能会好奇,为什么R5-5600X频率才4.6,单核却能在一众频率5.0+的i7、i9之上?以前的FX-9590频率也飙到5.0,为什么却那么弱?
其实CPU有一个指标叫“IPC”,全称Instruction Per Clock,它指CPU每一个频率周期里处理的指令数量。小白可以简单粗暴记忆——
CPU性能=频率*IPC
IPC愈高,基本意味着CPU架构愈优秀。但没有人会标注IPC的具体数值,一般只有Intel和AMD开发布会的时候,才会公布IPC“相比上一代提升了多少”——
CPU规格表
Intel桌面处理器规格表www.mydrivers.comAMD桌面处理器规格表www.mydrivers.com
单核性能
直接用规格表里的“加速频率”乘以对照表里的百分比,即可计算出来。
i9-10850K,单核性能为5.2;
E3-1230V2,单核性能为3.7*0.85≈3.15;
X3440超频3.6,单核性能为3.6*0.77≈2.77;
R7-5800X,单核性能为4.7*1.27≈5.97;
R5-2600,单核性能为3.9*0.93≈3.63;
X4-955超频4.0,单核性能为4.0*0.61=2.44。
单核性能主要影响:软件开关速度;较简单的运算(如Excel公式、Python脚本);大多数普通应用软件;游戏帧数的上限(前提是显卡足够强)。
多核性能
注意规格表里并没有标出“全核加速频率”,还需要自己网络。Intel的全核加速频率是有明确标识的,用Aida64可以查到,如i9-10850K为4.8;AMD则没有明确标识,需要通过一些测评报告才能查到,如R7-5800X为4.5。
多核性能用“全核加速频率”乘以核心数,有超线程的时候,Intel一个超线程≈0.25个核心;AMD的超线程效率稍高,可以≈0.3个核心。然后再乘以对照表里的百分比。
i9-10850K全核加速频率4.8,10核20线相当于12.5核,多核性能为12.5*4.8=60;
E3-1230V2全核加速频率3.5,4核8线相当于5核,多核性能为3.5*5*0.85≈14.88;
X3440超频3.6,4核8线相当于5核,多核性能为3.6*5*0.77=13.86;
R7-5800X全核加速频率4.5,8核16线相当于10.4核,多核性能为4.5*10.4*1.27≈59.44;
R5-2600全核加速频率3.7,6核12线相当于7.8核,多核性能为3.7*7.8*0.93≈26.84;
X4-955超频4.0,4核,多核性能为4.0*4*0.61=9.76。
多核性能主要影响:软件多开数目(前提是内存足够大)、较复杂的运算(如压缩解压缩、代码编译)、视频渲染等较重度应用、游戏帧数的下限(前提是单核没过分低)。
回到一开始的问题
Q:R5-5600X还是R7-3700X?
A:单核基本不用算,5600X完胜。多核5600X是4.15*7.8*1.27≈41.11,3700X是3.95*10.4*1.07≈43.96,差距非常小。所以绝大多数时候只推荐5600X。除非你确定你的工作能优化到16个线程(PR、C4D)才考虑3700X。
Q:R7-5800X还是R9-3900X?
A:单核基本不用算,5800X完胜。多核5800X是4.5*10.4*1.27≈59.44,3900X是4.05*15.6*1.07≈67.60,还是有一定差距。所以应根据自己的具体需求选择。
Q:想买i5-10600KF,有必要上Z主板超频吗?
A:i5-10600KF默认加速频率是单核4.8全核4.5,体质普遍较差,超频到4.8基本到顶了。单核无提升,多核只提升4.8/4.5-1≈6.7%,所以没必要超频啦。
Q:i5-4590不够用了,要升级i7-4790K再超频用吗?
A:i7-4790K用一般的主板和散热,超频到4.6基本到顶了,4核8线相当于5核,单核4.6*0.91≈4.19,多核4.6*5*0.91=20.93。和它规模相同的i3-10100F单核4.3,多核4.1*5=20.5。整体差不多,功耗少很多,不用折腾超频,板+u+新内存也贵不到哪里去。所以建议不要升级,直接换新平台叭。
这种判断的局限性
IPC是会随频率衰减的,实际上本文的判断方法,在单核4GHz上下最为准确。频率过分高的时候,会比实际偏高,比如你算出来5.8的实际可能更贴近5.5;频率过分低的时候,会比实际偏低,比如你算出来2.2的实际可能更贴近2.5。老架构超频后的表现同理,i5-2550K超频到5.0后,算出来单核多核都比i3-9100F强,但是实际表现差不多。
有些应用依赖内存缓存还有总线的性能,导致实际会在你的判断基础上打折扣。比如AMD Zen之后的平台内存控制器外挂,导致延迟偏高,一般要搭配3200内存玩游戏,才能达到Intel同性能搭配2666内存的效果。比如E5450的前端总线带宽上限较低,导致GTA5一类较新游戏的表现会明显受限,反而不如算出来不如它的X4-955。
有些应用依赖或会调用较新指令集,这种情况下不能以判断论处。比如X4-955没有SSE4.2指令集,而最终幻想15依赖这个,到头来游戏你打都打不开。i7-870缺少AVX指令集,而格式工厂会调用这个去加速,到头来你剪片速度会很慢,比i3-2120都慢。
尚未发布的11代酷睿台式机,119%属于根据10代10nm笔记本Ice Lake表现推算。实际结果应该会比这个稍低,毕竟台式机还是14nm,且IPC会随频率衰减。
⑽ 阿里平头哥宣布,自研处理器已成功落地,它的性能如何
阿里巴巴宣布旗下的平头哥半导体完成了安卓10对RISC-V的移植并开源了全部相关代码。从平头哥社区发布的信息来看,安卓10系统已经可以在玄铁910芯片上流畅运行。至于玄铁910,则是前年阿里巴巴旗下的平头哥半导体发布的一款号称是业界最强的RISC-V处理器内核IP。资料显示,玄铁910单核性能达到7.1 Coremark/MHz,主频达到2.5GHz,比当时业界最好的RISC-V处理器性能高40%以上。据介绍,玄铁910可以用于设计制造高性能端上芯片,应用于5G、人工智能以及自动驾驶等领域。