java 优化 寄存器_从volatile看代码优化

从volatile看代码优化

volatile的作用: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值.

简单地说就是防止编译器对代码进行优化.比如如下程序:

XBYTE[2]=0x55;

XBYTE[2]=0x56;

XBYTE[2]=0x57;

XBYTE[2]=0x58;

如果对外部硬件上述四条语句分别表示不同的操作,会产生四种不同的动作,那么编译器就不能像对待纯粹的程序那样对上述四条语句进行优化只认为XBYTE[2]=0x58;而忽略前三条语句(即只产生一条机器代码),此时编译器会逐一的进行编译并产生相应的机器代码(四条).

volatile变量的几个例子

推荐一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3). 多线程应用中被几个任务共享的变量

回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。

假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是真正懂得volatile完全的重要性。

1). 一个参数既可以是const还可以是volatile吗?解释为什么。

2). 一个指针可以是volatile 吗?解释为什么。

3). 下面的函数有什么错误:

int square(volatile int *ptr)

{

return *ptr * *ptr;

}

下面是答案:

1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2). 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。

3). 这段代码是个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr)

{

int a,b;

a = *ptr;

b = *ptr;

return a * b;

}

由于*ptr的值可能被意想不到地改变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)

{

int a;

a = *ptr;

return a * a;

}

关键在于两个地方:

1. 编译器的优化 (请高手帮我看看下面的理解)

在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值;

当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致

当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致

当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致

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

典型的例子

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

这个语句用来测试空循环的速度的

但是编译器肯定要把它优化掉,根本就不执行

如果你写成

for ( volatile int i=0; i<100000; i );

它就会执行了

volatile的本意是“易变的”

由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:

static int i=0;

int main(void)

{

...

while (1)

{

if (i) dosomething();

}

}

/* Interrupt service routine. */

void ISR_2(void)

{

i=1;

}

程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此

可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被

调用。如果将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。

/******************************以上来自百度百科***********************/

考虑这volatile的优化问题,是因为编写的程序不能正常工作,代码截取如下:

点击(此处)折叠或打开

#define GPBCON (*(volatile unsigned long *)0xa0000010)

#define GPBDAT (*(volatile unsigned long *)0xa0000014)

#define GPB5_out (1<

#define GPB6_out (1<

#define GPB7_out (1<

#define GPB8_out (1<

static inline int wait(void)

{

unsigned int dly = 50000;

while(dly--);

}

int main(void)

{

unsigned int i = 32;

GPBCON = GPB5_out|GPB7_out|GPB8_out|GPB6_out;

GPBDAT = 0xffffffff;

while(1)

{

GPBDAT = ~i;

i<<=1;

if(i==512)

i=32;

wait();

}

return 0;

}

只是实现控制流水灯的小程序。但却无法正常运行,原因在于被优化了,看反汇编代码如下:

点击(此处)折叠或打开

b0004000 :

b0004000:    e3a0320a     mov    r3, #-1610612736    ; 0xa0000000

b0004004:    e3a02b55     mov    r2, #87040    ; 0x15400

b0004008:    e3e01000     mvn    r1, #0    ; 0x0

