作者:小牛呼噜噜 | https://xiaoniuhululu.com
计算机内功、JAVA底层、面试、职业成长相关资料等更多精彩文章在公众号「小牛呼噜噜」
大家好,我是呼噜噜,在计算机的早期,Intel奔腾处理器时代,他们的主板上的单个处理器CPU是相当大的。为了提升计算机的整体运算处理能力,一般是在主板上安装更多这样的处理器,这样就可以轻松突破晶体管限制。
物理CPU,是硬件上的CPU, 也是主板上CPU的插槽个数,是计算机上实际配置的CPU个数
我们在linux下可以通过指令 cat /proc/cpuinfo | grep 'physical id'| sort| uniq |wc -l 来查看我们计算机的物理CPU个数

在windows下,我们可以在cmd命令中输入systeminfo,查看CPU个数

物理CPU通过连接器或插槽与其他主板元件通信,通过系统总线完成与系统的不同处理器之间的通信,但是系统总线的传输速度比起CPU的速度来说,是非常慢的通常导致出现瓶颈,使得无法充分利用每个CPU提供的计算能力。
所以工程师想 能不能让处理器中的核心组件小型化,并将它们封装在单个芯片中,这些就是物理内核,可以看作是计算单元
CPU核数,物理上真实的cpu核,有独立的电路元件以及L1,L2缓存,可以独立地执行指令。 通常每个CPU下的核数都是固定的,如果我们的计算机有两个物理CPU,每个CPU是双核的,那么计算机总共就是四核的。
在linux下,我们可以通过指令cat /proc/cpuinfo | grep "cpu cores" | wc -l来统计CPU核数。

在windows下,可以通过任务管理器查看:

我们分别看下,单核CPU和多核CPU的架构图:


