当前位置:首页 » 编程软件 » 编译器对多核

编译器对多核

发布时间: 2022-12-11 05:53:21

⑴ C语言中的MPI编程和多线程有什么区别,MPI编程中针对的是一台电脑多核还是多台电脑谢谢!

MPI(MPI是一个标准,有不同的具体实现,比如MPICH等)是多主机联网协作进行并行计算的工具,当然也可以用于单主机上多核/多CPU的并行计算,不过效率低。它能协调多台主机间的并行计算,因此并行规模上的可伸缩性很强,能在从个人电脑到世界TOP10的超级计算机上使用。缺点是使用进程间通信的方式协调并行计算,这导致并行效率较低、内存开销大、不直观、编程麻烦。OpenMP是针对单主机上多核/多CPU并行计算而设计的工具,换句话说,OpenMP更适合单台计算机共享内存结构上的并行计算。由于使用线程间共享内存的方式协调并行计算,它在多核/多CPU结构上的效率很高、内存开销小、编程语句简洁直观,因此编程容易、编译器实现也容易(现在最新版的C、C++、Fortran编译器基本上都内置OpenMP支持)。不过OpenMP最大的缺点是只能在单台主机上工作,不能用于多台主机间的并行计算!如果要多主机联网使用OpenMP(比如在超级计算机上),那必须有额外的工具帮助,比如MPI+OpenMP混合编程。或者是将多主机虚拟成一个共享内存环境(Intel有这样的平台),但这么做效率还不如混合编程,唯一的好处是编程人员可以不必额外学习MPI编程。

⑵ 多核处理器中的“核”指的是甚么

所谓多核处理器,简单说就是在一块CPU基板上集成多个处理器核心,通过并行总线将各处理器核心连接起来的,工作速度快

java内存模型的JMM简介

