[過去ログ] Visual Studio 2008 Part 22 (314レス)
1-

このスレッドは過去ログ倉庫に格納されています。
次スレ検索 歴削→次スレ 栞削→次スレ 過去ログメニュー
211
(1): 2018/09/16(日)13:21 ID:haV9TZ8e(2/12) AAS
>>209
>命令自体は rdtsc と同じで非同期に実行されている雰囲気だが、
>rdtsc命令の注意書きにある「シリアル化命令ではない」という但し書きが無く、状況は不明。
>正直、正しく読み出せているか怪しい。(あてにならない)

インラインアセンブラを使わずに、

_control87(), _controlfp() : Get and set the floating-point control word.

unsigned int _control87( unsigned int new, unsigned int mask );
unsigned int _controlfp( unsigned int new, unsigned int mask );

を使ってみたらどうなる?
212
(1): 2018/09/16(日)13:31 ID:Q5j4SiHR(1/2) AAS
win32コンソールなら結果が同じ。 もう理由は分かったのに何が問題なんだ?こんなの何の影響もないだろう。
213
(1): 2018/09/16(日)13:33 ID:zL1WUjLu(4/27) AAS
>>198
再現実験ありがとう。
しかし色々問題がある。

1. 俺は起動方法による違いについてフォーカスしているが、
 君はRelease/Debugの違いにフォーカスしている。
2. VC++2008では再現しない。(VC++2010では再現する)
3. ソース改変しすぎ。それでは意味がない。
4. >>206の結論は間違い。

まず問題なのはソースの改変だ。
ループ回数を16回と決め打ちしたことで 8*2 に展開されている。
省14
214: 2018/09/16(日)13:33 ID:zL1WUjLu(5/27) AAS
>>198
問題は、俺の環境で俺が提供したコード>>191だと、
同様に展開されないにも関わらず、『起動方法によって』結果が異なってしまう点だ。
俺の環境でのRelease/Debugの逆アセンブル結果のdiffは以下。
17c17
< 0000000c cmp dword ptr ds:[001C2E14h],0
---
> 0000000c cmp dword ptr ds:[00702E14h],0
19c19
< 00000015 call 68302BA9
省21
215: 2018/09/16(日)13:53 ID:haV9TZ8e(3/12) AAS
>>213
なるほど、全く別の2つの理由で、精度が変わっている可能性(というより多分、確実)があると。

それは以下の2つ:

1. Debug版とRelease版では、最適化の結果、x87 FPU命令の使われ方が変わる。
  x87では、メモリに書き戻さずに st(0)〜st(7)レジスタに入っている途中では、
 拡張倍精度の80BITで計算されるが、書き戻すとdoubleの場合でも64BITに丸め
 られる。なるべくメモリに書き戻さずにレジスタった方が高速なので、Release版
 では、80BITで計算される「期間」が多くなる。そのため、Debug版とRelese版では
 結果が僅かに違ってくる。

2. fpu control word が違っていて、st(0)〜st(7)に入っていても、計算が
省5
216: 2018/09/16(日)13:58 ID:haV9TZ8e(4/12) AAS
まず、
__asm{
 fnstcw [cw];
}
ではなく、_control87() を使ってみて欲しい。

インラインアセンブラは、独立した *.asm で書くより危険な場合が
あるかも知れないので。特にC関数の引数、今の場合は、「cw」を
インライン・アセンブラで用いた場合、正しいコードが出ているかどうか
は注意が必要。
217: 2018/09/16(日)14:05 ID:haV9TZ8e(5/12) AAS
>>208
よく見ると、それは、かなり複雑な事情が絡みそうなコード。
以下のようにした方が安心。なお、「cw」という短すぎる引数名
も長年のプログラミング経験からすると、インラインアセンブラでは
怖い。また、

TTTT reg,引数名

TTTT 引数名
は大丈夫でも、
TTTT reg,[引数名]
省14
218
(4): 2018/09/16(日)14:17 ID:haV9TZ8e(6/12) AAS
あ、後、インライン・アセンブラで実験する場合は、関数名の inline は
「取った」方がいい。つまり、以下の方が安心:

#pragma unmanaged
void fpu_getcw(unsigned short *pCW) {
 __asm{
  mov edx,pCW
  fnstcw [edx];
 }
}
#pragma managed
219
(2): 2018/09/16(日)15:23 ID:h8nMbN0G(1) AAS
また基本に戻るが、>>192で/MDになってるので
/MTや/MTdでも発生するかしてみた方がいい
220
(1): 2018/09/16(日)15:37 ID:Q5j4SiHR(2/2) AAS
.netはx87コンテキストをすべて保持しませんって分かったんだからもう十分。
win32かx64にすれば解決。そもそも問題になる仕様バグじゃない。
221
(1): 2018/09/16(日)15:40 ID:haV9TZ8e(7/12) AAS
>>219
ホントだ。/MDだと、Runtime Library として DLL のものを使ってしまう。
これは、今回の現象に非常に重要な影響を与えているかも知れない。
222: 2018/09/16(日)15:42 ID:haV9TZ8e(8/12) AAS
>>220
いや、まだまだ興味深い。
ここで終わらせずにもっと徹底して追及すべきだ。
223: 2018/09/16(日)15:48 ID:zL1WUjLu(6/27) AAS
>>211
それはどうやらclrでは使えないらしい。
> These functions are ignored when you use /clr (Common Language Runtime Compilation) or /clr:pure to compile
> because the common language runtime (CLR) only supports the default floating-point precision.
> 外部リンク[aspx]:msdn.microsoft.com

とはいえ無理矢理やってみた。警告は出るがコンパイルは通る。
結果は、どこに置いても、Debug/Releaseでも、常に 0x9001f が読み出される。
ただし、これは上記の仕様からして、当てにならない。
224
(1): 2018/09/16(日)15:49 ID:zL1WUjLu(7/27) AAS
>>218
218のコードで試してみた結果、209で言った不安定さはなくなり、
全てにおいて 0x027f が安定して読み出せるようになった。

ただしその過程で気づいたが、
IDEから起動した場合はReleaseビルドであっても、「未初期化のスタック値」も0x00が読み出せるようだ。
どうやらこれが原因の可能性が出てきた。(はっきり言って俺のバグだが)

コードは以下の通りだが、
unsigned short fpu_cw, fpu_cw_after;
// fpu_getcw(&fpu_cw);
double norm = calc_norm_and_regulate(count, inputs, false);
省12
225
(2): 2018/09/16(日)15:51 ID:zL1WUjLu(8/27) AAS
>>218
なお、逆アセンブルでコードバイトを表示させて確かめることは出来る。
正しいコードは出ている。(ただし不安定)
inline void fpu_getcw(unsigned short* cw) {
00DA1540 55 push ebp
00DA1541 8B EC mov ebp,esp
__asm{
fnstcw [cw];
00DA1543 D9 7D 08 fnstcw word ptr [cw]
}
省20
226: 2018/09/16(日)15:51 ID:zL1WUjLu(9/27) AAS
>>218
218のコードだと、
00381002 EC in al,dx
__asm{
mov edx,pCW
00381003 8B 55 08 mov edx,dword ptr [pCW]
fnstcw [edx];
00381006 D9 3A fnstcw word ptr [edx]
}
}
省4
227
(1): 2018/09/16(日)16:02 ID:haV9TZ8e(9/12) AAS
>>225
をを。やはり、ある意味ではVCが間違ったアセンブリコードを出していたよ。
それだと、
fnstcw [EBP+08]
という意味になってしまって、
fnstcw pCW
の意味になっている。つまり:
pCW = control_word;
あなたが、やりたいのは、
*pCW = control_word;
省9
228
(2): 2018/09/16(日)16:06 ID:zL1WUjLu(10/27) AAS
すまん、間違いの修正

>>224
× > どうやらこれが原因の可能性が出てきた。(はっきり言って俺のバグだが)
× > まあこれに当たっているのなら確実に俺のバグだし、これなら辻褄は合ってしまうのだが。

今回は俺はあくまで俺の本番コードのデバッグを念頭に置いていて、この発言だった。
ただし>>191の再現コードで『不定スタック領域』を掴んでいるわけもなく、
一応IDE起動とコマンドプロンプト起動での挙動の違いを再現出来ているわけだから、
これだけが問題ではないのも事実だ。

俺にとっては一つ新しい知見として、
・IDEから起動した場合、スタックが初期化されるっぽい
省6
229
(1): 2018/09/16(日)16:11 ID:haV9TZ8e(10/12) AAS
>>225
>正直、/7の意味が分からないのだが、
ModRM とは、

mod reg r/m
76 543 210

のようなオペランドを指しているのだけど、/7 は、regの部分を2進数の111、
10進数の「7」にするという意味。このタイプのマシン語は、
mod ttt r/m
とも書かれる。tttの部分は、命令の主幹部分(ニモニック部分)によって変わる。
普通は、レジスタ番号を入れるところに、命令の種類を表す3BITの値を入れる
省12
230
(1): 2018/09/16(日)16:17 ID:haV9TZ8e(11/12) AAS
>>228
>ただし>>191の再現コードで『不定スタック領域』を掴んでいるわけもなく、
>一応IDE起動とコマンドプロンプト起動での挙動の違いを再現出来ているわけだから、
>これだけが問題ではないのも事実だ。

