X



Visual Studio 2008 Part 22

■ このスレッドは過去ログ倉庫に格納されています
0001デフォルトの名無しさん
垢版 |
2014/10/13(月) 15:13:20.18ID:yFRqmPNp
■公式
ttp://www.microsoft.com/japan/msdn/vstudio/default.aspx

■前スレ
Visual Studio 2008 Part 21
http://peace.2ch.net/test/read.cgi/tech/1290969016/

■リンク
Visual Studio 2008に搭載された17の新機能
ttp://www.atmarkit.co.jp/fdotnet/special/visualstudio2008_01/visualstudio2008_01_01.html
5000個のバグと戦った、MSが「Visual Studio 2008」RTM出荷
ttp://www.atmarkit.co.jp/news/200711/20/vs.html

■関連
Visual Studio 2013 part4
http://peace.2ch.net/test/read.cgi/tech/1404333757/
Visual Studio 2012 Part8
http://peace.2ch.net/test/read.cgi/tech/1392639689/
Visual Studio 2010 Part21
http://peace.2ch.net/test/read.cgi/tech/1412136476/
Visual Studio 2005 Part 27
http://peace.2ch.net/test/read.cgi/tech/1291513609/

その他テンプレ>>2-5
0232デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:23:20.75ID:zL1WUjLu
>>227
なるほど、了解した。
つまり、>>209は全面的に間違いで、正しくは、

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

だな。


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

VCの問題ではなくて、
俺が fnstcw [cw] と書いたのが間違いで、それをそのままコードにされてしまっただけだな。
正しく書けばSyntaxErrorだったのだし。
なお fnstcw [*cw] もSyntaxErrorだ。手動で一旦レジスタに移さないと駄目だな。
全くもって>>218のコードが正しい。
0233デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:35:08.59ID:LrdaMWHl
>>232
>俺がやるべきだったのは fnstcw [[cw]] なのだと思うが、これはSyntaxErrorだ。

ちょっと違う。あなたはやるべきことをちゃんと正しく、
fnstcw [cw]
と書いた。しかし、cw=[ebp+8]なので、これは、
fnstcw [[ebp+8]]
という「意味」になる。でも、x86/x64のマシン語にはこんな[ ]を二重にした
オペランドは存在しないので、VCが無断で勝手に[ ]を一重にして、
fnstcw [ebp+8]
に改変してしまった。

**(ebp+8) = control_word;

としなくてはならないのに、VCが勝手に、
*(ebp+8) = control_word;

としたということ。
0234デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:36:04.13ID:zL1WUjLu
>>229-230
了解だ。ありがとう。

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

ただし、
> そこで精度の違いが出てるかもしれない
との繋がりがよくからない。
sqrt()でcallされると、スタックが改変される。おそらくデータ依存か?
なら未初期化のスタックを掴みに行っているコードが有ればバグる。
ただし今回の『再現コード』はこの限りではない。
(俺の本番コードはさておき)
0235デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:37:19.30ID:zL1WUjLu
>>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]
00000044 7D 1B jge 00000061
00000046 8B 45 F8 mov eax,dword ptr [ebp-8]
00000049 8B 55 E8 mov edx,dword ptr [ebp-18h]
0000004c DD 04 D0 fld qword ptr [eax+edx*8]
0000004f 8B 45 F8 mov eax,dword ptr [ebp-8]
00000052 8B 55 E8 mov edx,dword ptr [ebp-18h]
00000055 DC 0C D0 fmul qword ptr [eax+edx*8]
00000058 DC 45 F0 fadd qword ptr [ebp-10h]
0000005b DD 5D F0 fstp qword ptr [ebp-10h]
0000005e 90 nop
0000005f EB DA jmp 0000003B
norm = sqrt(norm);
00000061 DD 45 F0 fld qword ptr [ebp-10h]
00000064 83 EC 08 sub esp,8
00000067 DD 1C 24 fstp qword ptr [esp]
0000006a E8 0D 50 7B FF call FF7B507C
0000006f DD 5D D8 fstp qword ptr [ebp-28h]
00000072 DD 45 D8 fld qword ptr [ebp-28h]
00000075 DD 5D F0 fstp qword ptr [ebp-10h]
0236デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:37:34.94ID:zL1WUjLu
>>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
00000088 FF 45 EC inc dword ptr [ebp-14h]
0000008b 8B 45 EC mov eax,dword ptr [ebp-14h]
0000008e 3B 45 FC cmp eax,dword ptr [ebp-4]
00000091 7D 12 jge 000000A5
00000093 8B 45 F8 mov eax,dword ptr [ebp-8]
00000096 8B 55 EC mov edx,dword ptr [ebp-14h]
00000099 DD 45 F0 fld qword ptr [ebp-10h]
0000009c DC 3C D0 fdivr qword ptr [eax+edx*8]
0000009f DD 1C D0 fstp qword ptr [eax+edx*8]
000000a2 90 nop
000000a3 EB E3 jmp 00000088
return norm;
000000a5 DD 45 F0 fld qword ptr [ebp-10h]
000000a8 DD 5D E0 fstp qword ptr [ebp-20h]
0239デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:49:54.65ID:zL1WUjLu
>>237
いや、俺が提供した>>191のソースなら使われてるぞ。
>>200のソースでは使われてないが。

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

俺は出来るだけ元のソースのままで追跡しようとしている。
元のソースの該当ケースと離れてしまっては意味がないから。
そして元ソースではsqrtを使っている。
0240デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:53:58.12ID:/oSJzlqn
たぶん2008の最適化ミスだと思う。
static double norm = 0;// ←"static"を追加する
にするとか、最適化オプションをいじると
Release/コマンドプロンプトからの起動でも
0x1ff68ddfb62221ddになる
0241デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:54:10.71ID:LrdaMWHl
>>237
ああ。また訂正。

sqrt()が使われていないのは、>>200, >>201, >>202, >>203 の場合で、
それは、ループ内にfprintf()を入れた場合と入れない場合とで、
x87 fpuレジスタのst(0)〜st(7)を使う「期間」が変わるために 80BITから
64BITへの書き戻し丸めの問題のために精度が変わっているだけだった。

一方、あなたが指摘した >>191 では、ちゃんと sqrt() 関数が使われていて、
それだと、IDEからの起動とコマンド・プロンプトからの起動とで、精度が変
わってくると。そして、その場合の逆アセンブル結果は >>235 のように
sqrt() 関数がその場で x87 fpu の fsqrt 命令を使わずに、call 文によって
実際に本当のサブ・ルーチンを呼び出していると。

これはとても興味深い。そのサブ・ルーチンの中が、時と場合によって
精度が変わってくるような書き方をされている可能性が見えてきた。
0242デフォルトの名無しさん
垢版 |
2018/09/16(日) 16:56:42.67ID:LrdaMWHl
>>239
>いや、俺が提供した>>191のソースなら使われてるぞ。
> >>200のソースでは使われてないが。