1)JSR133:
在Java语言规范里面指出了JMM是一个比较开拓性的尝试,这种尝试视图定义一个一致的、跨平台的内存模型,但是它有一些比较细微而且很重要的缺点。其实Java语言里面比较容易混淆的关键字主要是synchronized和volatile,也因为这样在开发过程中往往开发者会忽略掉这些规则,这也使得编写同步代码比较困难。
JSR133本身的目的是为了修复原本JMM的一些缺陷而提出的,其本身的制定目标有以下几个: 保留目JVM的安全保证,以进行类型的安全检查: 提供(out-of-thin-air safety)无中生有安全性,这样“正确同步的”应该被正式而且直观地定义 程序员要有信心开发多线程程序,当然没有其他办法使得并发程序变得很容易开发,但是该规范的发布主要目标是为了减轻程序员理解内存模型中的一些细节负担 提供大范围的流行硬件体系结构上的高性能JVM实现,现在的处理器在它们的内存模型上有着很大的不同,JMM应该能够适合于实际的尽可能多的体系结构而不以性能为代价,这也是Java跨平台型设计的基础 提供一个同步的习惯用法,以允许发布一个对象使他不用同步就可见,这种情况又称为初始化安全(initialization safety)的新的安全保证 对现有代码应该只有最小限度的影响 2)同步、异步【这里仅仅指概念上的理解,不牵涉到计算机底层基础的一些操作】:
在系统开发过程,经常会遇到这几个基本概念,不论是网络通讯、对象之间的消息通讯还是Web开发人员常用的Http请求都会遇到这样几个概念,经常有人提到Ajax是异步通讯方式,那么究竟怎样的方式是这样的概念描述呢?
同步:同步就是在发出一个功能调用的时候,在没有得到响应之前,该调用就不返回,按照这样的定义,其实大部分程序的执行都是同步调用的,一般情况下,在描述同步和异步操作的时候,主要是指代需要其他部件协作处理或者需要协作响应的一些任务处理。比如有一个线程A,在A执行的过程中,可能需要B提供一些相关的执行数据,当然触发B响应的就是A向B发送一个请求或者说对B进行一个调用操作,如果A在执行该操作的时候是同步的方式,那么A就会停留在这个位置等待B给一个响应消息,在B没有任何响应消息回来的时候,A不能做其他事情,只能等待,那么这样的情况,A的操作就是一个同步的简单说明。
异步:异步就是在发出一个功能调用的时候,不需要等待响应,继续进行它该做的事情,一旦得到响应了过后给予一定的处理,但是不影响正常的处理过程的一种方式。比如有一个线程A,在A执行的过程中,同样需要B提供一些相关数据或者操作,当A向B发送一个请求或者对B进行调用操作过后,A不需要继续等待,而是执行A自己应该做的事情,一旦B有了响应过后会通知A,A接受到该异步请求的响应的时候会进行相关的处理,这种情况下A的操作就是一个简单的异步操作。
3)可见性、可排序性
Java内存模型的两个关键概念:可见性(Visibility)和可排序性(Ordering)
开发过多线程程序的程序员都明白,synchronized关键字强制实施一个线程之间的互斥锁(相互排斥),该互斥锁防止每次有多个线程进入一个给定监控器所保护的同步语句块,也就是说在该情况下,执行程序代码所独有的某些内存是独占模式,其他的线程是不能针对它执行过程所独占的内存进行访问的,这种情况称为该内存不可见。但是在该模型的同步模式中,还有另外一个方面:JMM中指出了,JVM在处理该强制实施的时候可以提供一些内存的可见规则,在该规则里面,它确保当存在一个同步块时,缓存被更新,当输入一个同步块时,缓存失效。因此在JVM内部提供给定监控器保护的同步块之中,一个线程所写入的值对于其余所有的执行由同一个监控器保护的同步块线程来说是可见的,这就是一个简单的可见性的描述。这种机器保证编译器不会把指令从一个同步块的内部移到外部,虽然有时候它会把指令由外部移动到内部。JMM在缺省情况下不做这样的保证——只要有多个线程访问相同变量时必须使用同步。简单总结:
可见性就是在多核或者多线程运行过程中内存的一种共享模式,在JMM模型里面,通过并发线程修改变量值的时候,必须将线程变量同步回主存过后,其他线程才可能访问到。
可排序性提供了内存内部的访问顺序,在不同的程序针对不同的内存块进行访问的时候,其访问不是无序的,比如有一个内存块,A和B需要访问的时候,JMM会提供一定的内存分配策略有序地分配它们使用的内存,而在内存的调用过程也会变得有序地进行,内存的折中性质可以简单理解为有序性。而在Java多线程程序里面,JMM通过Java关键字volatile来保证内存的有序访问。 1)简单分析:
Java语言规范中提到过,JVM中存在一个主存区(Main Memory或Java Heap Memory),Java中所有变量都是存在主存中的,对于所有线程进行共享,而每个线程又存在自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作并非发生在主存区,而是发生在工作内存中,而线程之间是不能直接相互访问,变量在程序中的传递,是依赖主存来完成的。而在多核处理器下,大部分数据存储在高速缓存中,如果高速缓存不经过内存的时候,也是不可见的一种表现。在Java程序中,内存本身是比较昂贵的资源,其实不仅仅针对Java应用程序,对操作系统本身而言内存也属于昂贵资源,Java程序在性能开销过程中有几个比较典型的可控制的来源。synchronized和volatile关键字提供的内存中模型的可见性保证程序使用一个特殊的、存储关卡(memory barrier)的指令,来刷新缓存,使缓存无效,刷新硬件的写缓存并且延迟执行的传递过程,无疑该机制会对Java程序的性能产生一定的影响。
JMM的最初目的,就是为了能够支持多线程程序设计的,每个线程可以认为是和其他线程不同的CPU上运行,或者对于多处理器的机器而言,该模型需要实现的就是使得每一个线程就像运行在不同的机器、不同的CPU或者本身就不同的线程上一样,这种情况实际上在项目开发中是常见的。对于CPU本身而言,不能直接访问其他CPU的寄存器,模型必须通过某种定义规则来使得线程和线程在工作内存中进行相互调用而实现CPU本身对其他CPU、或者说线程对其他线程的内存中资源的访问,而表现这种规则的运行环境一般为运行该程序的运行宿主环境(操作系统、服务器、分布式系统等),而程序本身表现就依赖于编写该程序的语言特性,这里也就是说用Java编写的应用程序在内存管理中的实现就是遵循其部分原则,也就是前边提及到的JMM定义了Java语言针对内存的一些的相关规则。然而,虽然设计之初是为了能够更好支持多线程,但是该模型的应用和实现当然不局限于多处理器,而在JVM编译器编译Java编写的程序的时候以及运行期执行该程序的时候,对于单CPU的系统而言,这种规则也是有效的,这就是是上边提到的线程和线程之间的内存策略。JMM本身在描述过程没有提过具体的内存地址以及在实现该策略中的实现方法是由JVM的哪一个环节(编译器、处理器、缓存控制器、其他)提供的机制来实现的,甚至针对一个开发非常熟悉的程序员,也不一定能够了解它内部对于类、对象、方法以及相关内容的一些具体可见的物理结构。相反,JMM定义了一个线程与主存之间的抽象关系,其实从上边的图可以知道,每一个线程可以抽象成为一个工作内存(抽象的高速缓存和寄存器),其中存储了Java的一些值,该模型保证了Java里面的属性、方法、字段存在一定的数学特性,按照该特性,该模型存储了对应的一些内容,并且针对这些内容进行了一定的序列化以及存储排序操作,这样使得Java对象在工作内存里面被JVM顺利调用,(当然这是比较抽象的一种解释)既然如此,大多数JMM的规则在实现的时候,必须使得主存和工作内存之间的通信能够得以保证,而且不能违反内存模型本身的结构,这是语言在设计之处必须考虑到的针对内存的一种设计方法。这里需要知道的一点是,这一切的操作在Java语言里面都是依靠Java语言自身来操作的,因为Java针对开发人员而言,内存的管理在不需要手动操作的情况下本身存在内存的管理策略,这也是Java自己进行内存管理的一种优势。
[1]原子性(Atomicity):
这一点说明了该模型定义的规则针对原子级别的内容存在独立的影响,对于模型设计最初,这些规则需要说明的仅仅是最简单的读取和存储单元写入的的一些操作,这种原子级别的包括——实例、静态变量、数组元素,只是在该规则中不包括方法中的局部变量。
[2]可见性(Visibility):
在该规则的约束下,定义了一个线程在哪种情况下可以访问另外一个线程或者影响另外一个线程,从JVM的操作上讲包括了从另外一个线程的可见区域读取相关数据以及将数据写入到另外一个线程内。
[3]可排序性(Ordering):
该规则将会约束任何一个违背了规则调用的线程在操作过程中的一些顺序,排序问题主要围绕了读取、写入和赋值语句有关的序列。
如果在该模型内部使用了一致的同步性的时候,这些属性中的每一个属性都遵循比较简单的原则:和所有同步的内存块一样,每个同步块之内的任何变化都具备了原子性以及可见性,和其他同步方法以及同步块遵循同样一致的原则,而且在这样的一个模型内,每个同步块不能使用同一个锁,在整个程序的调用过程是按照编写的程序指定指令运行的。即使某一个同步块内的处理可能会失效,但是该问题不会影响到其他线程的同步问题,也不会引起连环失效。简单讲:当程序运行的时候使用了一致的同步性的时候,每个同步块有一个独立的空间以及独立的同步控制器和锁机制,然后对外按照JVM的执行指令进行数据的读写操作。这种情况使得使用内存的过程变得非常严谨!
如果不使用同步或者说使用同步不一致(这里可以理解为异步,但不一定是异步操作),该程序执行的答案就会变得极其复杂。而且在这样的情况下,该内存模型处理的结果比起大多数程序员所期望的结果而言就变得十分脆弱,甚至比起JVM提供的实现都脆弱很多。因为这样所以出现了Java针对该内存操作的最简单的语言规范来进行一定的习惯限制,排除该情况发生的做法在于:
JVM线程必须依靠自身来维持对象的可见性以及对象自身应该提供相对应的操作而实现整个内存操作的三个特性,而不是仅仅依靠特定的修改对象状态的线程来完成如此复杂的一个流程。
[4]三个特性的解析(针对JMM内部):
原子性(Atomicity):
访问存储单元内的任何类型的字段的值以及对其更新操作的时候,除开long类型和double类型,其他类型的字段是必须要保证其原子性的,这些字段也包括为对象服务的引用。此外,该原子性规则扩展可以延伸到基于long和double的另外两种类型:volatile long和volatile double(volatile为java关键字),没有被volatile声明的long类型以及double类型的字段值虽然不保证其JMM中的原子性,但是是被允许的。针对non-long/non-double的字段在表达式中使用的时候,JMM的原子性有这样一种规则:如果你获得或者初始化该值或某一些值的时候,这些值是由其他线程写入,而且不是从两个或者多个线程产生的数据在同一时间戳混合写入的时候,该字段的原子性在JVM内部是必须得到保证的。也就是说JMM在定义JVM原子性的时候,只要在该规则不违反的条件下,JVM本身不去理睬该数据的值是来自于什么线程,因为这样使得Java语言在并行运算的设计的过程中针对多线程的原子性设计变得极其简单,而且即使开发人员没有考虑到最终的程序也没有太大的影响。再次解释一下:这里的原子性指的是原子级别的操作,比如最小的一块内存的读写操作,可以理解为Java语言最终编译过后最接近内存的最底层的操作单元,这种读写操作的数据单元不是变量的值,而是本机码,也就是前边在讲《Java基础知识》中提到的由运行器解释的时候生成的Native Code。
可见性(Visibility):
当一个线程需要修改另外线程的可见单元的时候必须遵循以下原则: 一个写入线程释放的同步锁和紧随其后进行读取的读线程的同步锁是同一个从本质上讲,释放锁操作强迫它的隶属线程【释放锁的线程】从工作内存中的写入缓存里面刷新(专业上讲这里不应该是刷新,可以理解为提供)数据(flush操作),然后获取锁操作使得另外一个线程【获得锁的线程】直接读取前一个线程可访问域(也就是可见区域)的字段的值。因为该锁内部提供了一个同步方法或者同步块,该同步内容具有线程排他性,这样就使得上边两个操作只能针对单一线程在同步内容内部进行操作,这样就使得所有操作该内容的单一线程具有该同步内容(加锁的同步方法或者同步块)内的线程排他性,这种情况的交替也可以理解为具有“短暂记忆效应”。这里需要理解的是同步的双重含义:使用锁机制允许基于高层同步协议进行处理操作,这是最基本的同步;同时系统内存(很多时候这里是指基于机器指令的底层存储关卡memory barrier,前边提到过)在处理同步的时候能够跨线程操作,使得线程和线程之间的数据是同步的。这样的机制也折射出一点,并行编程相对于顺序编程而言,更加类似于分布式编程。后一种同步可以作为JMM机制中的方法在一个线程中运行的效果展示,注意这里不是多个线程运行的效果展示,因为它反应了该线程愿意发送或者接受的双重操作,并且使得它自己的可见区域可以提供给其他线程运行或者更新,从这个角度来看,使用锁和消息传递可以视为相互之间的变量同步,因为相对其他线程而言,它的操作针对其他线程也是对等的。 一旦某个字段被申明为volatile,在任何一个写入线程在工作内存中刷新缓存的之前需要进行进一步的内存操作,也就是说针对这样的字段进行立即刷新,可以理解为这种volatile不会出现一般变量的缓存操作,而读取线程每次必须根据前一个线程的可见域里面重新读取该变量的值,而不是直接读取。 当某个线程第一次去访问某个对象的域的时候,它要么初始化该对象的值,要么从其他写入线程可见域里面去读取该对象的值;这里结合上边理解,在满足某种条件下,该线程对某对象域的值的读取是直接读取,有些时候却需要重新读取。这里需要小心一点的是,在并发编程里面,不好的一个实践就是使用一个合法引用去引用不完全构造的对象,这种情况在从其他写入线程可见域里面进行数据读取的时候发生频率比较高。从编程角度上讲,在构造函数里面开启一个新的线程是有一定的风险的,特别是该类是属于一个可子类化的类的时候。Thread.start由调用线程启动,然后由获得该启动的线程释放锁具有相同的“短暂记忆效应”,如果一个实现了Runnable接口的超类在子类构造子执行之前调用了Thread(this).start()方法,那么就可能使得该对象在线程方法run执行之前并没有被完全初始化,这样就使得一个指向该对象的合法引用去引用了不完全构造的一个对象。同样的,如果创建一个新的线程T并且启动该线程,然后再使用线程T来创建对象X,这种情况就不能保证X对象里面所有的属性针对线程T都是可见的除非是在所有针对X对象的引用中进行同步处理,或者最好的方法是在T线程启动之前创建对象X。 若一个线程终止,所有的变量值都必须从工作内存中刷到主存,比如,如果一个同步线程因为另一个使用Thread.join方法的线程而终止,那么该线程的可见域针对那个线程而言其发生的改变以及产生的一些影响是需要保证可知道的。 注意:如果在同一个线程里面通过方法调用去传一个对象的引用是绝对不会出现上边提及到的可见性问题的。JMM保证所有上边的规定以及关于内存可见性特性的描述——一个特殊的更新、一个特定字段的修改都是某个线程针对其他线程的一个“可见性”的概念,最终它发生的场所在内存模型中Java线程和线程之间,至于这个发生时间可以是一个任意长的时间,但是最终会发生,也就是说,Java内存模型中的可见性的特性主要是针对线程和线程之间使用内存的一种规则和约定,该约定由JMM定义。
不仅仅如此,该模型还允许不同步的情况下可见性特性。比如针对一个线程提供一个对象或者字段访问域的原始值进行操作,而针对另外一个线程提供一个对象或者字段刷新过后的值进行操作。同样也有可能针对一个线程读取一个原始的值以及引用对象的对象内容,针对另外一个线程读取一个刷新过后的值或者刷新过后的引用。
尽管如此,上边的可见性特性分析的一些特征在跨线程操作的时候是有可能失败的,而且不能够避免这些故障发生。这是一个不争的事实,使用同步多线程的代码并不能绝对保证线程安全的行为,只是允许某种规则对其操作进行一定的限制,但是在最新的JVM实现以及最新的Java平台中,即使是多个处理器,通过一些工具进行可见性的测试发现其实是很少发生故障的。跨线程共享CPU的共享缓存的使用,其缺陷就在于影响了编译器的优化操作,这也体现了强有力的缓存一致性使得硬件的价值有所提升,因为它们之间的关系在线程与线程之间的复杂度变得更高。这种方式使得可见度的自由测试显得更加不切实际,因为这些错误的发生极为罕见,或者说在平台上我们开发过程中根本碰不到。在并行程开发中,不使用同步导致失败的原因也不仅仅是对可见度的不良把握导致的,导致其程序失败的原因是多方面的,包括缓存一致性、内存一致性问题等。
可排序性(Ordering):
可排序规则在线程与线程之间主要有下边两点: 从操作线程的角度看来,如果所有的指令执行都是按照普通顺序进行,那么对于一个顺序运行的程序而言,可排序性也是顺序的 从其他操作线程的角度看来,排序性如同在这个线程中运行在异步方法中的一个“间谍”,所以任何事情都有可能发生。唯一有用的限制是同步方法和同步块的相对排序,就像操作volatile字段一样,总是保留下来使用 【*:如何理解这里“间谍”的意思,可以这样理解,排序规则在本线程里面遵循了第一条法则,但是对其他线程而言,某个线程自身的排序特性可能使得它不定地访问执行线程的可见域,而使得该线程对本身在执行的线程产生一定的影响。举个例子,A线程需要做三件事情分别是A1、A2、A3,而B是另外一个线程具有操作B1、B2,如果把参考定位到B线程,那么对A线程而言,B的操作B1、B2有可能随时会访问到A的可见区域,比如A有一个可见区域a,A1就是把a修改称为1,但是B线程在A线程调用了A1过后,却访问了a并且使用B1或者B2操作使得a发生了改变,变成了2,那么当A按照排序性进行A2操作读取到a的值的时候,读取到的是2而不是1,这样就使得程序最初设计的时候A线程的初衷发生了改变,就是排序被打乱了,那么B线程对A线程而言,其身份就是“间谍”,而且需要注意到一点,B线程的这些操作不会和A之间存在等待关系,那么B线程的这些操作就是异步操作,所以针对执行线程A而言,B的身份就是“异步方法中的‘间谍’。】
同样的,这仅仅是一个最低限度的保障性质,在任何给定的程序或者平台,开发中有可能发现更加严格的排序,但是开发人员在设计程序的时候不能依赖这种排序,如果依赖它们会发现测试难度会成指数级递增,而且在复合规定的时候会因为不同的特性使得JVM的实现因为不符合设计初衷而失败。
注意:第一点在JLS(Java Language Specification)的所有讨论中也是被采用的,例如算数表达式一般情况都是从上到下、从左到右的顺序,但是这一点需要理解的是,从其他操作线程的角度看来这一点又具有不确定性,对线程内部而言,其内存模型本身是存在排序性的。【*:这里讨论的排序是最底层的内存里面执行的时候的NativeCode的排序,不是说按照顺序执行的Java代码具有的有序性质,本文主要分析的是JVM的内存模型,所以希望读者明白这里指代的讨论单元是内存区。】 JMM最初设计的时候存在一定的缺陷,这种缺陷虽然现有的JVM平台已经修复,但是这里不得不提及,也是为了读者更加了解JMM的设计思路,这一个小节的概念可能会牵涉到很多更加深入的知识,如果读者不能读懂没有关系先看了文章后边的章节再返回来看也可以。
1)问题1:不可变对象不是不可变的
学过Java的朋友都应该知道Java中的不可变对象,这一点在本文最后讲解String类的时候也会提及,而JMM最初设计的时候,这个问题一直都存在,就是:不可变对象似乎可以改变它们的值(这种对象的不可变指通过使用final关键字来得到保证),(Publis Service Reminder:让一个对象的所有字段都为final并不一定使得这个对象不可变——所有类型还必须是原始类型而不能是对象的引用。而不可变对象被认为不要求同步的。但是,因为在将内存写方面的更改从一个线程传播到另外一个线程的时候存在潜在的延迟,这样就使得有可能存在一种竞态条件,即允许一个线程首先看到不可变对象的一个值,一段时间之后看到的是一个不同的值。这种情况以前怎么发生的呢?在JDK 1.4中的String实现里,这儿基本有三个重要的决定性字段:对字符数组的引用、长度和描述字符串的开始数组的偏移量。String就是以这样的方式在JDK 1.4中实现的,而不是只有字符数组,因此字符数组可以在多个String和StringBuffer对象之间共享,而不需要在每次创建一个String的时候都拷贝到一个新的字符数组里。假设有下边的代码:
String s1 = /usr/tmp;
String s2 = s1.substring(4); // /tmp
这种情况下,字符串s2将具有大小为4的长度和偏移量,但是它将和s1共享“/usr/tmp”里面的同一字符数组,在String构造函数运行之前,Object的构造函数将用它们默认的值初始化所有的字段,包括决定性的长度和偏移字段。当String构造函数运行的时候,字符串长度和偏移量被设置成所需要的值。但是在旧的内存模型中,因为缺乏同步,有可能另一个线程会临时地看到偏移量字段具有初始默认值0,而后又看到正确的值4,结果是s2的值从“/usr”变成了“/tmp”,这并不是我们真正的初衷,这个问题就是原始JMM的第一个缺陷所在,因为在原始JMM模型里面这是合理而且合法的,JDK 1.4以下的版本都允许这样做。
2)问题2:重新排序的易失性和非易失性存储
另一个主要领域是与volatile字段的内存操作重新排序有关,这个领域中现有的JMM引起了一些比较混乱的结果。现有的JMM表明易失性的读和写是直接和主存打交道的,这样避免了把值存储到寄存器或者绕过处理器特定的缓存,这使得多个线程一般能看见一个给定变量最新的值。可是,结果是这种volatile定义并没有最初想象中那样如愿以偿,并且导致了volatile的重大混乱。为了在缺乏同步的情况下提供较好的性能,编译器、运行时和缓存通常是允许进行内存的重新排序操作的,只要当前执行的线程分辨不出它们的区别。(这就是within-thread as-if-serial semantics[线程内似乎是串行]的解释)但是,易失性的读和写是完全跨线程安排的,编译器或缓存不能在彼此之间重新排序易失性的读和写。遗憾的是,通过参考普通变量的读写,JMM允许易失性的读和写被重排序,这样以为着开发人员不能使用易失性标志作为操作已经完成的标志。比如:
Map configOptions;
char[] configText;
volatile boolean initialized = false;
// 线程1
configOptions = new HashMap();
configText = readConfigFile(filename);
processConfigOptions(configText,configOptions);
initialized = true;
// 线程2
while(!initialized)
sleep();
这里的思想是使用易失性变量initialized担任守卫来表明一套别的操作已经完成了,这是一个很好的思想,但是不能在JMM下工作,因为旧的JMM允许非易失性的写(比如写到configOptions字段,以及写到由configOptions引用Map的字段中)与易失性的写一起重新排序,因此另外一个线程可能会看到initialized为true,但是对于configOptions字段或它所引用的对象还没有一个一致的或者说当前的针对内存的视图变量,volatile的旧语义只承诺在读和写的变量的可见性,而不承诺其他变量,虽然这种方法更加有效的实现,但是结果会和我们设计之初大相径庭。