系统总线进行通信,效率比较低。L3 cache和片内总线进行通信,主存和外设通过总线与CPU通信为了弥补 CPU 与内存两者之间的性能差异,就在 CPU 内部引入了 CPU Cache,也称高速缓存。
CPU Cache用的是 SRAM(Static Random-Access Memory)的芯片,也叫静态随机存储器。其只要有电,数据就可以保持存在,而一旦断电,数据就会丢失。
CPU Cache 通常分为大小不等的三级缓存,分别是 L1 Cache、L2 Cache 和 L3 Cache
| 部件 | CPU访问所需时间 | 备注 |
|---|---|---|
| L1 高速缓存 | 2~4 个时钟周期 | 每个 CPU 核心都有一块属于自己的 L1 高速缓存,L1 高速缓存通常分成指令缓存和数据缓存。 |
| L2 高速缓存 | 10~20 个时钟周期 | L2 高速缓存同样是每个 CPU 核心都有的 |
| L3 高速缓存 | 20~60个时钟周期 | L3 高速缓存是多个 CPU 核心共用的 |
我们可以发现越靠近 CPU 核心的缓存其访问速度越快,容量也越来越小。缓存读取数据过程。就像数据库缓存一样,首先在最快的缓存中找数据,如果缓存没有命中(Cache miss) 则往下一级找, 直到三级缓存都找不到时,最后会去内存找数据。
在历史上,为了提升CPU的性能,引入超标量、乱序运行、大量的寄存器及寄存器重命名、多指令解码器、预测运行、高速缓存等特性,这些特性从而让CPU拥有大量资源,并且CPU还能智能的预测执行指令。这就导致实际上CPU在大多数时间上,其资源是被闲置的,浪费是可耻的,为了进一步压榨CPU的性能,工程师发现,完全可以通过复制一些CPU内部组件,例如寄存器或一级缓存,来运行第二个线程,让这些闲置资源运行在另一个线程上。虽然只有一个物理CPU,但操作系统被提供了两个"逻辑CPU" ,而不是单个CPU,这样就成功使得操作系统被"欺骗"了。
操作系统可以使用逻辑CPU来模拟出真实CPU的效果。在从前没有多核处理器的时候,一个物理CPU只有一个物理内核,而现在有了多核技术,让物理核通过高速运算,让应用程序以为有两个CPU在运算,这样就可以把一个物理CPU当作多个"CPU"使用,即逻辑CPU
一般情况下,逻辑CPU=物理CPU个数*每颗核数,如果我们的电脑CPU支持超线程技术且开启的话,逻辑CPU的个数是核数的2倍,逻辑CPU=物理CPU个数*每颗核数*2,超线程技术可以使得 处理器中的1 颗内核在操作系统中,如同2 颗内核那样发挥作用。
还有一个我们需要知道:逻辑处理器个数 = CPU的线程数,也就是说有多少个逻辑处理器,就可以开多少个线程。对于一个CPU,线程数总是大于或等于核心数的。一个核心最少对应一个线程,但通过超线程技术,一个核心可以对应两个线程,也就是说它可以同时运行两个线程。
一般来说,物理CPU个数×每颗核数应该等于逻辑CPU的个数,如果不相等的话,则表示windows电脑的CPU支持超线程技术。
CPU的线程数概念仅仅只针对Intel的CPU , 对于AMD的CPU来说,只有核心数的概念,没有线程数的概念。因为其是通过Intel超线程技术来实现的,Intel早在2002年推出的Northwood奔腾4 HT处理器就把这一技术带入到消费级市场。
超线程技术(SMT),就是可以把一个物理线程模拟出两个线程来使用,使得单个核心用起来像两个核一样,以充分发挥CPU的性能。
我们需要先了解一下,线程和进程的概念
- 进程:
进程可以看作是程序的一次执行过程。一个程序的运行需要CPU时间、内存空间、文件以及I/O等资源。操作系统就是以进程为单位来分配这些资源的,所以说
进程是操作系统中资源分配的基本单位。进程之间的资源是独立隔离的,能很好的进行资源管理和保护。
进程也是一个动态的过程:有它自身的产生,存在和消亡的过程
- 线程:
线程是进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程可以包含多个线程,至少包含一个线程,与进程不同的是多个线程之间资源数据是共享的。所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。为了提高系统的执行效率,减少处理器的空转时间和调度切换的时间,线程取代了进程调度资源的基本功能,所以
线程是资源调度的基本单位
CPU之所以要增加线程数,是源于多任务处理的需要。线程数越多,越有利于同时运行多个程序,因为线程数等同于在某个瞬间CPU能同时并行处理的任务数。
超线程的原理其实是:由于CPU和寄存器,缓存,主存、硬盘的读取速度的差异不是一个数量级的,CPU非常快
比如主频为3.0GHZ的CPU,一个时钟周期大约是0.3纳秒,内存访问大约需要120纳秒,固态硬盘访问大约需要50-150微秒,机械硬盘访问大约需要1-10毫秒,最后网络访问最慢,得几十毫秒左右。
如果我们把一个时钟周期如果按1秒算的话,内存访问大约就是6分钟 ,固态硬盘大约是2-6天 ,传统硬盘大约是1-12个月,网络访问就得几年了!
这就给了我们超线程技术,将CPU内部暂时闲置处理资源充分“调动”起来,使得CPU中看起来同时有2个逻辑核,在同时工作的可能性。
我们知道CPU是采用指令流水线的方式来执行任务,在一个逻辑核等待指令执行的间隔(等待从cache或内存中获取下一条指令),把时间片分配到另一个逻辑核。物理CPU高速地在这两个逻辑核之间切换,让操作系统感知不到这个间隔,实现了“同时执行多个任务”
像奔腾4 HT处理器多加入了一个逻辑处理单元,这让CPU可以同时执行多个程序而共享一颗CPU内的资源,如:ALU、FPU、 缓存等,当两个线程都同时需要某一个资源时,其中一个要暂时停止,并让出资源,直到这些资源闲置后才能继续,所以单个物理CPU开启超线程的性能并不能等于两颗CPU的处理能力。
超线程技术只增加了5%的芯片面积,就可换来15%~30%的性能提升,而后来的Nehalem架构带来了全新的超线程技术,得益于指令集分制预测技术与较短的流水线,它拥有比奔腾4好得多的效能,再加上整合了内存控制器让其拥有更大的内存带宽,还有更大的缓存,这样就更能够有效的发挥超线程的作用,Nehalem的超线程可以在增加很少能耗的情况下,让性能提升20-30%,后续每一代虽然都有一些小修改,不过基本上都是Nehalem架构的延续。
虽然超线程能让计算机核数增加,但实际上计算机的核数翻倍并不能简单地认为着计算机的性能也翻倍了,计算机的性能还受CPU主频、机器字长、指令字长、存储字长、主存、I/O速度、硬盘速度等因素影响,也不意味着核数越多计算机性能会越来越好,因为超线程只是充分利用了CPU的空闲资源,提升了CPU利用率
我们再举个例子来理解一下逻辑CPU的概念:假设计算机有一个物理CPU,是2核的,支持超线程。那么这台计算机就是2核4线程的(4线程中线程数量也对应着逻辑CPU的数量)。 所以两路(两路指的是有两个物理CPU)四核超线程就有2*4*2=16个逻辑CPU。有人也把它称之为16核,实际上在linux的/proc/cpuinfo中查看只有8核。
processor的数量。我们可以使用指令cat /proc/cpuinfo | grep "processor" | wc -l来查看逻辑CPU数。
呼噜噜的2台电脑比较垃圾,没法把开超线程的图贴给大家看看,大家可以自己去试试