了解。

問題を切り分けるため、sqrt() を使わなかった場合の Release版での、
IDE起動とコマンドrライン起動の精度の違いを実験してみて欲しい。
0244デフォルトの名無しさん
垢版 |
2018/09/16(日) 17:22:07.21ID:zL1WUjLu
>>240
現象確認した。こちらでも再現した。
逆アセンブルは、以下。(肝心のループ部分は次レス内)

正直、fld/fmul/fadd/fstpのループ部分は変わらず、
normのアドレスが [ebp-10h](つまりローカル)から
ds:[00A4AD40h](つまりグローバル)に変わっただけであり、
これで結果が変わるのはかなり奇妙な気もするが、何か見落としがあるのかも。

>>240逆アセンブル(static付加版)
template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug.
static double norm = 0;
for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i];
00000000 55 push ebp
00000001 8B EC mov ebp,esp
00000003 83 EC 20 sub esp,20h
00000006 89 4D FC mov dword ptr [ebp-4],ecx
00000009 89 55 F8 mov dword ptr [ebp-8],edx
0000000c 83 3D 14 2E 38 00 00 cmp dword ptr ds:[00382E14h],0
00000013 74 05 je 0000001A
00000015 E8 FF 52 30 68 call 68305319
0000001a 33 D2 xor edx,edx
0000001c 89 55 F0 mov dword ptr [ebp-10h],edx
0000001f 33 D2 xor edx,edx
00000021 89 55 F4 mov dword ptr [ebp-0Ch],edx
00000024 D9 EE fldz
00000026 DD 5D E8 fstp qword ptr [ebp-18h]
00000029 33 D2 xor edx,edx
0000002b 89 55 F0 mov dword ptr [ebp-10h],edx
0000002e 90 nop
0000002f EB 03 jmp 00000034
0245デフォルトの名無しさん
垢版 |
2018/09/16(日) 17:22:29.32ID:zL1WUjLu
>>240逆アセンブル(続き)(static付加版)

00000031 FF 45 F0 inc dword ptr [ebp-10h]
00000034 8B 45 F0 mov eax,dword ptr [ebp-10h]
00000037 3B 45 FC cmp eax,dword ptr [ebp-4]
0000003a 7D 21 jge 0000005D
0000003c 8B 45 F8 mov eax,dword ptr [ebp-8]
0000003f 8B 55 F0 mov edx,dword ptr [ebp-10h]
00000042 DD 04 D0 fld qword ptr [eax+edx*8]
00000045 8B 45 F8 mov eax,dword ptr [ebp-8]
00000048 8B 55 F0 mov edx,dword ptr [ebp-10h]
0000004b DC 0C D0 fmul qword ptr [eax+edx*8]
0000004e DC 05 40 AD A4 00 fadd qword ptr ds:[00A4AD40h]
00000054 DD 1D 40 AD A4 00 fstp qword ptr ds:[00A4AD40h]
0000005a 90 nop
0000005b EB D4 jmp 00000031
norm = sqrt(norm);
0000005d DD 05 40 AD A4 00 fld qword ptr ds:[00A4AD40h]
00000063 83 EC 08 sub esp,8
00000066 DD 1C 24 fstp qword ptr [esp]
00000069 E8 0E 50 88 FF call FF88507C
0000006e DD 5D E0 fstp qword ptr [ebp-20h]
00000071 DD 45 E0 fld qword ptr [ebp-20h]
00000074 DD 1D 40 AD A4 00 fstp qword ptr ds:[00A4AD40h]
0246デフォルトの名無しさん
垢版 |
2018/09/16(日) 17:22:46.06ID:zL1WUjLu
>>240逆アセンブル(続き)(static付加版)
if (regulate) for (int i=0;i<num;i++) r[i] = (T)(r[i]/norm);
0000007a 0F B6 45 08 movzx eax,byte ptr [ebp+8]
0000007e 85 C0 test eax,eax
00000080 74 28 je 000000AA
00000082 33 D2 xor edx,edx
00000084 89 55 F4 mov dword ptr [ebp-0Ch],edx
00000087 90 nop
00000088 EB 03 jmp 0000008D
0000008a FF 45 F4 inc dword ptr [ebp-0Ch]
0000008d 8B 45 F4 mov eax,dword ptr [ebp-0Ch]
00000090 3B 45 FC cmp eax,dword ptr [ebp-4]
00000093 7D 15 jge 000000AA
00000095 8B 45 F8 mov eax,dword ptr [ebp-8]
00000098 8B 55 F4 mov edx,dword ptr [ebp-0Ch]
0000009b DD 05 40 AD A4 00 fld qword ptr ds:[00A4AD40h]
000000a1 DC 3C D0 fdivr qword ptr [eax+edx*8]
000000a4 DD 1C D0 fstp qword ptr [eax+edx*8]
000000a7 90 nop
000000a8 EB E0 jmp 0000008A
return norm;
000000aa DD 05 40 AD A4 00 fld qword ptr ds:[00A4AD40h]
000000b0 DD 5D E8 fstp qword ptr [ebp-18h]
}
000000b3 DD 45 E8 fld qword ptr [ebp-18h]
000000b6 8B E5 mov esp,ebp
000000b8 5D pop ebp
000000b9 C2 04 00 ret 4
0247デフォルトの名無しさん
垢版 |
2018/09/16(日) 17:35:35.70ID:zL1WUjLu
>>242
まだ異なった出力が得られた。
この意味では200がsqrtを外した判断は正しかった。
(彼はそこからさらにループ回数を固定してしまったのが間違いだった)

191ソースを以下に変更した。(sqrtをコメントアウト)
ついでに Console::Write(String::Format("{0:E6}, {0:E30}\r\n",norm)); の出力も付けておく。

ソース:
template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug.
double norm = 0;
for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i];
// norm = sqrt(norm);
if (regulate) for (int i=0;i<num;i++) r[i] = (T)(r[i]/norm);
return norm;
}

結果:(Releaseビルド/コマンドプロンプトからの起動)
0.000000, 0x0007f2c44dfff8f2
1.105348E-308, 1.105348254058510600000000000000E-308

結果:(Releaseビルド/IDEからの起動、Debugビルドは起動方法によらずこちら)
0.000000, 0x0007f2c44dfff8f1
1.105348E-308, 1.105348254058510100000000000000E-308


>>243
了解。いずれにしても助かってる。
こちらも後30分くらいでちょっと離れる予定。
0248デフォルトの名無しさん
垢版 |
2018/09/16(日) 18:30:34.19ID:HF0YmRsW
>>212
ほんそれ
0249デフォルトの名無しさん
垢版 |
2018/09/16(日) 20:54:27.39ID:zL1WUjLu
>>240
さて再見したが、やはりstaticだけで直る理由は分からない。
なお、最適化ミスの場合は、逆アセンブラを読めば分かる。
今のところそれではない。

一応、>>191ソースのtemplate部の逆アセンブルを上げておく。(ただし重複するので頭のみ)
頭はこれ。続きが>>235,236。

template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug.
double norm = 0;
00000000 55 push ebp
00000001 8B EC mov ebp,esp
00000003 83 EC 28 sub esp,28h
00000006 89 4D FC mov dword ptr [ebp-4],ecx
00000009 89 55 F8 mov dword ptr [ebp-8],edx
0000000c 83 3D 14 2E 76 00 00 cmp dword ptr ds:[00762E14h],0
00000013 74 05 je 0000001A
00000015 E8 FF 52 1B 68 call 681B5319
0000001a 33 D2 xor edx,edx
0000001c 89 55 E8 mov dword ptr [ebp-18h],edx
0000001f 33 D2 xor edx,edx
00000021 89 55 EC mov dword ptr [ebp-14h],edx
00000024 D9 EE fldz
00000026 DD 5D F0 fstp qword ptr [ebp-10h]
00000029 D9 EE fldz
0000002b DD 5D E0 fstp qword ptr [ebp-20h]
0000002e D9 EE fldz
00000030 DD 5D F0 fstp qword ptr [ebp-10h]
0250デフォルトの名無しさん
垢版 |
2018/09/16(日) 21:25:23.90ID:zL1WUjLu
>>219
>>221
/MTと/clrは同時に指定出来ないらしい。(error D8016)
/MTdも同じく無理。

もう一つ /MDd ってのがあるから試してみた。

/MDdの結果:
Releaseビルドでコマンドプロンプト起動の時のみ ****de、
ReleaseビルドでIDEからの起動だと ***dd。(Debugビルドは起動方法を問わずこっち)
(/MDと全く挙動は同じ)


これで有効な指摘については全て回答してるかな?
見落としが有れば指摘よろしく。
(規制に引っかかったので遅くなってすまん)


今のところ、可能性があるのは以下か?

・Releaseビルドをコマンドプロンプトから起動したときのみなぜか精度が高い
 (>>200から結果的に検出された。今のところ精度が高いときと同じ挙動をしている為)
・ReleaseビルドもIDEから起動すれば結果的にスタックが0初期化されている状態になっており、
 俺の本番プログラムに関してはここに当たるバグがある?(>>228)
 (ただしこれは>>191には該当しない)
0251240
垢版 |
2018/09/16(日) 21:43:04.47ID:/oSJzlqn
.netの場合、デバッガ配下では(デバッグのため)違うコードを実行しているような気がする。
デバッガの逆アセンブル表示とかasm出力はあまり当てにならないような気もする。
ループ部分だけど、レジスタのみで処理するか、メモリを使用するかで精度が変わるのかも。
そもそも、どっちが正しいのかよくわからんけど...
ループ部分の関数を#pragma unmanagedすると結果が変わるでそれが正しいのかも。
0252240
垢版 |
2018/09/16(日) 21:43:27.78ID:/oSJzlqn
static版
0000000e 33 C0 xor eax,eax
00000010 85 F6 test esi,esi
00000012 7E 16 jle 0000002A
00000014 DD 04 C7 fld qword ptr [edi+eax*8]
00000017 DC C8 fmul st(0),st
00000019 DC 05 00 30 CC 00 fadd qword ptr ds:[00CC3000h]
0000001f DD 1D 00 30 CC 00 fstp qword ptr ds:[00CC3000h]
00000025 40 inc eax
00000026 3B C6 cmp eax,esi
00000028 7C EA jl 00000014
0000002a DD 05 00 30 CC 00 fld qword ptr ds:[00CC3000h]

非static版
0000000e D9 EE fldz
00000010 33 C0 xor eax,eax
00000012 85 F6 test esi,esi
00000014 7E 0C jle 00000022
00000016 DD 04 C7 fld qword ptr [edi+eax*8]
00000019 DC C8 fmul st(0),st
0000001b DE C1 faddp st(1),st
0000001d 40 inc eax
0000001e 3B C6 cmp eax,esi
00000020 7C F4 jl 00000016
0253デフォルトの名無しさん
垢版 |
2018/09/16(日) 22:27:34.58ID:zL1WUjLu
>>251
とりあえず落ち着け。一つずつ行こう。