⑷ intel fortran如何实现单机多核并行运算

请使用openmp。

打开OpenMP支持,方法如下:

选择项目(Project) -> 属性(property) -> Fortran -> 语言(Language),在 Process OpenMP Directives 选项中选择 Generate Parallel Code (/Qopenmp),点击确定以打开 OpenMP 支持。

示例代码:

⑸ 编写C++语言程序是用英特尔 C++ 编译器好还是微软的VC++2008

都可以 关键看你要用什么 Intel C++的编译器效率比较高 对多核多线程的优化也比较好

⑹ 如何使用android的ndk编译器 编译c++的库

1. 概述 首先回顾一下 Android NDK 开发中,Android.mk 和 Application.mk 各自的职责。 Android.mk,负责配置如下内容: (1) 模块名(LOCAL_MODULE) (2) 需要编译的源文件(LOCAL_SRC_FILES) (3) 依赖的第三方库(LOCAL_STATIC_LIBRARIES,LOCAL_SHARED_LIBRARIES) (4) 编译/链接选项(LOCAL_LDLIBS、LOCAL_CFLAGS) Application.mk,负责配置如下内容: (1) 目标平台的ABI类型(默认值:armeabi)(APP_ABI) (2) Toolchains(默认值:GCC 4.8) (3) C++标准库类型(默认值:system)(APP_STL) (4) release/debug模式(默认值:release) 由此我们可以看到,本文所涉及的编译选项在Android.mk和Application.mk中均有出现,下面我们将一个个详细介绍。 2. APP_ABI ABI全称是:Application binary interface,即:应用程序二进制接口,它定义了一套规则,允许编译好的二进制目标代码在所有兼容该ABI的操作系统和硬件平台中无需改动就能运行。(具体的定义请参考 网络 或者 维基网络 ) 由上述定义可以判断,ABI定义了规则,而具体的实现则是由编译器、CPU、操作系统共同来完成的。不同的CPU芯片(如:ARM、Intel x86、MIPS)支持不同的ABI架构,常见的ABI类型包括:armeabi,armeabi-v7a,x86,x86_64,mips,mips64,arm64-v8a等。 这就是为什么我们编译出来的可以运行于Windows的二进制程序不能运行于Mac OS/Linux/Android平台了,因为CPU芯片和操作系统均不相同,支持的ABI类型也不一样,因此无法识别对方的二进制程序。 而我们所说的“交叉编译”的核心原理也跟这些密切相关,交叉编译,就是使用交叉编译工具,在一个平台上编译生成另一个平台上的二进制可执行程序,为什么可以做到?因为交叉编译工具实现了另一个平台所定义的ABI规则。我们在Windows/Linux平台使用Android NDK交叉编译工具来编译出Android平台的库也是这个道理。 这里给出最新 Android NDK 所支持的ABI类型及区别: 那么,如何指定ABI类型呢?在 Application.mk 文件中添加一行即可: APP_ABI := armeabi-v7a //只编译armeabi-v7a版本 APP_ABI := armeabi armeabi-v7a //同时编译armeabi,armeabi-v7a版本 APP_ABI := all //编译所有版本 3. LOCAL_LDLIBS Android NDK 除了提供了Bionic libc库,还提供了一些其他的库,可以在 Android.mk 文件中通过如下方式添加依赖: LOCAL_LDLIBS := -lfoo 其中,如下几个库在 Android NDK 编译时就默认链接了,不需要额外添加在 LOCAL_LDLIBS 中: (1) Bionic libc库 (2) pthread库(-lpthread) (3) math(-lmath) (4) C++ support library (-lstdc++) 下面我列了一个表,给出了可以添加到“LOCAL_LDLIBS”中的不同版本的Android NDK所支持的库: 下面是我总结的一些常用的CFLAGS编译选项: (1)通用的编译选项 -O2 编译优化选项,一般选择O2,兼顾了优化程度与目标大小 -Wall 打开所有编译过程中的Warning -fPIC 编译位置无关的代码,一般用于编译动态库 -shared 编译动态库 -fopenmp 打开多核并行计算, -Idir 配置头文件搜索路径,如果有多个-I选项,则路径的搜索先后顺序是从左到右的,即在前面的路径会被选搜索 -nostdinc 该选项指示不要标准路径下的搜索头文件,而只搜索-I选项指定的路径和当前路径。 --sysroot=dir 用dir作为头文件和库文件的逻辑根目录,例如,正常情况下,如果编译器在/usr/include搜索头文件,在/usr/lib下搜索库文件,它将用dir/usr/include和dir/usr/lib替代原来的相应路径。 -llibrary 查找名为library的库进行链接 -Ldir 增加-l选项指定的库文件的搜索路径,即编译器会到dir路径下搜索-l指定的库文件。 -nostdlib 该选项指示链接的时候不要使用标准路径下的库文件 (2) ARM平台相关的编译选项 -marm -mthumb 二选一,指定编译thumb指令集还是arm指令集 -march=name 指定特定的ARM架构,常用的包括:-march=armv6, -march=armv7-a -mfpu=name 给出目标平台的浮点运算处理器类型,常用的包括:-mfpu=neon,-mfpu=vfpv3-d16 -mfloat-abi=name 给出目标平台的浮点预算ABI,支持的参数包括:“soft”, “softfp” and “hard”