参考:
《深入理解计算机系统》
https://www.expreview.com/56674.html
本篇文章到这里就结束啦,很感谢你能看到最后,如果觉得文章对你有帮助,别忘记关注我!更多精彩的文章
计算机内功、JAVA源码、职业成长、项目实战、面试相关资料等更多精彩文章在公众号「小牛呼噜噜」

我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我正在尝试使用ruby编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?
我是ruby的新手,我认为重新构建一个我用C#编写的简单聊天程序是个好主意。我正在使用Ruby2.0.0MRI(Matz的Ruby实现)。问题是我想在服务器运行时为简单的服务器命令提供I/O。这是从示例中获取的服务器。我添加了使用gets()获取输入的命令方法。我希望此方法在后台作为线程运行,但该线程正在阻塞另一个线程。require'socket'#Getsocketsfromstdlibserver=TCPServer.open(2000)#Sockettolistenonport2000defcommandsx=1whilex==1exitProgram=gets.chomp
我有一个使用PDFKit呈现网页的pdf版本的Rails应用程序。我使用Thin作为开发服务器。问题是当我处于开发模式时。当我使用“bundleexecrailss”启动我的服务器并尝试呈现任何PDF时,整个过程会陷入僵局,因为当您呈现PDF时,会向服务器请求一些额外的资源,如图像和css,看起来只有一个线程.如何配置Rails开发服务器以运行多个工作线程?非常感谢。 最佳答案 我找到的最简单的解决方案是unicorn.geminstallunicorn创建一个unicorn.conf:worker_processes3然后使用它:
所以,Ruby1.9.1现在是declaredstable.Rails应该与它一起工作,并且正在慢慢地将gem移植到它。它具有native线程和全局解释器锁(GIL)。自从GIL到位后,原生线程是否比1.9.1中的绿色线程有任何优势? 最佳答案 1.9中的线程是原生的,但它们被“放慢了速度”,一次只允许一个线程运行。这是因为如果线程真的并行运行,它会混淆现有代码。优点:IO现在在线程中是异步的。如果一个线程阻塞在IO上,那么另一个线程将继续执行直到IO完成。C扩展可以使用真正的线程。缺点:任何非线程安全的C扩展都可能存在使用Thre
我在一个ruby文件中有一个函数可以像这样写入一个文件File.open("myfile",'a'){|f|f.puts("#{sometext}")}这个函数在不同的线程中被调用,使得像上面这样的文件写入不是线程安全的。有谁知道如何以最简单的方式使这个文件写入线程安全?更多信息:如果重要的话,我正在使用rspec框架。 最佳答案 您可以通过File#flock给锁File.open("myfile",'a'){|f|f.flock(File::LOCK_EX)f.puts("#{sometext}")}
我编写了几个类来控制我想如何处理多个网站,两者都使用类似的方法(即登录、刷新)。每个类都打开自己的WATIR浏览器实例。classSite1definitialize@ie=Watir::Browser.newenddeflogin@ie.goto"www.blah.com"endend无线程的main中的代码示例如下require'watir'require_relative'site1'agents=[]agents这工作正常,但在当前代理完成登录之前不会移动到下一个代理。我想合并多线程来处理这个问题,但似乎无法让它工作。require'watir'require_relative
代码:threads=[]Thread.abort_on_exception=truebegin#throwexceptionsinthreadssowecanseethemthreadseputs"EXCEPTION:#{e.inspect}"puts"MESSAGE:#{e.message}"end崩溃:.rvm/gems/ruby-2.1.3@req/gems/activesupport-4.1.5/lib/active_support/dependencies.rb:478:inload_missing_constant':自动加载常量MyClass时检测到循环依赖稍加研究后,
任何人都可以推荐任何详细介绍Ruby多线程/多处理的复杂性的好的多线程/处理书籍/网站吗?我尝试使用ruby线程,基本上在1.9vm上的无死锁代码中它在jruby中遇到了死锁。是的,我意识到差异很大(jruby没有GIL),但我想知道是否有用于ruby中多线程编程的策略或类集,我只需要继续阅读。旁注:从java到ruby必须定义是否需要重新输入锁,这有点奇怪。 最佳答案 如果你使用Ruby1.9,你可以试试Fiber,它是Ruby中线程的一大改进http://ruby-doc.org/core-1.9/classes/F
我想从不同线程调用一个公共(public)枚举器。当我执行以下操作时,enum=(0..1000).to_enumt1=Thread.newdopenum.nextsleep(1)endt2=Thread.newdopenum.nextsleep(1)endt1.joint2.join它引发了一个错误:Fibercalledacrossthreads.当enum在从t1调用一次后从t2调用时。为什么Ruby设计为不允许跨线程调用枚举器(或纤程),以及是否有其他方法可以提供类似的功能?我猜测枚举器/纤程上的操作的原子性在这里是相关的,但我不完全确定。如果这是问题所在,那么在使用时独占锁定