组合语言之艺术8

    技术2022-05-11  118

    附录二    程式语言效率分析

        以下为利用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 i100: INPUT A$110: FOR count=1 TO 10120: K=0130: FOR i=O TO 23140: T=0150: FOR j=0 TO 2160: FOR m=0 TO 7170: temp=table(wd24(i,j))180: temp=temp AND mask(m)190: IF temp=128 THEN r1=192 AND r1200: IF temp=64 THEN r1=48 AND r1210: IF temp=32 THEN r1=12 AND r1220: IF temp=16 THEN r1=3 AND r1230: IF temp=8 THEN r2=192 AND r2240: IF temp=128 THEN r2=48 AND r2250: IF temp=64 THEN r2=12 AND r2260: IF temp=32 THEN r2=3 AND r2270: NEXT m280: wd48(K,T)=r1290: wd48(K,T+1)=r2300: wd48(K+1,T)=r1310: wd48(K+1,T+1)=r2320: T=T+2330: NEXT j340: K=K+2350: NEXT i360: NEXT count370: 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)=A100:       WD48(T+1,J)=A110:   NEXT J120:      NEXT I130:  NEXT K140: 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 │            │ └────┴────┴────┴──────┴─────┴──────┘

     


    最新回复(0)