暇な人は見てくださいNo.04966
秀まるお さん 01/05/12 17:25
 
 最近、性能が気になって仕方がない状態になってしまいまして、文字列の比較をア
センブリ語で書いてしまいました。

 文字列の比較といっても、例えば「0123」と「123」を同一視するタイプで、しか
も大文字/小文字を区別しないタイプのややこしい文字列比較です。

 せっかく作ったのでここに書き込んでしまいます。(charはすべてunsignedです)

 間違ってたらご指摘を。


static int _stdcall CompareStringSuper( char* psz1, char* psz2 ) {
#if 0
    int n;
    for(;;) {
        if( *psz1 >= '0' && *psz1 <= '9'
         && *psz2 >= '0' && *psz2 <= '9' ) {
            int cchNumber1 = 0, cchNumber2 = 0;
            while( *psz1 == '0' ) {
                psz1 ++;
            }
            while( *psz1 >= '0' && *psz1 <= '9' ) {
                cchNumber1 ++;
                *psz1 ++;
            }
            while( *psz2 == '0' ) {
                psz2 ++;
            }
            n = cchNumber1 - cchNumber2;
            if( n != 0 ) {
                break;
            }
            n = memcmp( psz1 - cchNumber1, psz2 - cchNumber1, cchNumber1 );
            if( n != 0 ) {
                break;
            }
        } else {
            char    ch1 = *psz1;
            if( ch1 >= 'a' && ch1 < 'z' ) {
                ch1 = ch1 - 'a' + 'A';
            }
            char    ch2 = *psz2;
            if( ch2 >= 'a' && ch2 < 'z' ) {
                ch2 = ch2 - 'a' + 'A';
            }
            n = ch1 - ch2;
            if( n != 0 ) {
                break;
            }
            if( ch1 == '\0' ) {
                return 0;
            }
            if( (ch1 >= 0x81 && ch1 <= 0x9F) || ch1 >= 0xE0 ) {
                psz1 ++;
                psz2 ++;
                n = *psz1 - *psz2;
                if( n != 0 ) {
                    break;
                }
            }
            psz1 ++;
            psz2 ++;
        }
    }
    return n;
#endif
    _asm {
        mov     esi, psz1
        mov     edi, psz2
Loop1:
        movzx   eax,[esi]
        cmp     eax,'9'
        ja      NotNumber
        cmp     eax,'0'
        jb      NotNumber
        movzx   ebx,[edi]
        cmp     ebx,'9'
        ja      NotNumber2
        cmp     ebx,'0'
        jb      NotNumber2
                                // 数字同士の場合
        dec     esi             // psz1側での'0'をスキップ
Loop3:
        inc     esi
        mov     al,[esi]
        cmp     al,'0'
        je      Loop3
                            // psz1側での数字の桁数を数える --> eaxに入れる
        mov     eax,-1
        dec     esi
Loop4:
        inc     esi
        inc     eax
        mov     bl,[esi]
        cmp     bl,'0'
        jb      Skip4
        cmp     bl,'9'
        jbe     Loop4
Skip4:
        dec     edi             // psz2側での'0'をスキップ
Loop5:
        inc     edi
        mov     bl,[edi]
        cmp     bl,'0'
        je      Loop5
                            // psz2側での数字の桁数を数える --> ecxに入れる
        mov     ecx,-1
        dec     edi
Loop6:
        inc     edi
        inc     ecx
        mov     bl,[edi]
        cmp     bl,'0'
        jb      Skip6
        cmp     bl,'9'
        jbe     Loop6
Skip6:
        sub     eax,ecx
        jnz     Exit            // 桁数が一致しない時はそこで終わり

        neg     ecx             // ecx = - ecx
        sub     eax,eax
Loop7:
        movzx   eax,[esi+ecx]
        movzx   ebx,[edi+ecx]
        sub     eax,ebx
        jnz     Exit
        inc     ecx
        jnz     Loop7
        jmp     Loop1

NotNumber:
        movzx   ebx,[edi]
NotNumber2:
        sub     eax,ebx
        jz      BigSkip1
        add     eax,ebx
        cmp     eax,'z'
        ja      Skip10
        cmp     eax,'a'
        jb      Skip10
        add     eax,'A'-'a'
Skip10:
        cmp     ebx,'z'
        ja      Skip11
        cmp     ebx,'a'
        jb      Skip11
        add     ebx,'A'-'a'
Skip11:
        sub     eax,ebx
        jnz     Exit
BigSkip1:
        or      ebx,ebx
        jz      Exit        // 全部一致している場合。eaxも0になっているはず。
        cmp     ebx,81h
        jb      NotKanji
        cmp     ebx,0E0h
        jae     Kanji
        cmp     ebx,9Fh
        jbe     Kanji

NotKanji:
        inc     esi
        inc     edi
        jmp     Loop1

Kanji:
        inc     esi
        inc     edi
        movzx   eax,[esi]
        movzx   ebx,[edi]
        inc     esi
        inc     edi
        sub     eax,ebx
        jz      Loop1
        // jmp Exit

Exit:
    }
    // return eax
}