⑺ fatal error C1076: compiler limit : internal heap limit reached; use /Zm to specify a higher limit

意思是:
编译器限制:内部堆达到了最大的限制。使用/zm来指定一个更大的限制值。

我觉得有可能是你分配了太多的内存,或者是指定了太小的堆。建议你拿到别人默认设置的vc++上面编译一下试试看,然后才能确定到底是代码的问题还是编译环境设置的问题

⑻ 为什么在多核多线程程序中要慎用volatile关键字

a. 避免用通用寄存器对内存读写的优化。编译器常做的一种优化就是:把常用变量的频繁读写弄到通用寄存器中,最后不用的时候再存回内存中。但是如果某个内存地址中的值是由片外决定的(例如另一个线程或是另一个设备可能更改它),那就需要volatile关键字了。(感谢Kenny老师指正)
b.硬件寄存器可能被其他设备改变的情况。例如一个嵌入式板子上的某个寄存器直接与一个测试仪器连在一起,这样在这个寄存器的值随时可能被那个测试仪器更改。在这种情况下如果把该值设为volatile属性的,那么编译器就会每次都直接从内存中去取这个值的最新值,而不是自作聪明的把这个值保留在缓存中而导致读不到最新的那个被其他设备写入的新值。
c. 同一个物理内存地址M有两个不同的内存地址的情况。例如两个程序同时对同一个物理地址进行读写,那么编译器就不能假设这个地址只会有一个程序访问而做缓存优化,所以程序员在这种情况下也需要把它定义为volatile的。

⑼ intel visual fortran compiler干嘛用的

Visual Numerics Inc已于2004年初完成与Intel的结盟,将当时的IMSL Fortran Library的新版本v5.0整合在Intel Visual Fortran系列产品之专业版(承袭购并之Compaq Visual Fortran功能架构)中。2005.6月份发布其最新版本Intel Visual Fortran Compiler v9.0.018

针对Intel® CPU Pentium® 4, Xeon�6�4, Centrino�6�4 Itanium® 做最佳的福传编译,支持OpenMP及Auto-Parallelism特色. “英特尔R Visual Fortran 编译器Windows* 版”将Compaq Visual Fortran* (CVF) 语言的丰富功能与英特尔代码生成及优化技术结合在一起,构成面向英特尔体系结构的下一代Fortran 编译器。它提供250 多条CVF 与英特尔Fortran 命令及同义词,使编译器更加易于使用,同时仍能作为插件融入Microsoft Visual Studio 环境,并提供直接来自英特尔的技术支持。此编译器是一套功能齐备的Fortran 95 编译器,具有先进的优化功能,可以使Fortran 应用程序在英特尔R IA-32 与安腾2 处理器上快速运行。英特尔® 视觉FORTRAN 编译器为窗口* 标记一个里程碑为FORTRAN 开发商。它带来Compaq 视觉FORTRAN * 语言特色与英特尔编译器代码世代和优化技术一起。

