而不是介绍
本文包含一个与预算stm32微控制器有关的手动优化应用程序关键部分的示例,与库函数相比,该性能提高了5倍或更多。
平方根提取通常用于应用程序中。sqrt函数包含在标准C库中,并在实数上运行:
double sqrt (double num);
long double sqrtl (long double num);
微控制器主要处理整数。他们通常没有实数寄存器。
实际上,除了多次转换“整数<=>实数”上的计算速度损失之外,还另外损失了精度-示例1。
示例1:正向和反向转换的精度损失
//
uint32_t L1 = 169;
uint32_t L2 = 168;
//
uint32_t r1 = ( uint32_t )sqrt( ( double ) L1 );
uint32_t r2 = ( uint32_t )sqrt( ( double ) L2 );
//
L1 = r1*r1; // r1 = 13
L2 = r2*r2; // r2 = 12
//
// L1 = 169 — 169
// L2 = 144 — 168, 14%
问题的提法
通过四舍五入到最接近的整数来提高sqrt计算的精度。
如果可能,提高生产率。
问题的解决
创建一个自定义函数,例如,基于标准函数sqrt_fpu-示例2。
示例2:使用sqrt_fpu算法计算整数根
uint16_t sqrt_fpu ( uint32_t L )
{
if ( L < 2 )
return ( uint16_t ) L;
double f_rslt = sqrt( ( double ) L );
uint32_t rslt = ( uint32_t ) f_rslt;
if ( !( f_rslt - ( double ) rslt < .5 ) )
rslt++;
return ( uint16_t ) rslt;
}
Sqrt_fpu的优点:
- 紧凑的代码;
- 达到要求的精度。
sqrt_fpu的缺点:
- 由于额外的调用和额外的浮点运算而导致的性能损失;
- 缺乏在用户级别优化计算速度的明显潜力。
sqrt_fpu .
— - ().
-: , .
1. :
« , , .»
sqrt_odd — 3.
3: sqrt_odd
uint16_t sqrt_odd ( uint32_t L )
{
if ( L < 2 )
return ( uint16_t ) L;
uint16_t div = 1, rslt = 1;
while ( 1 )
{
div += 2;
if ( ( uint32_t ) div >= L )
return rslt;
L -= div, rslt++;
}
}
,
.
sqrt_odd:
- ;
sqrt_odd:
- ;
- ; , 10e4+ 150 — 1;
- .
1: sqrt_odd
2. :
« »:
Rj = ( N / Ri + Ri ) / 2
sqrt_new — 4.
4: sqrt_new
uint16_t sqrt_new ( uint32_t L )
{
if ( L < 2 )
return ( uint16_t ) L;
uint32_t rslt, div;
rslt = L;
div = L / 2;
while ( 1 )
{
div = ( L / div + div ) / 2;
if ( rslt > div )
rslt = div;
else
return ( uint16_t ) rslt;
}
}
sqrt_new — sqrt_fpu ( 2).
sqrt_new:
- ;
- — sqrt_fpu;
- ;
sqrt_new:
- .
sqrt_new ( 2):
- ;
- .
2: sqtr_new (!)
(!) — 10e5+ 8 .
sqrt_new :
- , , ( );
- , -, ;
- .
2. sqrt_evn ( 5).
sqrt_evn , , [ 0… 0xFFFFFFFF ].
sqrt_evn 2- 5- , sqrt_new ~40%.
[ 1… 10 000 000 ] sqtr_evn 2-3 .
sqrt_evn — 3.
3: sqtr_evn
, sqrt_evn — 5.
5: sqrt_evn
uint16_t sqrt_evn ( uint32_t L )
{
if ( L < 2 )
return ( uint16_t ) L;
uint32_t div;
uint32_t rslt;
uint32_t temp;
if ( L & 0xFFFF0000L )
if ( L & 0xFF000000L )
if ( L & 0xF0000000L )
if ( L & 0xE0000000L )
div = 43771;
else
div = 22250;
else
if ( L & 0x0C000000L )
div = 11310;
else
div = 5749;
else
if ( L & 0x00F00000L )
if ( L & 0x00C00000L )
div = 2923;
else
div = 1486;
else
if ( L & 0x000C0000L )
div = 755;
else
div = 384;
else
if ( L & 0xFF00L )
if ( L & 0xF000L )
if ( L & 0xC000L )
div = 195;
else
div = 99;
else
if ( L & 0x0C00L )
div = 50;
else
div = 25;
else
if ( L & 0xF0L )
if ( L & 0x80L )
div = 13;
else
div = 7;
else
div = 3;
rslt = L;
while ( 1 )
{
temp = L / div;
temp += div;
div = temp >> 1;
div += temp & 1;
if ( rslt > div )
rslt = div;
else
{
if ( L / rslt == rslt - 1 && L % rslt == 0 )
rslt--;
return ( uint16_t ) rslt;
}
}
}
«» — . 1 .
sqrt_evn , .
( 2).
— .
.
[ 3, 7, 13, 25 ] « ». (). .
— .
:
- : STM32F0308-DISCO, MCU STM32F030R8T6
- : STM32CubeIDE
- : USB-UART PL2303HX
:
- :
- : CPU — 48 MHz, UART (RS485) — 9600 bit/s
- : , Release
- : MCU GCC Linker: Miscellaneous: -u _printf_float
sqrt_fpu, sqrt_new sqrt_evn.
100 000 3- — 4.
4:
.
— sqrt_fpu, . — .
, ( 4), .
( 5) .
5:
( 6) , 1 .
sqrt_fpu 19 531, sqrt_evn 147 059 ; sqrt_evn ~7,5 , sqrt_fpu.
6:
, , , .
同时,由于使用了低成本的微控制器模型,手动算法代码优化可以有效地大规模生产小型IoT,从而为较旧的模型释放了复杂任务的空间。