パタヘネの”第3章:コンピュータにおける算術演算”にある、3.3乗算の補足説明です。
“乗算は、ハードウェア的には、前述した加算処理とシフト処理で実装できる”を確認するために、Cでコードを作成しました。
当然ながら、乗算命令が下記のような形でソフトウェア的に実装されているというわけではないです。
下記のような振る舞いを行う”電子回路が組まれる”という意味だけど、
回路的として説明すると分かり辛いので、説明としてコードとして記述しただけです。
#include <stdio.h> main() { int a = 11; int b = 12; // int c = multi( a, b ); int c = multi_by_shift( a, b ); printf( "c=%d\n", c ); } //------------------------------------------------- // 普通に掛け算を行う //------------------------------------------------- int multi( int a, int b ) { return a * b; } //------------------------------------------------- // 加算とシフト命令だけで、掛け算を行う //------------------------------------------------- int multi_by_shift( int multiplicand, int multiplier ) { // 値の初期化 int product = 0; // 積 //-------------------------------------------- // multiplicandを全ビット処理するまで繰り返し //-------------------------------------------- int loop = 0; for ( loop = 0; loop < 32; loop++ ) { // 最下位ビットを取り出す int t0 = multiplicand & 0x0001; // 最下位ビットが立っていたら、乗数を足しこむ int t1 = (t0 == 0) ? 0 : multiplier; product += t1; // for debug printf( "loop[%02d] 0x%02x 0x%08x -> %3d %3d\n", loop, t0, multiplier, t1, product ); // 被乗数をシフト(次の処理対象ビットをずらす) multiplicand >>= 1; // 乗数をシフト(桁上げを行う) multiplier <<= 1; } return product; } |
基本的な考えかた上記の通りだが、この形のままでCPU設計を行うと、掛け算にかかる処理時間が非常に長くなってしまう。
なので、通常はここからさらに最適化を行う。
方法の1つは、0~31bit分シフトされた値を一気に求めて、同時に2つづつ足しこみを行う。
そうするとビット数がnの場合の計算量が O(n)から O(log n)に減少する。
(一方でH/W的には、たくさんの加算器を用意する必要があるが、最近のトランジスタは十分に小さいので無視できるレベル)
また、下記はx86環境のアセンブリ(gccで生成したもの。本当はmipsのコードが欲しかったけど、環境がないのであきらめた。
multiは”imull”命令で乗算しているけど、_multi_by_shiftでは乗算命令がなくなっている。
_multi:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
imull 12(%ebp), %eax
popl %ebp
ret
.section .rdata,"dr"
.align 4
_multi_by_shift
pushl %ebp
movl %esp, %ebp
subl $56, %esp
movl $0, -12(%ebp)
movl $0, -16(%ebp)
movl $0, -16(%ebp)
jmp L4
L7:
movl 8(%ebp), %eax
andl $1, %eax
movl %eax, -20(%ebp)
cmpl $0, -20(%ebp)
je L5
movl 12(%ebp), %eax
jmp L6
L5:
movl $0, %eax
L6:
movl %eax, -24(%ebp)
movl -24(%ebp), %eax
addl %eax, -12(%ebp)
movl -12(%ebp), %eax
movl %eax, 20(%esp)
movl -24(%ebp), %eax
movl %eax, 16(%esp)
movl 12(%ebp), %eax
movl %eax, 12(%esp)
movl -20(%ebp), %eax
movl %eax, 8(%esp)
movl -16(%ebp), %eax
movl %eax, 4(%esp)
movl $LC1, (%esp)
call _printf
sarl 8(%ebp)
sall 12(%ebp)
addl $1, -16(%ebp)
L4:
cmpl $31, -16(%ebp)
jle L7
movl -12(%ebp), %eax
leave
ret |
関連記事
コメントを残す