性能、兼容性、技术支持:采用“英特尔 Visual Fortran 编译器 9.0 Windows* 版”,可以提高应用程序速度,并保护您在开发工具上所作的投资。先进的优化功能可以给最新英特尔® 处理器上运行的应用程序带来出众的性能。本产品包含标准版与专业版。“英特尔® Visual Fortran 编译器专业版”包含 Visual Numerics, Inc. 的“IMSL* Fortran 函数库6.0”。
目前版本号最高的Intel Fortran编译器,需要Visual Studio 6.0以上支持,可以生成更适用于Intel芯片的高效程序,是Intel平台下最优秀的Fortran语言编译器。大家可以考虑丢掉Fortran 77编译系统了:P
Fortran,是由Formula Translation两个字所组合而成,意思是公式翻译。它是世界上第一个被正式采用并流传至今的高级编程语言。

性能、兼容性、技术支持
使用“英特尔® Visual Fortran 编译器 Windows* 版”,可以让您的应用程序在英特尔® 处理器上取得前所未有的绝佳性能。

此编译器有标准版与专业版这两个版本提供。“英特尔® Visual Fortran 编译器专业版”包含 Visual Numerics, Inc. 的“IMSL* Fortran 函数库 5.0”。

客户评价:

“在英特尔编译器的帮助下,我们开发了大型海洋模型,充分利用了在基于英特尔的系统上所作的投资。在使用‘英特尔 Visual Fortran 编译器 Windows 版’编译代码时,我发现,与以前的版本相比,性能提高了大约 20%。”
- Xianyao Chen 博士
海洋模型小组组长
中国第一海洋研究所

