|
附錄二 程式語言效率分析
以下為利用ASSEMBLY,BASIC,PASCAL,C,FORTRAN 等程式語言,將一個24x 24之點陣字形,放大成為48x 48,並分別比較其處理速度、佔用空間以及製作時間。
為了正確計算執行時間,特意作 10,000 次處理,至於指定的24x 24字形,則假設為一空格。
一、ASSEMBLY
組合語言變化無窮,先以一般的作法,用點陣位移來處理。
1: PAGE 60, 132
2: CG SEGMENT
3: BUFIN DB 72 DUP(0)
4: BUFOT DB 72*4 DUP(0)
5: ASSUME CS:CG,DS:CG,ES:CG
6: START:
7: MOV AX,CG
8: MOV DS,AX
9: MOV ES,AX
10: CLD
11: MOV BP,10000 ; 處理10,000次
12: S3:
13: SUB CX,CX
14: MOV BX,CX
15: MOV DX,1803H ; 計數用
16: MOV SI,OFFSET BUFIN ; 24*24 點陣起始位址
17: MOV DI,OFFSET BUFOT ; 預定48*48儲存位址
18: MVBYTE:
19: MOV BH,DL ; 做三列
20: MVDB:
21: LODSB ; 取原點陣
22: MOV BL,AL
23: MOV CL,8 ; 做八位元
24: MVDB1:
25: RCL BL,1 ; 左移一次
26: PUSHF ; 保存狀態
27: RCL AX,1 ; 兩字同時左移一次
28: POPF ; 取出原移位狀態
29: RCL AX,1 ; 再一次,得雙位點值
30: LOOP MVDB1 ; 八次迴路
31: STOSW ; 存入
32: MOV [DI+4],AX ; 上下放大一行
33: DEC BH ; 共 3列
34: JNZ MVDB
35: ADD DI,6 ; 移向次行
36: DEC DH
37: JNZ MVBYTE ; 共24行
38: DEC BP ; 執行10,000次
39: JNZ S3 ; 完成
40: MOV AX,4C00H
41: INT 21H
42: CG ENDS
43: END START
本程式製作時間,為十五分鐘。
經匯編後,得934 字元的執行程式,執行耗時14.5秒。
若將上段程式加以分析,可以發現到此段程式執行時間全部浪費在23至30這一段「迴路」中。為了增加速度,可以將空間加大,避開迴路,連續執行八次「移位」動作如次:
23: RCL BL,1
24: RCL AX,1
25: SHL AX,1
26: 同上共八次
…
47: MOV CX,AX ; AX中為單位元值
48: SHR CX,1 ; CX得到雙位元點陣值
49: OR AX,CX ; 雙位元點陣合併
似此,程式增大了36字元,但執行時間卻減少為 7.1秒,速度快了一倍!
是不是還是更好的方法呢?相信一定多得不計其數。比如說,我們已知原點陣放大一倍後點形為「雙點」,以雙點做表,取其對應之值,即可免除各點移位的手續,再將原程式第18條以下改為:
18: VT2:
19: CALL MVBYTE ; 放大一行
20: SUB SI,3 ; 縱向尚須放大一次
21: CALL MVBYTE ; 再放大一行
22: DEC DH ; 完成否?
23: JNZ VT2 ; 再做
24: RET ; 完成
25: MVBYTE:
26: MOV CL,DL ; 一行有三字元
27: MVDB:
28: LODSB ; 取一字元
29: MOV AH,AL ; 分置兩處
30: AND AX,0FF0H ; AH,AL 各取四位元
31: SHR AL,1 ; 右移四次還原
32: SHR AL,1
33: SHR AL,1
34: SHR AL,1
35: MOV BL,AL
36: MOV AL,BYTETB[BX] ; 左字元取預設表值
37: MOV BL,AH
38: MOV AH,BYTETB[BX] ; 右字元取表值
39: STOSW ; 得二字元置緩衝器中
40: LOOP MVDB ; 做三次
41: RET
42 ; 轉換表
43: BYTETB DB 000H,003H,00CH,00FH,030H,033H,03CH,03FH
44: DB 0C0H,0C3H,0CCH,0CFH,0F0H,0F3H,0FCH,0FFH
45: CG ENDS
46: END START
再換個方法,因為有個XALT的指令,是專為這種程式所設計的。由第25條起,調整如下:
25: MVBYTE:
26: MOV CL,4 ; 供AL左移四位用
27: MOV BX,OFFSET BYTETB
28: MVDB:
29: LODSB ; 取一字元
30: MOV AH,AL ; 分置兩處
31: AND AX,0F00FH ; AH,AL 各取四位元
32: SHR AL,CL
33: XLAT ; 將[BX+AL]值放AL中
34: XCHG AL,AH
35: XLAT
36: STOSW
37: DEC DL
38: JNZ MVDB
如此,執行程式959 字元,執行速度3.2 秒,效率更佳。
上述程式的缺點為:在循環過程中,速度有所損失,而且用四位元查表也費事耗時。如果用一字元查表,則需增大「表」的對應值,再改為「總表」的方式,一次即可查到。且由第20行改起,並力求指令的精簡,如:
20: MOV DX,OFFSET BYTETB
21: MVDB:
22: LODSB
23: SUB AH,AH
24: SHL AX,1 ; 一字元須變為二字元
25: ADD AX,DX ; 之位置以查表
26: MOV BX,AX ; BX可供間接定址用
27: MOV AX,[BX] ; 以一字元查表值
28: STOSW ; 查妥存入第一行
29: MOV [DI+4],AX ; 上下再重複一行
30: LODSB
31: SUB AH,AH ; 處
32: SHL AX,1 ; 理
33: ADD AX,DX
34: MOV BX,AX ; 第
35: MOV AX,[BX] ; 二
36: STOSW ; 列
37: MOV [DI+4],AX ;
38: LODSB ;
39: SUB AH,AH ; 處
40: SHL AX,1 ; 理
41: ADD AX,DX
42: MOV BX,AX ; 第
43: MOV AX,[BX] ; 三
44: STOSW ; 列
45: MOV [DI+4],AX ;
46: ADD DI,6 ; 再處理下一行
47: LOOP MVDB ; 共24次
48: DEC BP ; 做10,000次
49: JNZ S3 ; 完成
50: MOV AX,4C00H
51: INT 21H
52: RET
程式到此為止,下面還有一轉換總表,可供各程式共用。
1:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2:; 轉 換 表 ;
3:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4: BYTETB LABEL WORD
5: DB 000H,000H,000H,003H,000H,00CH,000H,00FH
6: DB 000H,030H,000H,033H,000H,03CH,000H,03FH
7: DB 000H,0C0H,000H,0C3H,000H,0CCH,000H,0CFH
8: DB 000H,0F0H,000H,0F3H,000H,0FCH,000H,0FFH
9: DB 003H,000H,003H,003H,003H,00CH,003H,00FH
10: DB 003H,030H,003H,033H,003H,03CH,003H,03FH
11: DB 003H,0C0H,003H,0C3H,003H,0CCH,003H,0CFH
12: DB 003H,0F0H,003H,0F3H,003H,0FCH,003H,0FFH
13: DB 00CH,000H,00CH,003H,00CH,00CH,00CH,00FH
14: DB 00CH,030H,00CH,033H,00CH,03CH,00CH,03FH
15: DB 00CH,0C0H,00CH,0C3H,00CH,0CCH,00CH,0CFH
16: DB 00CH,0F0H,00CH,0F3H,00CH,0FCH,00CH,0FFH
17: DB 00FH,000H,00FH,003H,00FH,00CH,00FH,00FH
18: DB 00FH,030H,00FH,033H,00FH,03CH,00FH,03FH
19: DB 00FH,0C0H,00FH,0C3H,00FH,0CCH,00FH,0CFH
20: DB 00FH,0F0H,00FH,0F3H,00FH,0FCH,00FH,0FFH
21: DB 030H,000H,030H,003H,030H,00CH,030H,00FH
22: DB 030H,030H,030H,033H,030H,03CH,030H,03FH
23: DB 030H,0C0H,030H,0C3H,030H,0CCH,030H,0CFH
24: DB 030H,0F0H,030H,0F3H,030H,0FCH,030H,0FFH
25: DB 033H,000H,033H,003H,033H,00CH,033H,00FH
26: DB 033H,030H,033H,033H,033H,03CH,033H,03FH
27: DB 033H,0C0H,033H,0C3H,033H,0CCH,033H,0CFH
28: DB 033H,0F0H,033H,0F3H,033H,0FCH,033H,0FFH
29: DB 03CH,000H,03CH,003H,03CH,00CH,03CH,00FH
30: DB 03CH,030H,03CH,033H,03CH,03CH,03CH,03FH
31: DB 03CH,0C0H,03CH,0C3H,03CH,0CCH,03CH,0CFH
32: DB 03CH,0F0H,03CH,0F3H,03CH,0FCH,03CH,0FFH
33: DB 03FH,000H,03FH,003H,03FH,00CH,03FH,00FH
34: DB 03FH,030H,03FH,033H,03FH,03CH,03FH,03FH
35: DB 03FH,0C0H,03FH,0C3H,03FH,0CCH,03FH,0CFH
36: DB 03FH,0F0H,03FH,0F3H,03FH,0FCH,03FH,0FFH
37: DB 0C0H,000H,0C0H,003H,0C0H,00CH,0C0H,00FH
38: DB 0C0H,030H,0C0H,033H,0C0H,03CH,0C0H,03FH
39: DB 0C0H,0C0H,0C3H,0C0H,0CCH,0C0H,0CFH,0C0H
40: DB 0C0H,0F0H,0C0H,0F3H,0C0H,0FCH,0C0H,0FFH
41: DB 0C3H,000H,0CH3,003H,0C3H,00CH,0C3H,00FH
42: DB 0C3H,030H,0C3H,033H,0C3H,03CH,0C3H,03FH
43: DB 0C3H,0C0H,0C3H,0C3H,0C3H,0CCH,0C3H,0CFH
44: DB 0C3H,0F0H,0C3H,0F3H,0C3H,0FCH,0C3H,0FFH
45: DB 0CCH,000H,0CCH,003H,0CCH,00CH,0CCH,00FH
46: DB 0CCH,030H,0CCH,033H,0CCH,03CH,0CCH,03FH
47: DB 0CCH,0C0H,0CCH,0C3H,0CCH,0CCH,0CCH,0CFH
48: DB 0CCH,0F0H,0CCH,0F3H,0CCH,0FCH,0CCH,0FFH
49: DB 0CFH,000H,0CFH,003H,0CFH,00CH,0CFH,00FH
50: DB 0CFH,030H,0CFH,033H,0CFH,03CH,0CFH,03FH
51: DB 0CFH,0C0H,0CFH,0C3H,0CFH,0CCH,0CFH,0CFH
52: DB 0CFH,0F0H,0CFH,0F3H,0CFH,0FCH,0CFH,0FFH
53: DB 0F0H,000H,0F0H,003H,0F0H,00CH,0F0H,00FH
54: DB 0F0H,030H,0F0H,033H,0F0H,03CH,0F0H,03FH
55: DB 0F0H,0C0H,0F0H,0C3H,0F0H,0CCH,0F0H,0CFH
56: DB 0F0H,0F0H,0F0H,0F3H,0F0H,0FCH,0F0H,0FFH
57: DB 0F3H,000H,0F3H,003H,0F3H,00CH,0F3H,00FH
58: DB 0F3H,030H,0F3H,033H,0F3H,03CH,0F3H,03FH
59: DB 0F3H,0C0H,0F3H,0C3H,0F3H,0CCH,0F3H,0CFH
60: DB 0F3H,0F0H,0F3H,0F3H,0F3H,0FCH,0F3H,0FFH
61: DB 0FCH,000H,0FCH,003H,0FCH,00CH,0FCH,00FH
62: DB 0FCH,030H,0FCH,033H,0FCH,03CH,0FCH,03FH
63: DB 0FCH,0C0H,0FCH,0C3H,0FCH,0CCH,0FCH,0CFH
64: DB 0FCH,0F0H,0FCH,0F3H,0FCH,0FCH,0FCH,0FFH
65: DB 0FFH,000H,0FFH,003H,0FFH,00CH,0FFH,00FH
66: DB 0FFH,030H,0FFH,033H,0FFH,03CH,0FFH,03FH
67: DB 0FFH,0C0H,0FFH,0C3H,0FFH,0CCH,0FFH,0CFH
68: DB 0FFH,0F0H,0FFH,0F3H,0FFH,0FCH,0FFH,0FFH
69: CG ENDS
70: END START
本程式因為加了個轉換表,空間增大為1471字元,但速度卻加快為2.5 秒,這是空間換時間的最佳例証。
二、C
C近來極受美國各系統公司的推崇,我們特以之與組合語言作個比較,但不幸的是在指令的精簡上,就顯得力不從心,不像組合語言那樣可以斤斤計較。
因此,我們祇能就點陣移位、查小表及查總表的方式,測試其效率。首先,利用查大表的方式如下:
1: main()
2: {
3: unsigned char s[24][3];
4: unsigned short tab[256], d[48][3], count;
5: register short i,j,k;
6:
7: for (count = 0; count < 10000; count++)
8: {
9: k = 0;
10: for (i = 0; i < 24; i++)
11: {
12: for (j = 0; j < 3; j++)
13: d[k][j] = d[k + 1][j] = tab[s[i][j]];
14: k += 2;
15: }
16: }
17: }
程式製作時間10分鐘,較組合語言稍快;佔用空間4575字元,則大了三倍,至於執行速度為18秒,慢了七倍之多。
再換個方法,試一試查小表如次:
1: main()
2: {
3: unsigned char i,j, s[24][3], d[48][6], tab[16];
4: unsigned short count;
5: register short k, l, x;
6:
7: for (count = 0; count < 10000; count++)
8: {
9: k = 0;
10: for (i = 0; i < 24; i++)
11: {
12: l = 0;
13 for (j = 0; j < 3; j++)
14: {
15: x = s[i][j];
16: d[k][l] = d[k + 1][l] = tab[x & 0360 >> 4];
17: d[k][l+1] = d[k + 1][l + 1] = tab[x & 017];
18: l += 2;
19: }
20: k += 2;
21: }
22: }
23: }
佔用空間為4,693 字元,比組合語言大了五倍;速度為30秒,則慢了四倍多。這証明了組合語言的靈活性,在空時效率交換的技術運用下,可以選擇最有利的條件。再看利用位置的方式,結果如何?
1: main()
2: {
3: unsigned char ss[24][3];
4: unsigned short dd[48][3];
5: int i, k, count;
6: register short d, j;
7: register unsigned char s;
8:
9: for (count = 0; count < 10000; count++)
10: {
11: k = 0;
12: for (i = 0; i < 24; i++)
13: {
14: for (j = 0; j < 3; j++)
15: {
16: s = ss[i][j];
17: d = 0;
18: if (s & 01)
19: d |= 03;
20: if (s & 02)
21: d |= 014;
22: if (s & 04)
23: d |= 060;
24: if (s & 010)
25: d |= 0300;
26: if (s & 020)
27: d |= 01400;
28: if (s & 040)
29: d |= 06000;
30: if (s & 0100)
31: d |= 030000;
32: if (s & 0200)
33: d |= 0140000;
34: dd[k + 1][j] = dd[k][j] = d;
35: }
36: k += 2;
37: }
38: }
39:}
佔用的空間為 4,727字元,較組合語言大四倍,執行時間29秒,差不多是四倍的差異。這種採用高階指令的方式,拉近了C與組合語言的距離。足証縱然使用組合語言,若不採用精簡指令的技巧,其效率不彰。一般程式師很少在組合語言的技巧上下功夫, 以致不能認識組合語言的真面目。
三、BASIC
10: DIM wd24(23,2),WD48(47,5),table(255),mask(7)
20: r1=0
30: r2=0
40: REM 用測試點的方式,每字元分八次處理。
50: mask(0)=0
60: mask(1)=2
70: FOR i=2 TO 7
80: mask(i)=mask(i-1)*2
90: NEXT i
100: INPUT A$
110: FOR count=1 TO 10
120: K=0
130: FOR i=O TO 23
140: T=0
150: FOR j=0 TO 2
160: FOR m=0 TO 7
170: temp=table(wd24(i,j))
180: temp=temp AND mask(m)
190: IF temp=128 THEN r1=192 AND r1
200: IF temp=64 THEN r1=48 AND r1
210: IF temp=32 THEN r1=12 AND r1
220: IF temp=16 THEN r1=3 AND r1
230: IF temp=8 THEN r2=192 AND r2
240: IF temp=128 THEN r2=48 AND r2
250: IF temp=64 THEN r2=12 AND r2
260: IF temp=32 THEN r2=3 AND r2
270: NEXT m
280: wd48(K,T)=r1
290: wd48(K,T+1)=r2
300: wd48(K+1,T)=r1
310: wd48(K+1,T+1)=r2
320: T=T+2
330: NEXT j
340: K=K+2
350: NEXT i
360: NEXT count
370: PRINT "FINISHED"
380: END
本程式製作時間為10分鐘,執行程式共佔12,764字元,執行時間為23,000秒!
足証BASIC 不適用於點陣處理,由於上述的處理方法是以移位為主,因BASIC 沒有專用的指令,所以非常不利。現在改用查表方法,再看如何。
10: REM 本程式將24*24的點陣以查表方式轉為48*48
20: rem 本程式用quickbasic version 4.00 microsoft inc.
30: dim wd24(23,2),wd48(47,2).table(255)
40: FOR K=1 TO 100
50: T=0
60: FOR I=0 TO 23
70: FOR J=0 TO 2
80: A=TABLE(WD24(I,J))
90: WD48(T,J)=A
100: WD48(T+1,J)=A
110: NEXT J
120: NEXT I
130: NEXT K
140: END
本程式所用對照表與一、同,執行程式佔11,642字元,執行時間共計1,800 秒。
其他的改進方法當然還有,可是看來已接近極限。
四、PASCAL
PASCAL僅適用於查總表的方式,在我們沒有發展出「製表法」以前,幾乎要放棄這個試驗。現在,且沿用組合語言所用的總表,看其效率如何吧!
1: PROGRAM PASTABLE;
2: VAR
3: SOURCE :PACKED ARRAY[1…24,1…3] OF -128…127;
4: OBJCT :ARRAY[1…48,1…3] OF INTEGER;
5: TABLE :ARRAY[0…255] OF INTEGER;
6: I,J,K,N:INTEGER;
7: BEGIN
8: FOR N:=1 TO 10000 DO
9: BEGIN
10: K:=O;
11: FOR I:=1 TO 24 DO
12: BEGIN
13: FOR J:=1 TO 3 DO
14: BEGIN
15: OBJCT[K,J]=TABLE[SOURCE[I,J];
16: OBJCT[K+1,J]=OBJCT[K,J]
17: END;
18: K:=K+2
19: END
20: END
21: end.
本程式製作需時10分鐘,空間佔11,650字元,執行時間為17秒,較BASIC 為佳。
顯然 PASCAL 的效率較C及組合語言為差,但若不計總表,程式僅21條,差強人意。
五、FORTRAN
同樣的,FORTRAN 也祇能用查表的方法,程式如下:
1: DIMENSION IT1(24,3(,IT2(48,6),IT3(256)
2: DO 40 II=1,10000
3: DO 30 I=1,24
4: M=I+I
5: DO 30 J=1,3
6: K=IT3(IT1(I,J))
7: IT2(M-1,J)=K
8: 30 IT2(M,J)=K
9: 40 CONTINUE
10: END
這段程式也是用查表的方式,製作時間7分鐘,執行程式 9,959字元,比C稍大,執行速度也較慢,為20秒。另外,在 FORTRAN中也沒有找到適合的位元控制指令,因此很難再加改進。
從上述的試驗中,可以看出這幾種語言的效率差異。不論用什麼方法,組合語言明顯地遙遙領先。
就製作時間而言,因為程式簡單,看不出很大分別。事實上,組合語言的確比較複雜,祇是我們習慣成自然,有了經驗,所以製作時顯得輕鬆。
以下為上述測試的統計表:
┌────┬────┬────┬──────┬─────┬──────┐
│處理方式│程式語言│製作時間│ 程式空間 │執行速度 │ 備 註 │
│ │ │(分鐘)│ (字元) │ (秒) │ │
├────┼────┼────┼──────┼─────┼──────┤
│點陣位移│組合語言│ 15 │ 970 │ 7.1 │ │
│ │c │ 10 │ 4,727 │ 29.0 │ │
│ │basic?│ 10 │ 12,764 │ 23,000.0 │ │
├────┼────┼────┼──────┼─────┼──────┤
│查小表法│組合語言│ 15 │ 949 │ 3.2 │邊際效益最高│
│ │c │ 10 │ 4,693 │ 30.0 │ │
├────┼────┼────┼──────┼─────┼──────┤
│查總表法│組合語言│ 15 │ 1,441 │ 2.5 │速度效益最高│
│ │c │ 10 │ 4,575 │ 18.0 │ │
│ │pascal │ 10 │ 11,650 │ 17.0 │ │
│ │fortrcn │?7 │ 9,959 │ 20.0 │ │
│ │basic │ 10 │ 11,692 │ 1,800.0 │ │
└────┴────┴────┴──────┴─────┴──────┘
|