古い問題の新しい解決策

画像

または、自転車をジェット推進に移します


情報交換のための米国標準法の年齢に等しい年齢の非常に古いタスクが1つあります。 より具体的には、整数をASCII文字列の16進表現に変換するタスクです。

この出版物では、先行ゼロを切り捨てることなく、符号なし64ビット整数を固定長の文字列に変換することを検討します。

一見したところ、このタスクは初歩的です。 ASCIIテーブルが異なる場合はそうなります。 しかし、我々は持っているものを持っています。

すべてのソリューションは、IA-32およびIntel 64アーキテクチャ専用です。



この問題を解決するための入出力データとアルゴリズムを検討してください。

ログイン:

低から高までのアドレスで8バイトを占有する64ビットの符号なし整数。 番号の数字は同じ順序です。 各ビットは4ビットで、各バイトには2ビットあります。

出力:

それぞれが1バイトを占めるASCII文字列。 各バイトは元の数字の1桁を表します。 文字の順序は最初のバイト(最下位アドレス)-元の数値の最上位ビットです。



アルゴリズム:

1)古いノートブックから新しいノートブックに移動する

2)ノートブックを取り出し、拡張子がゼロのバイトに変換します。

3)30時間追加

4)値が39hを超える場合

4.1)さらに17(10進数)を追加します

4.2)5へ

4.3)それ以外の場合は、5に進みます

5)受信したバイトを文字列に保存します

6)すべてのノートブックが処理されているわけではありませんが、2に進みます



ソリューションNo. 1

mov cx,8 mov si,value mov di,hexstr add si,cx ;highest byte of value dec si next_tetrade: std lodsb mov bl,al and al,0fh call digit cld stosb mov al,bl shr al,4 call digit loop next_tetrade ret digit: add al,30h cmp al,39h jnb _zero-nine ;digit greater than 9 add al,11h _zero-nine: ret
      
      







額のほとんどの決定と同様に、それは美しさでもスピードでも区別されません。 ホームのugさは、1つのコマンドだけをバイパスする条件付き遷移です。 美しさをもたらすには2つの方法があります。

1つは、AAA AAD 17コマンドのシーケンスとして古代の「魔法」を使用することです。

決定番号2

AAA AAD 17
 mov cx,8 mov si,value mov di,hexstr add si,cx ;highest byte of value dec si next_tetrade: std lodsb mov bl,al and al,0fh call digit cld stosb mov al,bl shr al,4 call digit loop next_tetrade ret digit: mov ah,30h aaa aad 11h ret
      
      







別の方法は、XLATBコマンドを使用することです。

決定3

Xlatb
  mov cx,8 mov si,value mov di,hexstr mov bx,hextable add si,cx dec si m3: std lodsb mov ah,al and al,0fh xlatb cld stosb mov al,ah shr al,4 xlatb stosb loop m3 ret hextable db "0123456789ABCDEF"
      
      







どちらのソリューションもほぼ同じです。 ただし、ソリューションNo. 2は64ビットプロセッサモードでは機能しません。これは、AAAおよびAADコマンドのサポートが廃止されているためです。

しかし、一度に8バイトを処理することは本当に可能ですか?一度に4ビットだけを処理することを受け入れますか?

9(1001)を39h(00111001)に、A(1010)を41h(01000001)に変える方法はありますか?

AAA AADチームのペアの本質を明らかにし、それらの代替チームを選択してみましょう。

AAA AADの交換
  mov bl,0ah xor ah,ah div bl ; al - , ah -. ,     9    1. mov bh,al ; shl al,4 ;       11h add al,bh add al,30h ; voila!
      
      







もちろん、このコードは私たちの目的には適していませんが、貴重な情報を提供します。 9より大きい数の転送単位を取得できることを示しています。また、この単位を後で使用する方法も示しています。 現在、各ノートブックを1バイトに変換できる場合、これらのバイトを並列処理できます。 一度に8桁!

