上一頁 目錄 下一頁

附錄二 程式語言效率分析


附錄二    程式語言效率分析

    以下為利用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 │            │
 └────┴────┴────┴──────┴─────┴──────┘

上一頁 目錄 下一頁