b000400c:    e5832010     str    r2, [r3, #16]

b0004010:    e5831014     str    r1, [r3, #20]

b0004014:    e3a02020     mov    r2, #32    ; 0x20

b0004018:    e1a01003     mov    r1, r3

b000401c:    e1e03002     mvn    r3, r2

b0004020:    e1a02082     lsl    r2, r2, #1

b0004024:    e3520c02     cmp    r2, #512    ; 0x200

b0004028:    e5813014     str    r3, [r1, #20]

b000402c:    03a02020     moveq    r2, #32    ; 0x20

b0004030:    eafffff9     b    b000401c

b0004034:    43434700     movtmi    r4, #14080    ; 0x3700

b0004038:    5328203a     .word    0x5328203a

b000403c:    6372756f     .word    0x6372756f

b0004040:    20797265     .word    0x20797265

b0004044:    202b2b47     .word    0x202b2b47

b0004048:    6574694c     .word    0x6574694c

b000404c:    30303220     .word    0x30303220

b0004050:    2d317139     .word    0x2d317139

b0004054:    29363731     .word    0x29363731

b0004058:    332e3420     .word    0x332e3420

b000405c:    4100332e     .word    0x4100332e

b0004060:    00000029     .word    0x00000029

b0004064:    62616561     .word    0x62616561

b0004068:    1f010069     .word    0x1f010069

b000406c:    05000000     .word    0x05000000

b0004070:    06005434     .word    0x06005434

b0004074:    09010802     .word    0x09010802

b0004078:    14041201     .word    0x14041201

b000407c:    17011501     .word    0x17011501

b0004080:    19011803     .word    0x19011803

b0004084:    1e021a01     .word    0x1e021a01

b0004088:    Address 0xb0004088 is out of bounds.

从中可以看出没有进入过wait函数,因为编译时使用了-O2选项,编译器把这自定义的函数wait给删了。

如何使编译器不删除这自定义函数呢?使用volatile

把函数wait替换成

static inline int wait(void)

{

volatile unsigned int dly = 50000;

while(dly--);

}

这样编译后看看反汇编:

点击(此处)折叠或打开

b0004000 :

b0004000:    e3a0320a     mov    r3, #-1610612736    ; 0xa0000000

b0004004:    e3a02b55     mov    r2, #87040    ; 0x15400

b0004008:    e3e01000     mvn    r1, #0    ; 0x0

b000400c:    e5832010     str    r2, [r3, #16]

b0004010:    e5831014     str    r1, [r3, #20]

b0004014:    e3a01cc3     mov    r1, #49920    ; 0xc300

b0004018:    e24dd008     sub    sp, sp, #8    ; 0x8

b000401c:    e1a00003     mov    r0, r3

b0004020:    e2811050     add    r1, r1, #80    ; 0x50

b0004024:    e3a02020     mov    r2, #32    ; 0x20

b0004028:    e1e03002     mvn    r3, r2

b000402c:    e1a02082     lsl    r2, r2, #1

b0004030:    e5803014     str    r3, [r0, #20]

b0004034:    e3520c02     cmp    r2, #512    ; 0x200

b0004038:    e58d1004     str    r1, [sp, #4]

b000403c:    03a02020     moveq    r2, #32    ; 0x20

b0004040:    e59d3004     ldr    r3, [sp, #4]

b0004044:    e3530000     cmp    r3, #0    ; 0x0

b0004048:    e2433001     sub    r3, r3, #1    ; 0x1

b000404c:    e58d3004     str    r3, [sp, #4]

b0004050:    1afffffa     bne    b0004040

b0004054:    eafffff3     b    b0004028

b0004058:    43434700     movtmi    r4, #14080    ; 0x3700

b000405c:    5328203a     .word    0x5328203a

b0004060:    6372756f     .word    0x6372756f

b0004064:    20797265     .word    0x20797265

b0004068:    202b2b47     .word    0x202b2b47

b000406c:    6574694c     .word    0x6574694c

b0004070:    30303220     .word    0x30303220

b0004074:    2d317139     .word    0x2d317139

b0004078:    29363731     .word    0x29363731

b000407c:    332e3420     .word    0x332e3420

b0004080:    4100332e     .word    0x4100332e

b0004084:    00000029     .word    0x00000029

b0004088:    62616561     .word    0x62616561

b000408c:    1f010069     .word    0x1f010069

b0004090:    05000000     .word    0x05000000

b0004094:    06005434     .word    0x06005434

b0004098:    09010802     .word    0x09010802

b000409c:    14041201     .word    0x14041201

b00040a0:    17011501     .word    0x17011501

b00040a4:    19011803     .word    0x19011803

b00040a8:    1e021a01     .word    0x1e021a01

b00040ac:    Address 0xb00040ac is out of bounds.

看到了吧,第20行的数字开始减一操作,就是wait函数

LAY

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值