> ループ部分の関数を#pragma unmanagedすると結果が変わるでそれが正しいのかも。
こちらでも確認した。
calc_norm_and_regulateをunmanaged関数にすると、違いはなくなる。
(Releaseビルドの`をコマンドプロンプトで起動した際にも、****ddの結果となる)

ただしこちらの逆アセンブル結果は以下だ。(fld/fmul/fadd/fstpであることに注意)
for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i];
0007272C C7 45 F4 00 00 00 00 mov dword ptr [i],0
00072733 EB 09 jmp `anonymous namespace'::calc_norm_and_regulate<double>+1Eh (7273Eh)
00072735 8B 45 F4 mov eax,dword ptr [i]
00072738 83 C0 01 add eax,1
0007273B 89 45 F4 mov dword ptr [i],eax
0007273E 8B 4D F4 mov ecx,dword ptr [i]
00072741 3B 4D 08 cmp ecx,dword ptr [num]
00072744 7D 1A jge `anonymous namespace'::calc_norm_and_regulate<double>+40h (72760h)
00072746 8B 55 F4 mov edx,dword ptr [i]
00072749 8B 45 0C mov eax,dword ptr [r]
0007274C 8B 4D F4 mov ecx,dword ptr [i]
0007274F 8B 75 0C mov esi,dword ptr [r]
00072752 DD 04 D0 fld qword ptr [eax+edx*8]
00072755 DC 0C CE fmul qword ptr [esi+ecx*8]
00072758 DC 45 F8 fadd qword ptr [norm]
0007275B DD 5D F8 fstp qword ptr [norm]
0007275E EB D5 jmp `anonymous namespace'::calc_norm_and_regulate<double>+15h (72735h)
0254デフォルトの名無しさん
垢版 |
2018/09/16(日) 22:33:32.07ID:zL1WUjLu
>>252
そちらの逆アセンブルは以下の違いが出てるだろ。
static版: fld/fmul/fadd/fstp
非static版: fld/fmul/faddp (fstpが無い)
この非static版の場合、拡張倍精度(80bit)で演算されるから精度が高いことになり、
static版との演算結果に違いが出るのも仕様通りなんだよ。(これは>>200と同じ間違い)

一応、fstpにも80bit版はあって、Intelのマニュアルによると以下。
> オペコード命令説明
> D9 /2 FST m32fp ST(0) をm32fp にコピーする。
> DD /2 FST m64fp ST(0) をm64fp にコピーする。
> DD D0+i FST ST(i) ST(0) をST(i) にコピーする。
> D9 /3 FSTP m32fp ST(0) をm32fp にコピーし、レジスタスタックをポップする。
> DD /3 FSTP m64fp ST(0) をm64fp にコピーし、レジスタスタックをポップする。
> DB /7 FSTP m80fp ST(0) をm80fp にコピーし、レジスタスタックをポップする。
> DD D8+i FSTP ST(i) ST(0) をST(i) にコピーし、レジスタスタックをポップする。
つまり君のstatic版
> 0000001f DD 1D 00 30 CC 00 fstp qword ptr ds:[00CC3000h]
では FSTP /3 m64fp [disp32] であり、そこで64bit(倍精度)に丸められてる。
だからレジスタ(80bit=拡張倍精度)で演算される非static版と結果が異なる。
static版のsftpが DB /7 m80fp なら誤差は出ないはずなんだよ。(Cでどう書くのかは知らん)

だから>>252の場合の誤差なら、仕様通りなんだよ。(片方が倍精度、もう片方は拡張倍精度)
ただし、>>191は逆アセンブル(>>235)を見る限りそれに該当しないし、(両方とも倍精度)
今回の俺の上記逆アセンブル(>>253、中身は君の指摘通りunmanagedにしただけ)も該当しない。(両方とも倍精度)
そして253は何故か直ってしまった。
0255デフォルトの名無しさん
垢版 |
2018/09/16(日) 22:34:16.44ID:zL1WUjLu
>>252
> .netの場合、デバッガ配下では(デバッグのため)違うコードを実行しているような気がする。
> デバッガの逆アセンブル表示とかasm出力はあまり当てにならないような気もする。
これは俺も相当疑っているのだが、今のところ尻尾を掴めない。
ILspyだっけ?外部の逆アセンブルツール使えばチェック出来るのかな?

いずれにしても、>>251の指摘
・unmanagedにすれば直る
のも事実だし、逆アセンブルを見る限り、これを説明出来る理由もないのも事実。
0256240
垢版 |
2018/09/16(日) 23:13:48.62ID:/oSJzlqn
> だから>>252の場合の誤差なら、仕様通りなんだよ。(片方が倍精度、もう片方は拡張倍精度)
そうなの? これが仕様通りならstatic版での違いは仕様通りということになる。
252はRelease版をコンソールで実行したときの逆アセンブル結果。
よって、Release版をコンソールで実行したときのみ(たまたま)レジスタ(80ビット)での演算になるので、
計算結果が変わるのはやむを得ないという結論になるのだが...

ちなみに、235はDebugモードでコンパイルし、デバッガ配下の逆アセンブル結果でしょ。
0257デフォルトの名無しさん
垢版 |
2018/09/16(日) 23:24:30.83ID:zL1WUjLu
>>256
> 252はRelease版をコンソールで実行したときの逆アセンブル結果。
それはどうやって得たの?俺はそれが出来ないから困ってる。

> ちなみに、235はDebugモードでコンパイルし、デバッガ配下の逆アセンブル結果でしょ。
235は、IDE上でReleaseモードでF5で起動し、ブレークポイントを当てて止めて逆アセンブルした結果。
俺が貼ってる逆アセンブル結果は全てこの方法で、IDEで表示されているもの。
だからIDEの表示がおかしかったら話が全部おかしくなる。

君がIDEから独立して逆アセンブル出来ているのなら、その方法を知りたい。
こちらでも試す。


なおILSpy、グダグダ言わずに試してみたが、
当たり前だがmanaged code だとILが出る(x86ではない)ので、
俺って根本的に間違ってたかも?
今までx86のアセンブラで議論してたけど、これって .NET アプリには同梱されていないというオチ?
(まあその場合は君がやっている外部逆アセンブルが単純には出来ないはずなのだが)
0258240
垢版 |
2018/09/16(日) 23:35:36.48ID:/oSJzlqn
>> 252はRelease版をコンソールで実行したときの逆アセンブル結果。
>それはどうやって得たの?俺はそれが出来ないから困ってる。
calc_norm_and_regulateの次の行に
System::Diagnostics::Debugger::Launch();
を入れてコンソールから実行すると just in time デバッグできるので、デバッガを選んだ後、
Visual Studioの 呼び出し履歴から calc_norm_and_regulate を探して移動する

>> ちなみに、235はDebugモードでコンパイルし、デバッガ配下の逆アセンブル結果でしょ。
>235は、IDE上でReleaseモードでF5で起動し、ブレークポイントを当てて止めて逆アセンブルした結果。

あれ? だとすると最適化していないのでは?
こちらの結果と違うのだが。
0259デフォルトの名無しさん
垢版 |
2018/09/16(日) 23:49:32.68ID:SOVIz+sV
とりあえず、>>198に書いてあるとおり、元のコードで再現はしてるからな
その再現したコードと逆アセンブルした結果はあげとく

B-1 デフォルト設定(Release) 【コード】

#include "stdafx.h"
#include <stdio.h>
#include <stdint.h>
#include <math.h>

using namespace System;

template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug.
   double norm = 0;
   for (int i = 0; i < num; i++) {
      norm += (double)r[i] * (double)r[i];
//      fprintf(stdout, "[0]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   }
   fprintf(stdout, "[1]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   norm = sqrt(norm);
   if (regulate)
      for (int i=0;i<num;i++)
         r[i] = (T)(r[i]/norm);
   return norm;
}
0260デフォルトの名無しさん
垢版 |
2018/09/16(日) 23:50:40.20ID:SOVIz+sV
B-1 デフォルト設定(Release) 【コード】(その2)

int main(array<System::String ^> ^args)
{
   int count = 16;
   __int64 inputs_hex[16] = {
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1fedb1530240aa54,
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1ff0af0d95025bc3,
      0x1fc9353df6af376b, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000
   };
   double* inputs = (double*)inputs_hex;
   double norm = calc_norm_and_regulate(count, inputs, false);
   fprintf(stdout, "[2]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   // Console::Write(String::Format("{0:F6}, 0x{1:x16}\r\n",norm, *(__int64*)&norm));
   // Release build
   // 0.000000, 0x1ff68ddfb62221dd from IDE
   // 0.000000, 0x1ff68ddfb62221de from command prompt
   return 0;
}
0261デフォルトの名無しさん
垢版 |
2018/09/16(日) 23:51:14.21ID:SOVIz+sV
B-1 デフォルト設定(Release) 【実行結果】

↓このコードの逆アセンブルコード
https://ideone.com/Gf4qUQ

[1]0x0007F2C44DFFF8F2:1.1053482540585106e-308
[2]0x1FF68DDFB62221DE:1.051355436595308e-154
0262デフォルトの名無しさん
垢版 |
2018/09/16(日) 23:54:13.85ID:SOVIz+sV
で、>>199に書いてあるとおり↓Debugビルドと同じ結果が再現された

> ↓この下に(ループ内に)fprintf文を入れるだけで
>  ReleaseビルドとDebugビルドが同じ実行結果になることが確認できた
> norm += (double)r[i] * (double)r[i];

B-2 デフォルト設定(Release) 【コード】(その1)

#include "stdafx.h"
#include <stdio.h>
#include <stdint.h>
#include <math.h>

using namespace System;

template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug.
   double norm = 0;
   for (int i = 0; i < num; i++) {
      norm += (double)r[i] * (double)r[i];
      fprintf(stdout, "[0]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   }
   fprintf(stdout, "[1]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   norm = sqrt(norm);
   if (regulate)
      for (int i=0;i<num;i++)
         r[i] = (T)(r[i]/norm);
   return norm;
}
0263デフォルトの名無しさん
垢版 |
2018/09/16(日) 23:54:58.60ID:SOVIz+sV
B-2 デフォルト設定(Release) 【コード】(その2)

int main(array<System::String ^> ^args)
{
   int count = 16;
   __int64 inputs_hex[16] = {
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1fedb1530240aa54,
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1ff0af0d95025bc3,
      0x1fc9353df6af376b, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000
   };
   double* inputs = (double*)inputs_hex;
   double norm = calc_norm_and_regulate(count, inputs, false);
   fprintf(stdout, "[2]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   // Console::Write(String::Format("{0:F6}, 0x{1:x16}\r\n",norm, *(__int64*)&norm));
   // Release build
   // 0.000000, 0x1ff68ddfb62221dd from IDE
   // 0.000000, 0x1ff68ddfb62221de from command prompt
   return 0;
}
0264デフォルトの名無しさん
垢版 |
2018/09/16(日) 23:56:17.88ID:SOVIz+sV
B-2 デフォルト設定(Release) 【実行結果】

↓このコードの逆アセンブルコード
https://ideone.com/E3Nxt8

↓実行結果を書き込めないからこっちに書き込んどいた
https://ideone.com/1cky6N

[1]0x0007F2C44DFFF8F1:1.1053482540585101e-308
[2]0x1FF68DDFB62221DD:1.0513554365953078e-154

以上だ
0265デフォルトの名無しさん
垢版 |
2018/09/16(日) 23:58:42.66ID:zL1WUjLu
>>258
おお、そのやり方は知らなかった。大変助かった。ありがとう。
で、結果だが、>>252とは微妙に違うが、確かに拡張倍精度で計算されている。
逆アセンブル結果は、以下。

0000000e D9 EE fldz
for (int i=0;i<num;i++) norm += (double)r[i] * (double)r[i];
00000010 33 C9 xor ecx,ecx
00000012 EB 01 jmp 00000015
00000014 41 inc ecx
00000015 3B CE cmp ecx,esi
00000017 7D 0B jge 00000024
00000019 DD 04 CF fld qword ptr [edi+ecx*8]
0000001c D9 C0 fld st(0)
0000001e DE C9 fmulp st(1),st
00000020 DE C1 faddp st(1),st
00000022 EB F0 jmp 00000014
norm = sqrt(norm);
00000024 83 EC 08 sub esp,8
00000027 DD 1C 24 fstp qword ptr [esp]
0000002a E8 49 7C F2 FF call FFF27C78

とにかく、Releaseビルドをコンソールから起動した場合は拡張倍精度になってるのは分かった。
なら、ReleaseビルドをIDEから起動した場合は何を起動してるんだこれは?
Debugビルドとも微妙にアドレス等が違うんだが。

とはいえ、これは「そもそも色々間違っている」可能性が出てきたので、もう一度全体を見直す。
明日(だけで済むとも思えないが)確認し、整理してまた投稿する。

とにかくありがとう。これはだいぶインパクトがある。(はず)
0266デフォルトの名無しさん
垢版 |
2018/09/17(月) 00:49:10.27ID:gBxGzBbQ
>>265
ideとconsoleで微妙に違うバージョンのvcruntimeがロードされてるとか?
process monitorで何が実行されて何がロードされてるか調べるとか?
あとはwindbgから起動したら、ideとconsoleのどちらの結果と一致するのか気になる。
0267デフォルトの名無しさん
垢版 |
2018/09/17(月) 01:06:37.11ID:+dwRu2dr
>>261
だからそれは>>200と同じなんだよ。
その逆アセンブルでいうと、以下部分がメモリに出力されず、拡張倍精度で動作してるだろ。

00000281 fld qword ptr [ebp+FFFFFF14h]
00000287 fmul st,st(0)
00000289 fadd qword ptr [ebp+FFFFFF70h]
0000028f fld qword ptr [ebp+FFFFFF1Ch]
00000295 fmul st,st(0)
00000297 faddp st(1),st
00000299 fld qword ptr [ebp+FFFFFF24h]
0000029f fmul st,st(0)
000002a1 faddp st(1),st
000002a3 fld qword ptr [ebp+FFFFFF2Ch]
000002a9 fmul st,st(0)
000002ab faddp st(1),st
000002ad fld qword ptr [ebp+FFFFFF34h]
000002b3 fmul st,st(0)
000002b5 faddp st(1),st
000002b7 fld qword ptr [ebp+FFFFFF3Ch]
000002bd fmul st,st(0)
000002bf faddp st(1),st
000002c1 fld qword ptr [ebp+FFFFFF44h]
000002c7 fmul st,st(0)
000002c9 faddp st(1),st
000002cb fld qword ptr [ebp+FFFFFF4Ch]
000002d1 fmul st,st(0)
000002d3 faddp st(1),st
000002d5 fstp qword ptr [ebp+FFFFFF70h]
0268デフォルトの名無しさん
垢版 |
2018/09/17(月) 01:07:41.74ID:+dwRu2dr
>>261(続き)
これは少なくとも「ループ回数が8の倍数である」事がコンパイラに見えないと出来ない最適化だ。
そうでなければ、例えばループ回数が6回や14回の時に、
最初の1回だけ 0299 に飛び込んで始める(頭の2回をスキップする)コードが必要になるが、
それは出てないだろ。

(そもそもこのアンローリングがx86的に意味があるのかも疑問だが)
一般的に、可変回数ループを展開すると、必ず上記の端切れ処理(キリが良くないときの処理)が必要になる。
だから「可変」だと確定しているのなら普通は展開しない。
つまり、一般的には、別関数でループ回数が引数で与えられてたら、その最適化はかからない。

今回ヒットするデータが偶々16回ループだっただけで、
俺の本番コードは可変で用いている為、
このようなアンローリングはされてない。(と思ってる。ただし265の通りもう一度確認必要)
こちらではデフォでそこまで最適化がかからないのでそちらの状況はよく分からないが、
その最適化がかかってるのなら、俺の本番コードとは違う状況で動いていることになる。(はず)
だから俺はその最適化がかからない範囲で議論してる。
それが俺が最初から別関数にしてた理由。(というか元のコードが別関数だからだが)


てゆうかマジでそれデフォのままか?
もしかして俺の環境がおかしくて、全く最適化されてないコードが出てる?
0269247
垢版 |
2018/09/17(月) 01:12:11.55ID:yaPtorLJ
今、戻った。

席を離れて思ったが、多分、C++/CLI の場合、IDEでコンパイル後、
exeファイルになっても、unmanaged コード部分以外は、本質的には
.NETの共通中間言語(CIL、MSIL)になるだけで、x86のマシン語には
なってないと思う。一方、昔ながらの本当のC++では、exeの中身は、
本質的には、x86のマシン語が入っていた。

C++/CLIの場合、作成されたexeは、起動時に、中間言語がx86の
マシン語に直されてから実行される。その際、最適化の関係で、
どう直されるかが必ずしも一定していない。だから、IDEからの起動と、
コマンドラインからの起動で異なったx86命令に直されてしまう
のかも知れない。

出来たx86コードが、良く最適化された場合、x87 fpu命令を長きに
渡ってメモリに書き戻さずに、st(0)〜st(7)のレジスタ上で演算し
続けられることになる。その場合、80BITの精度で計算される
期間が長くなる。

一方、x87 fpu命令に関する最適化が少し悪いか、悪くなくても命令の
使い方が変わってしまうと、80BIT(拡張double)から64BIT(double)へ
直されるタイミングや回数が変わる。

この両者では結果の精度が変わってくる。前者の方が後者より僅か
に精度が高くなる。
0270243=269
垢版 |
2018/09/17(月) 01:14:28.51ID:yaPtorLJ
>>269
名前欄は、「247」ではなく「243」の間違いだった。
0271デフォルトの名無しさん
垢版 |
2018/09/17(月) 01:24:06.04ID:yaPtorLJ
>>257
>なおILSpy、グダグダ言わずに試してみたが、
>当たり前だがmanaged code だとILが出る(x86ではない)ので、
>俺って根本的に間違ってたかも?
>今までx86のアセンブラで議論してたけど、これって .NET アプリには同梱されていないというオチ?

やはり、managed code部分は、x86命令では無く、ILにコンパイルされていて、
普通のC++とは違ってたんだ。
0272デフォルトの名無しさん
垢版 |
2018/09/17(月) 01:30:31.16ID:yaPtorLJ
>calc_norm_and_regulateをunmanaged関数にすると、違いはなくなる。
>(Releaseビルドの`をコマンドプロンプトで起動した際にも、****ddの結果となる)

やはり。
unmanaged関数の場合は、CIL(MSIL)ではなく、exeの段階で既に
x86マシン語に直されたものが格納されるんだろう。だとすると、起動方法に
関係なく、少なくともその部分に関しては、x87 fpu命令の使われ方が
全く同じになる。callしたsqrt()関数の中は除いて。
0273デフォルトの名無しさん
垢版 |
2018/09/17(月) 01:37:20.55ID:dj7qSZnZ
>>259-260のコードも>>262-263
も同じ条件でデフォルトでコンパイルしてる
コレは間違いない

一行コメントはずしてコンパイルしなおすだけだからな

で、>>261>>264みたいな結果になる
>>264の実行結果はDebugビルドとまったく同じになる
そのまんま
0274デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:03:16.26ID:yaPtorLJ
はっきり書いてある。managed code は、起動時にJITコンパイルされる。
だから、どんなマシン語に置き換わるかが、コンパイルしただけでは
まだ完全には決定されてない。

https://en.wikipedia.org/wiki/Managed_code

Drawbacks include slower startup speed (the managed code must be JIT
compiled by the VM) and generally increased use of system resources
on any machine that is executing the code.


managed code は、VMによって、JIT コンパイルされないといけないので、
起動速度が遅くなり、かつ、一般的に、システム・リソースの使用が増える。
0275デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:49:02.18ID:dj7qSZnZ
普通のコンソールアプリケーション(CLRじゃないほう)でも
同じコードで普通にまったく同じように再現するワケだが

ホントなキミラはなにを頭悪いことばっかりいってんの?
実行結果だけははっといてやるが
0276デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:50:12.89ID:dj7qSZnZ
C-1 デフォルト設定(Release) 【コード】(その1)

#include "stdafx.h"
#include <stdio.h>
#include <stdint.h>
#include <math.h>

template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug.
   double norm = 0;
   for (int i = 0; i < num; i++) {
      norm += (double)r[i] * (double)r[i];
//      fprintf(stdout, "[0]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   }
   fprintf(stdout, "[1]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   norm = sqrt(norm);
   if (regulate)
      for (int i=0;i<num;i++)
         r[i] = (T)(r[i]/norm);
   return norm;
}
0277デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:50:37.91ID:dj7qSZnZ
C-1 デフォルト設定(Release) 【コード】(その2)

int _tmain(int argc, _TCHAR* argv[])
{
   int count = 16;
   __int64 inputs_hex[16] = {
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1fedb1530240aa54,
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1ff0af0d95025bc3,
      0x1fc9353df6af376b, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000
   };
   double* inputs = (double*)inputs_hex;
   double norm = calc_norm_and_regulate(count, inputs, false);
   fprintf(stdout, "[2]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   return 0;
}
0278デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:51:12.35ID:dj7qSZnZ
C-1 デフォルト設定(Release) 【実行結果】

↓このコードの逆アセンブルコード
https://ideone.com/Dqqn6J

[1]0x0007F2C44DFFF8F2:1.1053482540585106e-308
[2]0x1FF68DDFB62221DE:1.051355436595308e-154
0279デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:51:53.99ID:dj7qSZnZ
C-2 デフォルト設定(Release) 【コード】(その1)

#include "stdafx.h"
#include <stdio.h>
#include <stdint.h>
#include <math.h>

template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug.
   double norm = 0;
   for (int i = 0; i < num; i++) {
      norm += (double)r[i] * (double)r[i];
//      fprintf(stdout, "[0]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   }
   fprintf(stdout, "[1]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   norm = sqrt(norm);
   if (regulate)
      for (int i=0;i<num;i++)
         r[i] = (T)(r[i]/norm);
   return norm;
}
0280デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:52:10.55ID:dj7qSZnZ
C-2 デフォルト設定(Release) 【コード】(その2)

int _tmain(int argc, _TCHAR* argv[])
{
   int count = 16;
   __int64 inputs_hex[16] = {
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1fedb1530240aa54,
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x1ff0af0d95025bc3,
      0x1fc9353df6af376b, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
      0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000
   };
   double* inputs = (double*)inputs_hex;
   double norm = calc_norm_and_regulate(count, inputs, false);
   fprintf(stdout, "[2]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   return 0;
}
0281デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:52:25.20ID:dj7qSZnZ
C-2 デフォルト設定(Release) 【実行結果】

↓このコードの逆アセンブルコード
https://ideone.com/5OaUe6

↓実行結果を書き込めないからこっちに書き込んどいた
https://ideone.com/FFW0P0

[1]0x0007F2C44DFFF8F1:1.1053482540585101e-308
[2]0x1FF68DDFB62221DD:1.0513554365953078e-154
0282デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:53:45.62ID:dj7qSZnZ
C-2 デフォルト設定(Release) 【コード】(その1)

#include "stdafx.h"
#include <stdio.h>
#include <stdint.h>
#include <math.h>

template<typename T> static double calc_norm_and_regulate(int num, T* r, bool regulate){ // <float> for debug.
   double norm = 0;
   for (int i = 0; i < num; i++) {
      norm += (double)r[i] * (double)r[i];
      fprintf(stdout, "[0]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   }
   fprintf(stdout, "[1]0x%016llX:%.19lg\n", *(uint64_t*)&norm, norm);
   norm = sqrt(norm);
   if (regulate)
      for (int i=0;i<num;i++)
         r[i] = (T)(r[i]/norm);
   return norm;
}
0283デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:54:34.82ID:dj7qSZnZ
(正)>>282
(誤)>>279
0284デフォルトの名無しさん
垢版 |
2018/09/17(月) 02:57:46.48ID:dj7qSZnZ
>>276-277のコードも>>282,280のコードも
も同じ条件でデフォルトでコンパイルしてる

一行コメントはずしてコンパイルしなおすだけだからな

で、>>278>>281みたいな結果になる
>>281の実行結果はDebugビルドとまったく同じになる
そのまんま

CLRのとき(>>273)の動作とまったく同じ
キミラはずーっとなにやってるわけ?
0285デフォルトの名無しさん
垢版 |
2018/09/17(月) 09:44:34.76ID:yu1Dprt2
>>284
同一のreleaseをコンソールで実行するかデバッガで実行するかで結果が異なるのはなぜだろう。
という話をしていたのであって、
debug/releaseで別の結果になることを問題にしているのではないです。
0286デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:25:22.39ID:+dwRu2dr
>>191がコンソール起動とIDE起動で挙動が異なる理由は分かりました。
ありがとう。

結論はつまり以下だ。
> JIT の最適化とデバッグ(抜粋)
> マネージ アプリケーションをデバッグするとき、Visual Studio では、既定で、
> ジャスト イン タイム (JIT: Just-In-Time) コードの最適化が省略されています。
> 最適化されたコードをデバッグするのは困難であるため、
> 最適化されたコードで発生するバグが、非最適化バージョンでは再現しないときにのみお勧めします。
> JIT 最適化は、Visual Studio の [モジュールの読み込み中に JIT 最適化を抑制する] オプションで制御されます。
> 実行中のプロセスにアタッチする場合、既に読み込まれ、JIT でコンパイルされ、
> 最適化されているコードが含まれることがあります。
> このようなコードの場合、[モジュールの読み込み中に JIT 最適化を抑制する] オプションの影響はありません。
> https://msdn.microsoft.com/ja-jp/library/ms241594.aspx
確かにこのオプションで直った。
0287デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:25:48.51ID:+dwRu2dr
その他諸々、話を整理すると、以下となる。(ソースは>>191参照)
1. managedコードではMSILが出力され、x86コードは含まれていない。
2. 起動時、MSILはJITされ、x86コードに落とされる。
3. このため、mainの1行目でブレークポイントで止め、calc_norm_and_regulateの逆アセンブルを見ようとしても、
 IDE上で「逆アセンブルを表示できません。式がまだネイティブ マシン コードに翻訳されていません。」と出る。
 これはmainの1行目に System::Diagnostics::Debugger::Launch(); を入れたときも同様。
4. そしてこのJITに関して、上記IDE中の 『[モジュールの読み込み中に JIT 最適化を抑制する] オプション』 が効いてくる。
 規定ではオフ、つまり、ReleaseビルドでもIDE起動ならJIT最適化は抑制される。
 これがfld/fmul/fadd/fstpのループコードになる理由。
 これをオンにすれば、確かにReleaseビルドIDE起動でも、
 fld/fmul/faddのループコードとなり、コマンドプロンプト起動と同じ結果になることは確認した。
5. 上記では表現が微妙だが、JIT最適化をするかどうかは読み込まれるときに決まるらしい。
 したがって、Releaseビルドを起動後にアタッチした場合は通常通り最適化され、
 IDEからReleaseビルドを起動した場合は『既定では』最適化が抑制されてしまう。
 これがIDE起動とコマンドプロンプト起動で挙動が異なった原因。
 上記、『[モジュールの読み込み中に JIT 最適化を抑制する]』のチェックを外せば、直った。
6. おそらくこのオプションはソリューション毎ではなく、IDEのインストール毎なんだと思う。
 (ソリューション毎のオプションはプロジェクトのプロパティにあり、場所が違う)
 だからその人の環境によっては最初からオフにしている人がいたかも?
 これが再現実験をしてくれた人たちと微妙に結果が異なったりした原因か?

これで>>191についての疑問は解消した。(はず)
俺の本番コードについては再度確認し、また報告する。
0288デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:28:21.54ID:dj7qSZnZ
同じリリースビルドで
結果がかわってんのになにいってんの?

fprintf入れるだけで
リリースビルドで結果が変わる
0289デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:31:16.66ID:dj7qSZnZ
やはり低学歴知恵遅れは
結果が意味するところが分かってないわ。。。
0290デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:32:49.39ID:dj7qSZnZ
ちなみにオレがあげた結果は
すべてリリースビルドの結果だからな
デバッグビルドの結果なんかあげても
意味ないからな
0291デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:38:00.02ID:dj7qSZnZ
CLRのケースもCLRでない普通のexeのケースでも
結果はまったく同じだからな

しかもすべてリリースビルドで
おきてる誤差までぴったり一致してる
0292デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:52:24.35ID:+dwRu2dr
>>288
お前は相変わらず理解してないな。

80bit(拡張倍精度)と64bit(倍精度)の演算で桁落ちが異なり、結果が異なるのは当然なんだよ。
問題は同じバイナリの癖に何故起動方法によって異なるのか?だったんだ。

理由はMSILだからだ。
MSILはCLR上でJITされ、x86コードに落とされる。
このときにJIT最適化がかかれば、拡張倍精度(を保ったまま)のコードになるし、
最適化がかからず毎回メモリに書き戻していれば、倍精度のコードになる。
.NETにおける同一バイナリってのは、同一MSILという意味であって、同一x86機械語という意味ではない。
だから、確かに同一バイナリを掴んでいたが、起動方法によって結果が異なっていたんだよ。
(VSがデバッグ用に意図的にそういう仕様にしていただけ。俺はそれを知らなかった)

君のコードについては、
いちいちfprintする場合はdouble(倍精度)が毎回必要になるから、
コンパイラはその部分での拡張倍精度でのループを断念し、
結果的にそのループが倍精度で回っているだけのこと。
もし仮にCが拡張倍精度型 ExDouble を持っていたとして、printfもそれに対応していれば、
もしかするとその毎回printfするコードでも拡張倍精度で回っていたかもしれん。
勿論手動でそういうコードのすることも可能だ。
そこはコンパイラがどう判断したのかでしかなく、あまり詰めても意味がない。
0294デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:57:31.99ID:dj7qSZnZ
で、最適化されてるかされてないかすら
いまのいままで気付くことすらできない
そして気付く方法すらわからなかったわけか

うあわ
頭わるう。。。

キミ、プログラムくむの向いてないわ
0295デフォルトの名無しさん
垢版 |
2018/09/17(月) 10:58:47.63ID:dj7qSZnZ
問題の切り分けができない人は
プログラムはくめない

コレは定説だからな
0296デフォルトの名無しさん
垢版 |
2018/09/17(月) 11:09:05.96ID:ivGPGa/P
>>286
なるほど面白いね。

レスが膨大過ぎて最初の方しか読んでなかったけど、ネイティブコードの話と思い込んでたら
.NETの話だったのかw
0297デフォルトの名無しさん
垢版 |
2018/09/17(月) 12:34:14.16ID:yaPtorLJ
個人的には、C++やx87 FPUは割と知識があったけど、.NET関連は余り追いかけてなかったので、気づくのが遅れた。

managedコード、unmanagedコードについて、今回初めて調べてみたくらいだし。
0299デフォルトの名無しさん
垢版 |
2018/09/17(月) 13:39:23.91ID:F2vzl5VC
最初に指摘されたことだろうに。
アセンブラレベルで精度や効率に介入したきゃ.netなんて使うな、なんて分かりきったこと。
0300デフォルトの名無しさん
垢版 |
2018/09/17(月) 14:58:46.52ID:zCVYDMXL
エディタ使ってるとたまに Intellisense機能が効かないときがあるんだが
あれなんなの?
中間ファイルとか消せば直るの?
0303デフォルトの名無しさん
垢版 |
2018/09/17(月) 18:30:58.83ID:+dwRu2dr
さて俺の本番コード、以下のようだ。
疑問は解消した。協力してくれた皆様ありがとう。

◎:拡張倍精度、○:倍精度、として、(ソースは>>191参照)
・Releaseビルドをコマンドプロンプトから起動→◎積和、◎平方根
・Debugビルドをコマンドプロンプトから起動→◎積和、○平方根
・IDEから起動→○積和、○平方根

これで3種類出来上がってた。
(なお、>>166内バイナリをアタッチした際の「AまたはC」は、「AまたはB」の間違い)
そしてIDE上で『[モジュールの読み込み中に JIT 最適化を抑制する]』を変更すると、
確かにRelease/Debugの2種類に絞れる。
Debugだからといって、全く最適化がかからないわけでもないようだ。
(1行内なら最適化がかかる?)

参考に、Releaseビルドの該当部分の逆アセンブルは以下。
積和が拡張倍精度で行われ、そのまま fsqrt で平方根が取られる。
(関数ごとインライン化されているのでアドレスが中途半端だが)
double retval = calc_norm_and_regulate(count, vec, false);
0000003e fldz
00000040 xor edx,edx
00000042 test esi,esi
00000044 jle 00000056
00000046 lea eax,[esp+28h]
0000004a fld qword ptr [eax+edx*8]
0000004d fmul st(0),st
0000004f faddp st(1),st
00000051 inc edx
00000052 cmp edx,esi
00000054 jl 00000046
00000056 fsqrt
00000058 fstp qword ptr [esp+10h]
0304デフォルトの名無しさん
垢版 |
2018/09/19(水) 17:24:44.24ID:GzaYiinu
VC++2008だけど、突然一部のファイルだけブレークポイントが入らなくなった・・・
.ncb消したり、そのプロジェクトだけリビルドしたけど直らない
全リビルドすればおk?
0305デフォルトの名無しさん
垢版 |
2018/09/19(水) 18:59:08.26ID:KTUM/Tgt
>>304
根本的な解決策とは違うが
「ブレークポイントは現在の設定ではヒットしません。ソースコードが元のバージョンと異なります。」
なら
[オプション] の[デバッグ]から[元のバージョンと完全に一致するソース ファイルを必要とする] をオフで
0306デフォルトの名無しさん
垢版 |
2018/09/20(木) 12:44:39.12ID:rLmEYUhk
>>305
ありがとうございます。

結局、全リビルドでも直らなかったので、.sln と .vcproj 以外全部消して
Windowsも再起動して完全にまっさらな状態にして全ビルドしたら直りましたが、
そのうちまたブレークポイントが入らなくなったので、>>305の方法で
回避しました。
0307デフォルトの名無しさん
垢版 |
2019/06/19(水) 14:12:16.93ID:Yoy0IPRe
お、おう
0308デフォルトの名無しさん
垢版 |
2019/10/04(金) 14:14:25.88ID:gozwERy2
C++ CLRのWindowsフォームアプリ作ってると、
イベントとか記入するときインデントがおかしい(スペースが1つ付く)んですが、
これを直す方法ってありますか?
0311デフォルトの名無しさん
垢版 |
2021/04/20(火) 20:00:45.05ID:rVJPu0WM
わからないので教えてください。
シリアル通信をするプログラムを作りたく、
VS2008にて、「SerialPort」を使え、というのが
サイトにあったのですが、
ツールボックスにSerialPortがありません。
追加する方法を教えてください。

使用環境:VS2008 C++ MFCアプリケーション
0313デフォルトの名無しさん
垢版 |
2021/04/23(金) 21:39:54.21ID:OsunfFOw
>>311
SerialPortは使ったこと無いから知らんが、
その程度のことを自力で解決出来ない奴が今更VS2008でしかもMFCとか無理だ。
サイトを参考にするのもいいが、日付は必ず見るようにしろ。
.NET serial port で検索すると以下が当たるし、今なら普通にこれだと思うが。
https://docs.microsoft.com/ja-jp/dotnet/api/system.io.ports.serialport?view=dotnet-plat-ext-5.0


どうせ見えた展開だし、面倒だから先に言っておくが、
お前のやってることは初心者のあるある

・そもそもやり方を間違っているのに、それ以外の解決方法を認めない
・正しいやり方を教えても、それは聞いてないとして受け付けない

だ。だからどうせこれから文句も言うのだろうが、重ねて言うが、
今更その環境(VS2008+MFC)はあり得ないくらいの異常さだ。
お前が初心者で何も知らないだけなら、常に最新の環境を使うように心がけろ。それだけで無駄なことに嵌りにくくなる。
その環境に拘る何らかの理由があり、それを強いてきた上司等が居るのなら、まずそいつに聞け。
聞く人が居ないのなら、今のお前にその環境でやりきる能力はないから諦めろ。
■ このスレッドは過去ログ倉庫に格納されています

ニューススポーツなんでも実況