[ ]
RE:04966 暇な人は見てくださいNo.04967
"y.iida" さん 01/05/12 19:08
 
>  最近、性能が気になって仕方がない状態になってしまいまして、文字列の比較をア
> センブリ語で書いてしまいました。

すみません。

今日と明日は、暇なんですけど・・
ニーモニックから遠のいて久しいので自信ないッス(^^;;;

[ ]
RE:04966 暇な人は見てくださいNo.04969
ぱんかず さん 01/05/13 02:44
 
 ぱんかずです。

頭の体操になりました。少々、長いことをお許しください。
#また、なかなかフォローできません。ご了承ください。

> 最近、性能が気になって仕方がない状態になってしまいまして、文字列の比較をア
>センブリ語で書いてしまいました。
>
> 文字列の比較といっても、例えば「0123」と「123」を同一視するタイプで、しか
>も大文字/小文字を区別しないタイプのややこしい文字列比較です。
>
> せっかく作ったのでここに書き込んでしまいます。(charはすべてunsignedです)
>
> 間違ってたらご指摘を。
>
間違ってるかまでは見れてませんが、少々コメントさせてください。

movzx は便利な命令ですが、パイプライン制御などの関係で、遅延が発生する
要因となる場合があり、単純命令の組み合わせで書いた方が良いというような
記述を見たことがあります。(Pentiumの仕様書だったかな?)

それ以下の命令もDWORD比較よりもBYTE比較で済むはずですので、わざわざ
DWORDに拡張しなくても良いのではないでしょうか?
#命令はどれも1クロックですが、定数の分命令長が大きくなります。

あと、cでreturnされるならば、nに戻り値を設定しておく必要があるのでは
ないでしょうか?

>static int _stdcall CompareStringSuper( char* psz1, char* psz2 ) {
>#if 0
>    int n;
>    for(;;) {
>        if( *psz1 >= '0' && *psz1 <= '9'
>         && *psz2 >= '0' && *psz2 <= '9' ) {
>            int cchNumber1 = 0, cchNumber2 = 0;
>            while( *psz1 == '0' ) {
>                psz1 ++;
>            }
>            while( *psz1 >= '0' && *psz1 <= '9' ) {
>                cchNumber1 ++;
>                *psz1 ++;

(1) 引数の値がかわりませんか?

>            }
>            while( *psz2 == '0' ) {
>                psz2 ++;
>            }
>            n = cchNumber1 - cchNumber2;

(2) cchNumber2は0のままではありませんか?
    アセンブラの方は、カウントしていますが…

>            if( n != 0 ) {
>                break;
>            }
>            n = memcmp( psz1 - cchNumber1, psz2 - cchNumber1, cchNumber1 );
>            if( n != 0 ) {
>                break;
>            }
>        } else {
>            char    ch1 = *psz1;
>            if( ch1 >= 'a' && ch1 < 'z' ) {

(3) 'z'が範囲から漏れませんか?

>                ch1 = ch1 - 'a' + 'A';
>            }
>            char    ch2 = *psz2;
>            if( ch2 >= 'a' && ch2 < 'z' ) {

(3)'

>                ch2 = ch2 - 'a' + 'A';
>            }
>            n = ch1 - ch2;
>            if( n != 0 ) {
>                break;
>            }
>            if( ch1 == '\0' ) {
>                return 0;
>            }
>            if( (ch1 >= 0x81 && ch1 <= 0x9F) || ch1 >= 0xE0 ) {
>                psz1 ++;
>                psz2 ++;
>                n = *psz1 - *psz2;
>                if( n != 0 ) {
>                    break;
>                }
>            }
>            psz1 ++;
>            psz2 ++;
>        }
>    }
>    return n;
>#endif
>    _asm {
>        mov     esi, psz1
>        mov     edi, psz2
>Loop1:
>        movzx   eax,[esi]
>        cmp     eax,'9'
>        ja      NotNumber
>        cmp     eax,'0'
>        jb      NotNumber
>        movzx   ebx,[edi]
>        cmp     ebx,'9'
>        ja      NotNumber2
>        cmp     ebx,'0'
>        jb      NotNumber2
>                                // 数字同士の場合
>        dec     esi             // psz1側での'0'をスキップ
>Loop3:
>        inc     esi
>        mov     al,[esi]
>        cmp     al,'0'
>        je      Loop3
>                            // psz1側での数字の桁数を数える --> eaxに入れる
>        mov     eax,-1
>        dec     esi
>Loop4:
>        inc     esi
>        inc     eax
>        mov     bl,[esi]
>        cmp     bl,'0'
>        jb      Skip4
>        cmp     bl,'9'
>        jbe     Loop4
>Skip4:
>        dec     edi             // psz2側での'0'をスキップ
>Loop5:
>        inc     edi
>        mov     bl,[edi]
>        cmp     bl,'0'
>        je      Loop5
>                            // psz2側での数字の桁数を数える --> ecxに入れる
>        mov     ecx,-1
>        dec     edi
>Loop6:
>        inc     edi
>        inc     ecx
>        mov     bl,[edi]
>        cmp     bl,'0'
>        jb      Skip6
>        cmp     bl,'9'
>        jbe     Loop6
>Skip6:
>        sub     eax,ecx
>        jnz     Exit            // 桁数が一致しない時はそこで終わり
>
>        neg     ecx             // ecx = - ecx
>        sub     eax,eax
>Loop7:
>        movzx   eax,[esi+ecx]
>        movzx   ebx,[edi+ecx]
>        sub     eax,ebx
>        jnz     Exit
>        inc     ecx
>        jnz     Loop7
>        jmp     Loop1
>
>NotNumber:
>        movzx   ebx,[edi]
>NotNumber2:
>        sub     eax,ebx
>        jz      BigSkip1
>        add     eax,ebx
>        cmp     eax,'z'
>        ja      Skip10
>        cmp     eax,'a'
>        jb      Skip10
>        add     eax,'A'-'a'
>Skip10:
>        cmp     ebx,'z'
>        ja      Skip11
>        cmp     ebx,'a'
>        jb      Skip11
>        add     ebx,'A'-'a'
>Skip11:
>        sub     eax,ebx
>        jnz     Exit
>BigSkip1:
>        or      ebx,ebx
>        jz      Exit        // 全部一致している場合。eaxも0になっているはず。
>        cmp     ebx,81h
>        jb      NotKanji
>        cmp     ebx,0E0h
>        jae     Kanji
>        cmp     ebx,9Fh
>        jbe     Kanji
>
>NotKanji:
>        inc     esi
>        inc     edi
>        jmp     Loop1
>
>Kanji:
>        inc     esi
>        inc     edi
>        movzx   eax,[esi]
>        movzx   ebx,[edi]
>        inc     esi
>        inc     edi
>        sub     eax,ebx
>        jz      Loop1
>        // jmp Exit
>
>Exit:
>    }
>    // return eax
>}

以下、私が書き換えたものを送ります。
#VCおよびアセンブラを持ち合わせていないため、動作確認できていません。
-------------------------------------------------------------------
   _asm {
        mov     esi, psz1
        mov     edi, psz2
Loop1:
        mov     bl, byte ptr [edi]
        cmp     bl, '9'
        ja      NotNumber2
        cmp     bl, '0'
        jb      NotNumber2
        mov     al, byte ptr [esi]
        cmp     al, '9'
        ja      NotNumber
        cmp     al, '0'
        jb      NotNumber
        jnz     L1              // '0'以外ならば、桁数処理
        xchg    esi, edi
        rep     scasb           // '0'をSKIP
        xchg    esi, edi
L1:                            // psz1側での数字の桁数を数える --> edxに
入れる
        xor     edx, edx
Loop4:
        mov     al, byte ptr [esi]
        cmp     al, '0'
        jb      Skip4
        cmp     al, '9'
        ja      Skip4
        inc     esi
        inc     edx
        jmp     Loop4
Skip4:
        cmp     bl, '0'
        jnz     L2              // '0'以外ならば、桁数処理
        mov     al, bl          // al = '0'
        rep     scasb           // '0'をSKIP
L2:
        xor     ecx, ecx
Loop6:
        mov     bl, byte ptr [edi]
        cmp     bl, '0'
        jb      Skip6
        cmp     bl, '9'
        ja      Skip6
        inc     edi
        inc     ecx
        jmp     Loop6
Skip6:
        mov     eax, edx
        sub     eax, ecx
        jnz     Exit            // 桁数が一致しない時はそこで終わり
        neg     ecx             // ecx = - ecx
        xor     eax, eax
        xor     ebx, ebx
Loop7:
        mov     al, byte ptr [esi+ecx]
        mov     bl, byte ptr [edi+ecx]
        sub     al, bl
        jnz     Exit
        inc     ecx
        jnz     Loop7
        jmp     Loop1

NotNumber:
        mov     bl, byte ptr [edi]
NotNumber2:
        xor     ebx, ebx
        xor     eax, eax
        sub     al, bl
        jz      BigSkip1
        add     al, bl
        cmp     al, 'z'
        ja      Skip10
        cmp     al, 'a'
        jb      Skip10
        add     al, 'A'-'a'
Skip10:
        cmp     bl, 'z'
        ja      Skip11
        cmp     bl, 'a'
        jb      Skip11
        add     bl, 'A'-'a'
Skip11:
        sub     al, bl
        jnz     Exit
BigSkip1:
        or      bl, bl
        jz      Exit        // 全部一致している場合。eaxも0になっている
はず。
        cmp     bl, 81h
        jb      NotKanji
        cmp     bl, 0E0h
        jae     Kanji
        cmp     bl, 9Fh
        jbe     Kanji

NotKanji:
        inc     esi
        inc     edi
        jmp     Loop1

Kanji:
        inc     esi
        inc     edi
        mov     al, byte ptr [esi]
        mov     bl, byte ptr [edi]
        inc     esi
        inc     edi
        sub     eax, ebx
        jz      Loop1
        // jmp Exit

Exit:
        mov     dword ptr [n], eax      // cのreturn文でnから値を取得す
るため
    }
    // return eax

[ ]
RE:04969 暇な人は見てくださいNo.04971
秀まるお さん 01/05/13 16:30
 
 ぱんかずさんバージョンを細かい所までは見てないですが、C言語関数の復帰値は
eaxで返されるので、int nと宣言して値を返さなくても、eaxに値を入れてそのまま
returnすれば手っ取り早いです。(コンパイル時にWarningが出ますけど)

 あと、文字の大小比較自体はバイトでやってもいいんですけど、結果をDWORDで返
すためにはDWORDで大小比較(引き算)した方が何かと都合がいいです。

 例えば、\x01と\xFFを比較する場合、alに\x01、blに\xFFが入っていて、

   sub al,bl

 としたのでは、結果が\x02となって、それをそのままDWORDに拡張すると\x02とな
って、> 0となって都合が悪いです。

 eaxに1、ebxにFFが入っている場合なら、

   sub eax,ebx

 でeaxにたしかに負の数が入って、そのままreturnすれば済みます。

 あと、ご指摘のmovzx命令ですが、googleで検索したら、たしかに遅いと書いてあ
りました。

    xor eax,eax
    mov al,memory

 した方が速いそうな。直します。

[ ]