そうだよ。精度が変わるのはあなたの間違いではない。スタック領域が0クリア
されようがれまいが、あなたのコード自体には特に不安定さはない。
非初期化領域を参照しているコードは見当たらないし。
231
(3): 2018/09/16(日)16:20 ID:haV9TZ8e(12/12) AAS
逆アセンブラ結果を見てないで言うけど、もし、sqrt() が call文で関数呼び出し
されているんだったら、そこで精度の違いが出てるかもしれない。
232
(1): 2018/09/16(日)16:23 ID:zL1WUjLu(11/27) AAS
>>227
なるほど、了解した。
つまり、>>209は全面的に間違いで、正しくは、

・fpu control register は 0x027F で、IDEからも正しく読めている

だな。

俺がやるべきだったのは fnstcw [[cw]] なのだと思うが、これはSyntaxErrorだ。
そして、こんな命令はないから、
[]内に変数を書かず、レジスタ名にしろ、ということだったのだな。
全くもって了解だ。

VCの問題ではなくて、
省4
233
(1): 2018/09/16(日)16:35 ID:LrdaMWHl(1/5) AAS
>>232
>俺がやるべきだったのは fnstcw [[cw]] なのだと思うが、これはSyntaxErrorだ。

ちょっと違う。あなたはやるべきことをちゃんと正しく、
fnstcw [cw]
と書いた。しかし、cw=[ebp+8]なので、これは、
fnstcw [[ebp+8]]
という「意味」になる。でも、x86/x64のマシン語にはこんな[ ]を二重にした
オペランドは存在しないので、VCが無断で勝手に[ ]を一重にして、
fnstcw [ebp+8]
に改変してしまった。
省4
234
(1): 2018/09/16(日)16:36 ID:zL1WUjLu(12/27) AAS
>>229-230
了解だ。ありがとう。

>>231
その部分の逆アセンブラは以下の通り。
普通にcallされている。(行数オーバーなので切るが)

ただし、
> そこで精度の違いが出てるかもしれない
との繋がりがよくからない。
sqrt()でcallされると、スタックが改変される。おそらくデータ依存か?
なら未初期化のスタックを掴みに行っているコードが有ればバグる。
省2
235
(4): 2018/09/16(日)16:37 ID:zL1WUjLu(13/27) AAS
>>231
逆アセンブラ

for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i];
00000033 33 D2 xor edx,edx
00000035 89 55 E8 mov dword ptr [ebp-18h],edx
00000038 90 nop
00000039 EB 03 jmp 0000003E
0000003b FF 45 E8 inc dword ptr [ebp-18h]
0000003e 8B 45 E8 mov eax,dword ptr [ebp-18h]
00000041 3B 45 FC cmp eax,dword ptr [ebp-4]
省19
236
(1): 2018/09/16(日)16:37 ID:zL1WUjLu(14/27) AAS
>>231
逆アセンブラ(続き)

if (regulate) for (int i=0;i<num;i++) r[i] = (T)(r[i]/norm);
00000078 0F B6 45 08 movzx eax,byte ptr [ebp+8]
0000007c 85 C0 test eax,eax
0000007e 74 25 je 000000A5
00000080 33 D2 xor edx,edx
00000082 89 55 EC mov dword ptr [ebp-14h],edx
00000085 90 nop
00000086 EB 03 jmp 0000008B
省14
237
(2): 2018/09/16(日)16:40 ID:LrdaMWHl(2/5) AAS
>>234
よく見ると、最小(?)の実験コードでは sqrt() が使われていなかった。
スマン。
238: 2018/09/16(日)16:42 ID:zL1WUjLu(15/27) AAS
>>233
ああ、なるほど、了解。
239
(1): 2018/09/16(日)16:49 ID:zL1WUjLu(16/27) AAS
>>237
いや、俺が提供した>>191のソースなら使われてるぞ。
>>200のソースでは使われてないが。

ただまあ、彼(200)がsqrtを落としたのも分からなくはない。
誤差が生じる=通常は桁落ちだから、この場合は当然積和部分が怪しい。
あらかじめ彼はそうなると分かっていてそれを落とし、予定調和的な結論にたどり着いてしまった。
それが彼の間違いだった、ということ。

俺は出来るだけ元のソースのままで追跡しようとしている。
元のソースの該当ケースと離れてしまっては意味がないから。
そして元ソースではsqrtを使っている。
240
(8): 2018/09/16(日)16:53 ID:/oSJzlqn(1/5) AAS
たぶん2008の最適化ミスだと思う。
static double norm = 0;// ←"static"を追加する
にするとか、最適化オプションをいじると
Release/コマンドプロンプトからの起動でも
0x1ff68ddfb62221ddになる
1-
あと 74 レスあります
スレ情報 赤レス抽出 画像レス抽出 歴の未読スレ AAサムネイル

ぬこの手 ぬこTOP 0.031s