raxに元の番号が含まれるようにします。 若いノートブックを分離するには、マスクと組み合わせて実行するだけです。 そして、古いノートブックをなくさないように、それらをrbxにコピーします。

パック解除
  mov rdx,0f0f0f0f0f0f0f0fh mov rbx,rax and rax,rdx shr rbx,4 and rbx,rdx
      
      







残念ながら、分割によって希望する転送を取得することはできません。 他の方法はありますか?

実際には、基本:10h-0ah = 6。 各バイトに6を追加すれば十分であり、シニアノートブックに必要なユニットを取得します。

キャリー
  mov rdx,0f0f0f0f0f0f0f0fh mov rcx,0606060606060606h mov rbx,rax and rax,rdx mov rdi,rax ;   shr rbx,4 and rbx,rdx add rax,rcx shr rax,4 and rax,rdx ;    ,     9,  .   - .
      
      







除算の残りの代わりに除算を使用して転送単位を取得する以前の方法とは対照的に、元の図が残っています。 つまり、番号Aがあった場合、そこに残り、0にはなりません。したがって、11hではなく、41h-3ah = 7を追加する必要があります。

そして今、次のタスクが発生します。1つから7つを作る方法は? はい、隣接するバイトに影響を与えないようにします。 結局、7 = 0111bです。つまり、2つの左シフトと2つの論理和で必要なものを取得できるということです。

1〜7
  mov rsi,rax shl rsi,1 or rax,rsi ;11b shl rsi,1 or rax,rsi ; 111b
      
      







ピースをまとめて、何が起こるか見てみましょう

hex ver 0.1に署名なし
  mov rax,[value] mov rdx,0f0f0f0f0f0f0f0fh mov rcx,0606060606060606h mov rbx,rax mov rbp,3030303030303030h shr rbx,4 and rax,rdx and rbx,rdx mov rdi,rax mov r9,rbx add rax,rcx add rbx,rcx shr rax,4 shr rbx,4 and rax,rdx and rbx,rdx mov rsi,rax mov r8,rbx shl rsi,1 shl r8,1 or rax,rsi or rbx,r8 shl rsi,1 shl r8,1 or rax,rsi or rbx,r8 add rax,rbp add rbx,rbp add rax,rdi add rbx,r9 mov [hexstr],rax mov [hexstr+8],rbx
      
      







このコードをコンパイルして実行すると、1つの問題が明らかになります。行内の数字の順序が正しくありません。 最初に、数字が前後に移動し、次に、偶数と奇数が一緒にグループ化されます。 もちろん、結論を出すこともできますが、私たちは正直であり、まだ整理されています。

インターリーブ解除
  mov rcx,4 highpart: rol rbx,8 shrd [hexstr],rbx,8 rol rax,8 shrd [hexstr],rax,8 loop highpart mov rcx,4 lowpart: rol rbx,8 shrd [hexstr+8],rbx,8 rol rax,8 shrd [hexstr+8],rax,8 loop lowpart ret
      
      







彼らが去りたかったことから、彼らはそれに来ました。 再び64モードでのバイト処理。 バイトを反転するために、それらを後方に置くために、Intelはずっと前にbswapコマンドを作成しました。 しかし、インターレース解除のためには、MMX、SSE、およびそれらの開発に目を向ける必要があります。 そして、そのようなチームがあり、その名前はpunpcklbwです。 発見を使用します。

デインターリービング版 2
  bswap rax bswap rbx mov [hexstr],rax mov [hexstr+8],rbx movdqu xmm0,[hexstr] movdqu xmm1,[hexstr+8] punpcklbw xmm1,xmm0 movdqu [hexstr],xmm1
      
      







停止停止停止。 SSEの使用を開始した場合、他に便利なものがありますか? コードを完全にSSEで書き直してください。

hex ver 1.1に署名なし
  movdqu xmm0,[value] pxor xmm1,xmm1 punpcklbw xmm0,xmm1 movdqa xmm1,xmm0 pand xmm1,[efes] psllq xmm0,4 pand xmm0,[efes] por xmm0,xmm1 movdqa xmm1,xmm0 paddb xmm1,[sixes] psrlq xmm1,4 pand xmm1,[efes] pxor xmm9,xmm9 psubb xmm9,xmm1 pand xmm9,[sevens] paddb xmm0,xmm9 paddb xmm0,[zeroes] movdqu [hexstr],xmm0 mov rax,[hexstr] mov rbx,[hexstr+8] bswap rax bswap rbx mov [hexstr],rbx mov [hexstr+8],rax ret efes: dq 0f0f0f0f0f0f0f0fh dq 0f0f0f0f0f0f0f0fh zeroes: dq 3030303030303030h dq 3030303030303030h sixes: dq 0606060606060606h dq 0606060606060606h sevens: dq 0707070707070707h dq 0707070707070707h
      
      





ここで、開梱を簡素化し、7の編成を1つの減算と1つの連結の助けを借りて、異なる方法で編成しました。



しかし、私たちは何に勝ちましたか? 各メソッドのパフォーマンスを比較します。

CPU 1 2 3 4 5 6 7 8
Core 2 Quad Q8200 670 600 150 77 70 77 76 1170
AMD C-60 290 195 290 120 105 120 140 290


  1. Londb stosb with jnb
  2. Lodsb stosb with xlatb
  3. shrdを使用した汎用レジスター
  4. punpckを使用した汎用レジスター
  5. SSE 64ビット
  6. SSE 64ビット非整列
  7. SSE 128ビット
  8. xlatb 128ビットのLodsb stosb


オウムの値は、プロセッサティックの平均数です。

Intelの数値が理論上うまく適合する場合、AMDの数値はやや不思議です。 嬉しい驚きは、Intelのプロセッサ上の高速SSEコードでした。 64ビットモードにはまだ多くの空きxmmレジスタがあるため、必要な時間をわずかに増やすことで、変換された数値のビット深度を256ビットに安全に増やすことができます。 一般的に、最初はシーケンシャルなタスクのように見えますが、非常に並列な方法を解決することが可能になりました。

16進数の文字列を数値に変換するという逆の問題は、それほど面白くは解決されません。



スナックの場合:

SSE 128ビット
  movdqa xmm0,[value] pxor xmm1,xmm1 movdqa xmm2,xmm0 punpcklbw xmm0,xmm1 movdqa xmm1,xmm0 punpckhbw xmm2,xmm1 pand xmm1,[efes] movdqa xmm3,xmm2 psllq xmm0,4 pand xmm3,[efes] pand xmm0,[efes] psllq xmm3,4 por xmm0,xmm1 pand xmm2,[efes] movdqa xmm1,xmm0 por xmm2,xmm3 paddb xmm1,[sixes] movdqa xmm3,xmm2 psrlq xmm1,4 paddb xmm3,[sixes] pand xmm1,[efes] psrlq xmm3,4 pxor xmm9,xmm9 pand xmm3,[efes] psubb xmm9,xmm1 pxor xmm10,xmm10 pand xmm9,[sevens] psubb xmm10,xmm3 paddb xmm0,xmm9 pand xmm10,[sevens] paddb xmm0,[zeroes] paddb xmm2,xmm10 movdqa [hexstr],xmm0 paddb xmm2,[zeroes] mov rax,[hexstr] movdqa [hexstr+16],xmm2 mov rbx,[hexstr+8] mov rcx,[hexstr+16] bswap rax mov rdx,[hexstr+16+8] bswap rbx bswap rcx mov [hexstr],rbx bswap rdx mov [hexstr+8+16],rax mov [hexstr+8],rcx mov [hexstr+16],rdx ret
      
      






All Articles