“‘英特尔 Fortran 编译器 7.0’堪称一流,‘英特尔 Visual Fortran 8.0’做得还要好。英特尔将 Compaq Visual Fortran 与‘英特尔 Fortran’中最优秀的功能结合在一起,实现了一次巨大的飞跃。”现在为9.0最新版!

sual Fortran 与‘英特尔 Fortran’中最优秀的功能结合在一起,实现了一次巨大的飞跃。”现在为9.0最新版!

⑽ Java优势有哪些

JAVA的优势是什么?

Java语言是目前的排行第一的语言,其优势也是非常突出的,那么具体体现在哪里呐?

我来给你说一下Java的优势:

1.java的风格类似C++但不同于C++,从某种意义上讲,java是C++的一个变种;

2.java摒弃了C、C++中的容易引发错误和难以理解的指针,结构,以及内存管理等;

3.java提供了丰富的类库,很方便开发程序;

4.java是完全面向对象的语言,支持 继承,重载,多态等面向对象的特性;

5.C++是面向对象和面向过程的混合语言, C是纯面向过程的语言;

6.java是一种强类型的语言,比C/C++检查还要严格,java区分大小写字母;

7.java提供了自动垃圾回收机制gc,有效避免了C、C++的内存泄漏问题;

8.java禁止非法访问内存,因为java没有指针,内存的申请和释放;

