しつこく・・・
いいかげんやめたらいいのに、と自分でも思うのですが、改善すべき点があるとなるともう少し粘りたくなるので。
上で"ntohl(b[i]) - ntohl(s[i])"を返すようにしたのですが、どうせなら減算までを1つの関数として実行すれば関数呼び出しの手間が少なくなっていいのでは?と。で、とりあえず見よう見まねでアセンブラ。
proc @_ntohl$qii push ebp mov ebp,esp push ebx mov ebx,[ebp+8] bswap ebx mov eax,[ebp+12] bswap eax sub eax,ebx pop ebx pop ebp endproc
2変数をそれぞれエンディアン変換し、引数1から引数2を減算したものを戻り値とする関数_ntohl()。これで2文字目以降においてはmemcmp()より速くなりました(2文字目で17秒対22秒)。ちなみにさっきまでのでは28秒を要しています。
アセンブラには不慣れなので、実は間違っていたりするかもしれない(滝汗
本当はinline化したいところだけど、TASM持っていないし全てをアセンブラでやる気はないし・・・。
更にもうちょっと
x86はLittleEndianなのでintやshortで比較すると4Byte目の大小から優先されて戻り値の正負がstrncmpと異なりうるという問題が・・・。
Endian変換にはbswap命令を使えばいいわけですが、Cから直接扱う方法を知らないので保留。元々等しいかどうかの確認のために始めたわけだし、と言い訳(ぉ
大小関係を比較するような用途には要注意。
そうは言っても誤用されたりしたら困るので差し替えてみる。
↓昨日最後のもの
int _strncmp(void *buf, void *str, size_t size){ int *b = (int *)buf; int *s = (int *)str; unsigned int k = size >> 2; for(unsigned int i=0;i<k;i++){ if( (b[i])^(s[i]) ){ return (b[i] - s[i]); } } k <<= 1; short *sb = (short *)buf; short *ss = (short *)str; if( (size & 2) && ( (sb[k])^(ss[k]) ) ){ return (sb[k] - ss[k]); } k <<= 1; char *cb = (char *)buf; char *cs = (char *)str; if( (size & 1) && ( (cb[k])^(cs[k]) ) ){ return (cb[k] - cs[k]); } return 0; }
↓Endian変換挿入後
int _strncmp(void *buf, void *str, size_t size){ int *b = (int *)buf; int *s = (int *)str; unsigned int k = size >> 2; for(unsigned int i=0;i<k;i++){ if( (b[i])^(s[i]) ){ return (ntohl(b[i]) - ntohl(s[i])); } } k <<= 1; short *sb = (short *)buf; short *ss = (short *)str; if( (size & 2) && ( (sb[k])^(ss[k]) ) ){ return (ntohs(sb[k]) - ntohs(ss[k])); } k <<= 1; char *cb = (char *)buf; char *cs = (char *)str; if( (size & 1) && ( (cb[k])^(cs[k]) ) ){ return (cb[k] - cs[k]); } return 0; }
おそらくこれで正しく動作すると思います(未確認(汗
追記:200文字目で異なる場合、50秒対68秒と4割ほど減速。memcmp()よりは早いけど。ntohl()/ntohs()のオーバーヘッドがかなり大きいらしく数文字の比較のみで片が付く場合はmemcmp()にすら惨敗します(汗