快捷搜索:  汽车  科技

offsetregistry存的什么(register与volatile的区别及应用场景)

offsetregistry存的什么(register与volatile的区别及应用场景)解决问题:注意:C 编译器有自己的优化方式,不使用register也可能做优化。所以register关键字在高版本编译器中已经不用显示声明。比如,register关键字在c 11中被弃用,c 17中被删除,所以如果把代码适配到C 高版本的话,需要修改makefile 在CMAKE_CXX_FLAGS 中增加 -Wno-error=register所以,如果我们进行一段频繁的运算,则存储变量肯定要花费不少时间。所以C/c 语言允许将局部变量的值存放在寄存器中,这样需要时就直接搬用,不必再经过内存,从而提高运算速度。应用场景:某个算法函数内部对某个数据进行大量频繁的计算,但是又追求算法处理性能。

引言

在嵌入式c开发或多线程开发的程序中,因为追求数据处理速度、追求共享数据实时性等,会对代码进行深度优化,甚至会考虑到CPU的寄存器层面。那么,与寄存器相关的特殊类型都有哪些呢?分别应用在哪些场景呢?

我们在前半部分介绍常见的register和volatile区别和使用场景,然后在文章后半部分进行cpu和寄存器的简单介绍。

register

解决问题:

一般变量的值都是存储在内存中(当程序需要用到哪一个变量的值,由控制器发出指令将内存中该变量的值送到运算器,完了如果需要存储,再从运算器将数据送到内存中存放)。

所以,如果我们进行一段频繁的运算,则存储变量肯定要花费不少时间。所以C/c 语言允许将局部变量的值存放在寄存器中,这样需要时就直接搬用,不必再经过内存,从而提高运算速度。

应用场景:

某个算法函数内部对某个数据进行大量频繁的计算,但是又追求算法处理性能。

注意:C 编译器有自己的优化方式,不使用register也可能做优化。所以register关键字在高版本编译器中已经不用显示声明。比如,register关键字在c 11中被弃用,c 17中被删除,所以如果把代码适配到C 高版本的话,需要修改makefile 在CMAKE_CXX_FLAGS 中增加 -Wno-error=register

volatile

解决问题:

volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,不再把变量从内存装入CPU寄存器中,从而可以提供对特殊地址的稳定访问。

volatile的意思是让编译器每次操作该变量时,一定要直接从内存中真正取出,而不是使用已经存在寄存器中的备份。

应用场景:

一般说来,volatile用在如下的几个地方:

1) 中断服务程序中修改的供其它程序检测的变量需要加volatile;

2) 多任务环境下各任务间共享的标志变量前,应该添加上volatile进行修饰;

3) 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;

示例代码和运行效果

#include <windows.h>

#include <thread>

using namespace std;

volatile bool flag = false;//唤醒状态

int result = 0; //注意:此变量不应该使用volatile进行修饰

void wake_up(const int num)

{

flag = true;

cout << "线程 " << num << " [id= " << this_thread::get_id() << "] wake_up" << endl;

}

//线程要做的事情

void thread_run(const int num int n)

{

while(1)

{

if (false == flag)

{

cout << "线程 " << num << " [id= " << this_thread::get_id() << "] wait..." << endl;

Sleep(1000);

}

else

{

for (int i = 0; i < n; i )

{

result ;

cout << "线程 " << num << " [id= " << this_thread::get_id() << "] result=" << result << endl;

}

return;

}

}

}

int main()

{

thread first(thread_run 1 5); //子线程1

thread second(thread_run 2 5); //子线程2

thread third(thread_run 3 5); //子线程3

thread fouth(wake_up 4); //子线程4

int temp = 0;

while (temp < 10)

{

Sleep(100);//单位是 毫秒

cout << "主线程result=" << result << endl;

temp ;

}

first.join(); //主线程要等待子线程执行完毕

second.join();

third.join();

fouth.join();

return 0;

}

volatile int result = 0; //使用volatile 进行修饰,编译器内部无法使用register对result 进行修饰,所以会出现读出(打印)数据显示异常的情况。

offsetregistry存的什么(register与volatile的区别及应用场景)(1)

int result = 0;//不使用volatile 进行修饰,编译器内部默认使用register对result 进行修饰,打印数据显示正常。

offsetregistry存的什么(register与volatile的区别及应用场景)(2)


(后半部分属于简单的科普内容,更详细的信息大家可以自行到网上搜一下)

计算机主要硬件分布

计算机上有许多硬件: CPU、内存、USB、显卡等 它们由不同的总线(Bus) 相连。

计算最强的 CPU 和存储最快的 Memory 之间由北桥芯片连接。

北桥芯片专门用来处理那些 IO 较快的数据 最典型的例子就是 CPU 与 内存之间的交互。

南桥芯片用于接收那些 IO 没那么快的数据 如键盘、音频、网卡、显卡等硬件输入。

备注:为什么要区分南桥北桥呢? 因为 南桥的 IO 较慢 如果只有一根总线来接受所有数据 那么当低速的 IO 占用总线的时候 势必会阻塞高速的 IO。

offsetregistry存的什么(register与volatile的区别及应用场景)(3)

图片来自网络

CPU的组成

控制器 – 把内存中的指令 数据等读入到寄存器中

寄存器 – 用来暂存指令数据等处理对象

运算器 – 负责运算从内存读入到寄存器的数据

时钟 – 负责发出cpu开始计时的时钟信号

offsetregistry存的什么(register与volatile的区别及应用场景)(4)

图片来自网络

寄存器、缓存、内存的关系

offsetregistry存的什么(register与volatile的区别及应用场景)(5)

图片来自网络

寄存器

寄存器是cpu的组成部分 是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。其实寄存器就是一种常用的时序逻辑电路,但这种时序逻辑电路只包含存储电路。

寄存器是有限存储容量的高速存储部件,它们可用来暂存指令、数据和位址。寄存器分为通用寄存器和特殊寄存器 cpu访问寄存器的速度是最快的 但是寄存器的容量非常小。

缓存

缓存是cpu的一部分 位于cpu中。在没有缓存之前 cpu一直都是在内存中读取数据的 但由于两者速度差异 cpu每次都要等内存的’回信’ 缓存的设计是用来解决cpu与内存速度差异问题。

cpu内一般有3级缓存。假设一个8核的cpu 每个核都有自己独立的L1 Cache和L2 Cache 而L3 Cache是8核共享的。内存的数据会先加载到共享的L3 Cache中 再加载到每个核心独有的L2 Cache 最后进入到最快的L1 Cache。离核心越近 等级越高 速度越快 L1 Cache缓存最小 速度最快

内存

内存又称主存,也称内存储器和主存储器。它用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。它是CPU能直接寻址的存储空间,由半导体器件制成。特点是存取速率快。

它是外存(硬盘、软盘、光盘、U盘等)与CPU进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平。只要计算机开始运行,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成,CPU将结果传送出来。

内存的运行决定计算机整体运行快慢。

总结

处理速度排序,cpu从寄存器读取最快 接下来是缓存 接下来是内存。


原创不易,欢迎点赞、收藏、关注!

猜您喜欢: