0

最初は、これは部分的に議論し、部分的に解決するような質問かもしれません。そこに誰かを怒らせる意図はありません。

64 ビット アセンブリで、64 ビット用の MT Prime ベースの乱数ジェネレーターを生成するアルゴリズムを記述しました。このジェネレーター関数は、サイズ 2048x2048x2048 の配列を作成し、1..small_value (通常は 32) の間でランダムな no を生成するために、80 億回呼び出される必要があります。

これで、次の 2 つのステップの可能性がありました。

(a) 数値を生成し続け、制限 [1..32] と比較し、収まらないものを破棄します。このロジックの実行時間は、clock() 関数を呼び出して測定すると 181,817 ミリ秒です。

(b) RAX で 64 ビットの乱数出力を取得し、FPU を使用して [0..1] の間になるようにスケーリングしてから、目的の範囲 [1..32] にスケーリングします。これのコード シーケンスは次のとおりです。下 :

 mov word ptr initialize_random_number_scaling,dx
 fnclex             ; clears status flag
 call generate_fp_random_number ; returns a random number in ST(0) between [0..1]
 fimul word ptr initialize_random_number_scaling ; Mults ST(0) & stores back in ST(0)
 mov word ptr initialize_random_number_base,ax ; Saves base to a memory
 fiadd word ptr initialize_random_number_base  ; adds the base to the scaled fp number
 frndint                            ; rounds off the ST(0)
 fist word ptr initialize_random_number_result ; and stores this number to result.
 ffree st(0)               ; releases ST(0)
 fincstp                       ; Logically pops the FPU
 mov ax, word ptr initialize_random_number_result       ; and saves it to AX

そして、generate_fp_random_number の手順は次のとおりです。

 shl rax,1  ; RAX gets the original 64 bit random number using MT prime algorithm
 shr ax,1   ; Clear top bit
 mov qword ptr random_number_generator_act_number,rax ; Save the number in memory as we cannot move to ST(0) a number from register
 fild   qword ptr random_number_generator_max_number    ; Load 0x7FFFFFFFFFFFFFFFH
 fild   qword ptr random_number_generator_act_number    ; Load our number
 fdiv   st(0),st(1) ; We return the value through ST(0) itself, divide our random number with max possible number
 fabs
 ffree st(1)    ; release the st(1)
 fld1           ; push to top of stack a 1.0
 fcomip st(0), st(1)    ; compares our number in ST(1) with ST(0) and sets CF.
 jc generate_fp_random_get_next_no ; if ST(0) (=1.0) < ST(1) (our no), we need a new no
 fldz               ; push to top of stack a 0.0
 fcomip st(0),st(1) ; if ST(0) (=0.0) >ST(1) (our no) clears CF
 jnc generate_fp_random_get_next_no ; so if the number is above zero the CF will be set
 fclex

問題は、これらの命令を追加するだけで、実行時間がなんと 5,633,963 ミリ秒に跳ね上がることです。代わりに xmm レジスタを使用して上記を書きましたが、違いはまったくありません。(5,633,703 ミリ秒)。

これらの追加の命令が合計実行時間に与える負荷の程度について、誰か親切に教えてくれませんか? FPUは本当にこれほど遅いのですか?それとも私はトリックを逃していますか?いつものように、すべてのアイデアを歓迎します。あなたの時間と努力に感謝します。

Env : 4.4 GHz にオーバークロックされた Intel 2700K CPU 上の Windows 7 64 ビット VS 2012 Express 環境でデバッグされた 16 GB RAM

4

2 に答える 2

0

あなたのコードには、理由がわからないものがたくさんあります。理由があれば、遠慮なく訂正してください。

generate_fp_random_number の場合

shl rax, 1
shr rax, 1
mov qword ptr act_number, rax
fild qword ptr max_number
fild qword ptr act_number
fdivrp   ; divide actual by max and pop
; and that's it. It's already within bounds.
; It can't be outside [0, 1] by construction.
; It can't be < 0 because we just divided two positive number,
; and it can't be > 1 because we divided by the max it could be

他のことについて:

mov word ptr scaling, dx
mov word ptr base, ax
call generate_fp_random_number
fimul word ptr scaling
fiadd word ptr base
fistp word ptr result  ; just save that thing
mov ax, word ptr result
; the default rounding mode is round to nearest,
; so the slow frndint is unnecessary

また、 などの完全な欠如にも注意してくださいffree。正しい命令をポップにすることで、すべてうまくいきました。通常はそうです。

于 2013-05-25T08:20:53.133 に答える