Showing posts with label MCU. Show all posts
Showing posts with label MCU. Show all posts

Jun 2, 2011

Easy Going with ARM, square root up [3]

In my last post, we noticed that gc is still nearly twice as slow as the gcc for nbody benchmark. So, why? We put a simplest program under test.

go@localhost:~/go/ex$ cat f.c
double mysqrt(double a){ return sqrt(a);}
main(){ mysqrt(0.1);}

go@localhost:~/go/ex$ cat f.go
package main
import "math"
func mysqrt(a float64) float64 {return math.Sqrt(a)}
func main(){ mysqrt(0.1)}

Now compare the gcc's disassembly

000083e0 <mysqrt>:
push    {r7, lr}
sub     sp, #8
add     r7, sp, #0
strd    r0, r1, [r7]
vldr    d6, [r7]
vsqrt.f64       d7, d6
vcmp.f64        d7, d7
vmrs    APSR_nzcv, fpscr
beq.n   8408 <mysqrt+0x28>
vmov    r0, r1, d6
blx     8354 <_init+0x48>
vmov    d7, r0, r1
vmov    r2, r3, d7
mov     r0, r2
mov     r1, r3
add.w   r7, r7, #8
mov     sp, r7
pop     {r7, pc}

00008418 <main>:
push    {r7, lr}
add     r7, sp, #0
add     r1, pc, #16 
ldrd    r0, r1, [r1]
bl      83e0 <mysqrt>
mov     r0, r3
pop     {r7, pc}
to gc's:
00010c00 <main.mysqrt>:
ldr     r1, [sl]
cmp     sp, r1
movcc   r1, #180        ; 0xb4
movcc   r2, #16
movcc   r3, lr
blcc    14398 <runtime.morestack>
str     lr, [sp, #-20]!
vldr    d0, [sp, #24]
vstr    d0, [sp, #4]
bl      280e4 <math.sqrt>
vldr    d0, [sp, #12]
vstr    d0, [sp, #32]
ldr     pc, [sp], #20

00010c34 <main.main>:
ldr     r1, [sl]
cmp     sp, r1
movcc   r1, #180        ; 0xb4
movcc   r2, #0
movcc   r3, lr
blcc    14398 <runtime.morestack>
str     lr, [sp, #-20]!
ldr     fp, [pc, #16] 
vldr    d0, [fp]
vstr    d0, [sp, #4]
bl      10c00 <main.mysqrt>
ldr     pc, [sp], #20
b       10c64 <main.main+0x30>
Russ is correct. Gcc uses intrinsics to generate VSQRT instruction in the code stream directly, while Gc uses a function call, which it seems expensive. Go will have function inlining, tough not available now. But look at A9 Neon MPE reference manual, VSQRTD uses 32 cycles (VDIV is 25, most others including VMUL are just 1 or 2 cycles), so the saving from function inlining may not help much.

Of course, SQRT in hardware already speeds up nbody benchmark by 7 times.  ARM VFP also has ABS and NEG instruction. They are trivial at first glance, because what they do are simply clear, or negate the MSB (sign bit) of the operand register. Cannot this be done by simple AND or XOR? Yes. But we also need to know:
  1. MPE has separate VFP and NEON (SIMD) block share the same 32 sixty-four bit register file. 
  2. VFP has no logic operation.
  3. NEON has, but switching between VFP and NEON in A9, is expensive.
  4. Move back and forth between VFP and ARM core registers stalls both pipelines, should not be used in tight loop. 
Reason No. 2 and 4 explains the reasons for VABS and VNEG, but not a strong one. I am happy to live without them.

Reason No. 3 is critical. FPU normally is mandatory for C and Go compilers (software emulation in both compiler and Linux kernel exists to help low end chips that without hardware floating point unit), but SIMD, is in the land of handcrafts, is used to speed up certain special operation, which in turn the special hardware logic tends to do much better job, thus renders SIMD a white elephant in the end. For example, NVidia is frequently challenged why not implement NEON in its cutting edge Tegra 2, the reason quoted usually is, with dedicate Audio/video codec and 2D/3D GPU, they don't see an immediate needs for NEON. Most other chips, including NVidia's next generation Tegra, have NEON, for programmers to waste time on. 

Back to package math. I would like to port sincos_amd64.s to ARM VFP. That function can be used as the basis for other trigonometry functions. The algorithm, SLEEF, is intended for SIMD, but this implementation is not. It is a slightly optimized translation from C. So it would be straightforward to have a Go copy. But, as said in its comment, a VCMP would save a branch, always a win in modern deep pipelined CPU.

What does that mean? Dive into assembly again. A Go like this:

package main

func main(){
a, b := 1.0, 2.0
if a > b {
b = a
}
}

would 5g -S to this:

--- prog list "main" ---
0000 (f.go:3) TEXT   main+0(SB),R0,$32-0
0001 (f.go:4) MOVD   $(1.00000000000000000e+00),F4
0002 (f.go:4) MOVD   $(2.00000000000000000e+00),F3
0003 (f.go:5) B       ,5(APC)
0004 (f.go:5) B       ,16(APC)
0005 (f.go:5) B       ,7(APC)
0006 (f.go:5) B       ,14(APC)
0007 (f.go:5) MOVD   F4,F0
0008 (f.go:5) MOVD   F4,F2
0009 (f.go:5) MOVD   F3,F1
0010 (f.go:5) CMPD   F3,F4,
0011 (f.go:5) BVS     ,13(APC)
0012 (f.go:5) BGT     ,6(APC)
0013 (f.go:5) B       ,4(APC)
0014 (f.go:6) MOVD   F2,F0
0015 (f.go:5) B       ,16(APC)
0016 (f.go:8) RET     ,

Lot of B for branches. Modern CPU puts lot of silicon to predict the branches, to squeeze out the pipeline bubbles caused by mis-prediction. But if we work on assembly, the code could be much optimized with conditional instructions. That could be a big deal for things like math.Sincos, because it is often used in tight loops for thousands of calculations.

Looking forward to another commit source-icide soon.


May 31, 2011

Easy Going with ARM, Kung Fu Panda [2]

One of the fastest ARM board we can get today is $179 Pandaboard from Digikey. Do prepare to wait for months though.

I chose to install the headless image of ubuntu 11.04. Because my laptop has a SD slot on /dev/mmcblk0, the installation process was as smooth as my Teflon pan.

  1. insert SD card to Laptop
  2. sudo umount /dev/mmcblk0
  3. sudo sh -c 'zcat ubuntu-11.04-preinstalled-headless-armel+omap4.img.gz > /dev/mmcblk0'
  4. sync
  5. insert SD card to pandaboard, plug power, LAN and USB-Serial cable in
  6. On the laptop terminal: TERM=vt100 minicom
  7. turn on the pandaboard, on minicom after the uboot comes the standard ubuntu installation process, and finally gives us a shell prompt.
The Go installation is the same as in my last post, only much faster. Now comes to the benchmark, how does 1GHz dual core ARM A9 compare to my 2GHz dual core Intel x86, and 1GHz single core ARM A8?

Not surprisingly, the number of cores does not count here since no parallel processing is benchmarked. For string processing 1GHz A9 is slightly faster than A8 , but still more than twice slower than 2GHz x86 core. A9's VFP has been greatly improved, 5x faster than A8 by gcc.

But surprisingly, and I am astonished to see, for the floating point crunching on A9, gc is 11x slower than optimized gcc. This is very unfortunate because what I am interested in Go on ARM is OpenGL ES, which is all about matrix operations on floating points.

[update] 11x slower is caused by unoptimized pkg/math/sqrt.go, since ARM VFP has VSQRT instruction, it should not be hard to speed it up.

[update 2] I made it. Now it is 7x faster

nbody -n 50000000
        gcc -O2 -lm nbody.c     71.40u 0.00s 71.43r
        gc nbody        120.93u 0.00s 120.94r
        gc_B nbody      119.78u 0.00s 119.80r
[/update2]

go@localhost:~/go/test/bench$ cat /proc/cpuinfo
Processor   : ARMv7 Processor rev 2 (v7l)
processor   : 0
BogoMIPS  : 2009.29
                                      
processor   : 1        
BogoMIPS  : 1963.08         
Features  : swp half thumb fastmult vfp edsp thumbee neon vfpv3        
Hardware : OMAP4 Panda board 

go@localhost:~/go/test/bench$ gomake timing  
./timing.sh
reverse-complement < output-of-fasta-25000000
gcc -O2 reverse-complement.c    7.88u 1.55s 9.54r
gc reverse-complement   18.36u 1.91s 20.29r
gc_B reverse-complement 17.75u 2.08s 19.85r
                                                                              
nbody -n 50000000
gcc -O2 -lm nbody.c     71.40u 0.00s 71.41r
gc nbody        862.53u 0.02s 862.78r
gc_B nbody      865.00u 0.05s 865.28r   

(OMAP3, OMAP4, IMX53 and the earth creator)

May 30, 2011

Easy Going with ARM

How easy would it be to Go with ARM? It depends, from super difficult to duper easy. This is the latter case. Just got one from Mouser for US$149 with free FedEx to Singapore. Opened the box, plugged the cables in, inserted the SD card, and pressed the power switch. 

As advertised, I should expect an instant boot-up. But nothing happened. Dead on arrival? Scratched my head. Where's the manual? Searched the box again. Not found. Looked at the board and thought, should I insert the SD or microSD? No harm to try. 

Sliced the microSD from the SD sleeve, put in that slot, powered up. Bingo. AND as I idly lying back on my armchair and glanced the box again... There you are. The manual and CD are just there, pasted on the back of the box cover!

From then on, it could not be easier to just follow the normal procedure to have a Go on the pre-installed Ubuntu Lucid on the IMX53 Starter-Kit.
  1. sudo apt-get update
  2. sudo apt-get install mercurial bison ed gawk gcc libc6-dev make
  3. hg clone -u release https://go.googlecode.com/hg/ go
  4. cd go/src; ./make.bash
It just needs much longer time (an hour?), because it only runs on a 1GHz ARM Cortex-A8, with 1GB DDR3 RAM, and worst of all, SD is way too slow. Great news is this pixie has SATA port. Next time will try find a spare hard disk and see how speedy it could go.

How slow is it? Here's the go/test/bench on my PC and ARM. I just picked two to show the string processing and floating point crunching, and cpuinfo is shorten to show the relevance only.

#! cat /proc/cpuinfo
model name : Intel(R) Core(TM)2 Duo CPU     T7300  @ 2.00GHz
stepping : 11
cpu MHz : 800.000
cache size : 4096 KB
bogomips : 3990.32

#! make timing
reverse-complement < output-of-fasta-25000000
gcc -O2 reverse-complement.c 1.43u 0.23s 1.67r
gc reverse-complement 2.72u 0.26s 3.00r
gc_B reverse-complement 2.64u 0.31s 2.95r

nbody -n 50000000
gcc -O2 -lm nbody.c 27.78u 0.00s 27.92r
gc nbody 54.01u 0.00s 54.06r
gc_B nbody 52.18u 0.00s 52.27r

lucid@lucid-desktop:~$ cat /proc/cpuinfo
Processor : ARMv7 Processor rev 5 (v7l)
BogoMIPS : 999.42
Features : swp half thumb fastmult vfp edsp neon vfpv3 
CPU implementer : 0x41
CPU architecture: 7
Hardware : Freescale MX53 LOCO Board

lucid@lucid-desktop:~/go/test/bench$ gomake timing
reverse-complement < output-of-fasta-25000000
gcc -O2 reverse-complement.c 8.00u 1.14s 10.27r
gc reverse-complement 22.97u 1.26s 27.62r
gc_B reverse-complement 22.09u 1.52s 30.77r

nbody -n 50000000
gcc -O2 -lm nbody.c 316.08u 0.40s 389.46r
gc nbody  645.32u 686.06s 1843.30r
gc_B nbody 653.49u 640.93s 1373.40r

Not surprisingly, Cortex-A8 VFP is known to be very slow, due to its non-pipeline architecture. But I don't know it is so tortoise-like. Will be Cortex-A9 much better? Once I get my OMAP4430 panda board work, I will report it here.

Just to confirm I was using VFP not the soft-float, I created each version and compare:

$    5l -F -o nbody.arm5 nbody.5
$    5l -o nbody.arm6 nbody.5
$    time ./nbody.arm6 -n 50000
real 0m1.316s
user 0m1.310s
sys 0m0.000s

$    time ./nbody.arm5 -n 50000
real 0m30.788s
user 0m29.830s
sys 0m0.000s

By the way, the proper way to run glib is via pkg-config as below. But I gave up trying to fix run().

run 'gcc -O2 `pkg-config --cflags glib-2.0` k-nucleotide.c `pkg-config --libs glib-2.0`' a.out <x

  



(the mouse is for illustration only, not come with the board)

Aug 1, 2005

The revolution of evolution


从进化的角度看, Blackfin 已不再是一颗普通的DSP 或MCU. 它已经将这两个品种的优点揉合在一起. 定名为 MSA, 即综合信号体系, 具有非常强大的数字运算能力, 又有灵活的内存和中断服务. 统一精简的指令集使 Blackfin 无论从性价比还是使用方便上, 都超过分立的DSP和MCU.

为 了达到最大的定点运算性能, Blackfin 使用了十级流水线, 单周期的乘法指令, 双乘加器, 硬件循环和宽达160位的片内数据通路, 可以同时读取四条16位指令, 四个16位操作数, 将其两两相乘并累加, 再将一个32位的结果写回, 比如下面 ADI VDSP++ 的汇编指令:
a1+=r0.l * r2.h, a0+=r0.h * r2.l || r2.l = W[i2++] || [i1--] = r3

或对应的 Inferno 的汇编指令:
a2 + =r0 * r2;
r2.l.w = (i2)+;
(i1)- = r3;


这种体系结构为大量的DSP运算提供了必要的支持, 这不是在现有的MCU 如ARM上增加几条优化指令就能做到的.

同时, Blackfin 具备12通道的DMA, 两层15级中断, MMU提供的内存保护和缓存, 这些为编写复杂的控制软件和操作系统提供了可能. 已有大量的实时操作系统直接运行在 Blackfin 上, 包括Linux 2.6, 将 Inferno 移植到 Blackfin 上也没有任何技术问题.

其集成在片上的SDRAM控制器, 高速串口, 并口, 时钟, 甚至以太网控制器, 也为连接外设搭建完整系统提供了极大方便.

更让人钦佩的是, Blackfin 出色性能的背后, 是低廉的成本和很低的功耗. 这和现时片面追求高性能成为鲜明对比. 我们需要的是能满足基本性能的最便宜最省电的芯片, Blackfin 是最佳选择.