9.跨平台,java的源代码编译成结构中立的中间格式,这种格式与机器无关,只要在安装有JVM(java虚拟机)的电脑上,都能运行这种与机器无关的中间文件;java一次编译,到处运行;

10.对于不同的操作系统,会有不同的java虚拟机,这也就决定了java的可移植性;

11.java支持多线程,简单理解,如果是单核CPU,那么会通过时间片轮转的方式,多线程执行程序,如果是多核CPU,那么就可以理解为,两个或多个线程同时运行。


一、做网站

Java可以用来做网站,很多大型网站都是用Java写的,比如我们熟悉的B站,所以想学习Java的同学还可以负责网站方面的制作,这方面的岗位(网站开发)也比较多,一直以来都相当流行。

二、做安卓软件

安卓是基于Linux的操作系统,其中源代码就是Java,市面上所有的安卓手机都是修改Java运行的,对于更多的开发人员来说,他们更多的时间是花在开发APP上面。你随便打开一个App应用,他们就是用Java语言做的。

三、做游戏

电脑上的大多数游戏也是用Java来开发的,最经典的就是《我的世界》,还有当今世界最具影响力的游戏英雄联盟,吃鸡也是用Java写的

四、写软件

很多编程语言都是可以来写软件的,但Java是现在应用最广泛的,比如:企业级应用开发,还有OA、邮箱、物流、医疗、投票、金融、考试、矿山等信息方面的系统,Java都占有极为重要的地位。现在国内的最热门的就是手机应用,学习Java去做手机应用还是比较吃香的。

说啦这么多那么应该如何学习Java那?

在这个里我使用脑图给大家出一个,Java的学习线路供大家参考:

Java在市场中的占有率是非常之高的,希望大都成为优秀的程序员!!!

热点内容
安卓在哪里找游戏 发布:2025-07-04 22:15:25 浏览:240
路由器访问光猫 发布:2025-07-04 22:07:47 浏览:897
数据库显示语句 发布:2025-07-04 22:04:30 浏览:740
编程课道具 发布:2025-07-04 22:04:02 浏览:844
华为手机不是安卓什么时候可以更新米加小镇 发布:2025-07-04 22:01:37 浏览:785
饥荒服务器搭建视频 发布:2025-07-04 21:48:38 浏览:523
github上传文件夹 发布:2025-07-04 21:29:22 浏览:1003
php课程学习中心 发布:2025-07-04 21:29:16 浏览:298
win7加密文件夹如何解密 发布:2025-07-04 21:25:24 浏览:555
为啥系统缓存的垃圾多呢 发布:2025-07-04 21:15:45 浏览:952