Closed haruyama480 closed 8 months ago
ありがとうございます!その辺は確認しているんですが、パソコンをMacに変えて環境が変わったりして直せずにいます。。。
また、サーバー側のtravisが時間制限(30分くらい?)みたいなのがあって途中で止まってしまったりもしています。もし、直し方をご存知でしたら教えていただけたらと思います。
調査していたらrosetta errorと出たので、Rosettaかnimコンパイラが吐くバイナリが悪いか、がありそうです(根が深そう)
以下調査ログ とりあえず代表的にscc_test.nimを実行。large_cycle_00.out が失敗している
rosetta error: unexpectedly need to EmulateForward on a synchronous exception
% .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out < /Users/xxx/ghq/github.com/haruyama480/Nim-ACL/src/.verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/test/example_00.in
4
1 5
2 1 4
1 2
2 0 3
% .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out < /Users/xxx/ghq/github.com/haruyama480/Nim-ACL/src/.verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/test/large_cycle_00.in
rosetta error: unexpectedly need to EmulateForward on a synchronous exception x86_rip=0x4304811968 arm_pc=0x4305005336 num_insts=4 inst_index=2 x86 instruction bytes: 0x6215344901283465301 0x9604197274868732993
zsh: trace trap .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out <
Mac使っていますか?
前にUbuntuを使っていた頃は通っていたはずなので環境依存かもしれません。Apple Siliconだとlong doubleが64bitになってしまうみたいな罠とかもあったと思います。
はい、M1 Macです
やはりそうでしたかーこちらはM2 Macを使っていて同じように前通っていたものが通らなくなって原因もわからない状態でしたね。誤差ジャッジ系が落ちることが多いので、浮動小数を使わないsccとかtwosatとかは通っても良さそうです。
M2もだめなんですね😞
確かにfloat使ってないので浮動小数点は関係ないかもですね
(rosetta周りを疑っていたんですけど、かなり辛そうでした😇)
nimはCPUをx86-64系 (少なくとも非arm)と認識していそう
% nim --version Nim Compiler Version 1.6.14 [MacOSX: amd64]
下のエラーログを見るに、x86の命令をarmの命令にエミュレートし、その実行時に同期例外?というのが出ているよう
rosetta error: unexpectedly need to EmulateForward on a synchronous exception x86_rip=0x4304811968 arm_pc=0x4305005336 num_insts=4 inst_index=2 x86 instruction bytes: 0x6215344901283465301 0x9604197274868732993
事前知識として以下の記事を思い出しました。 apple siliconはRosetta2によりx86-64バイナリをAArch64(arm)のバイナリに変換し、TSOモードによってパフォーマンス良く実行している、ということだけ読み取っています。正直よくわかっていませんが、エミュレーションしているというのは事実です。 https://yamasa.hatenablog.jp/entry/2020/12/07/041649
変換前のバイナリが悪いのか、変換後のバイナリが悪いのか...
前者であれば、以下の命令周辺の命令列が正しくなさそうです。
x86 instruction bytes: 0x6215344901283465301 0x9604197274868732993
後者であれば、変換後のarmの命令列が正しくなさそうなのですがこれはどうやって確認できるのか そもそもバイナリが見れても正しくない、と判定することが可能なのか
かなりしんどそうで何もわからないかもしれませんが、暇なときにx86-64のバイナリを見てみようと思います
objdumpで逆アセンブルしましたが、なんにもわかりませんでした
% objdump -d -S -M intel .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out > out
もっと簡単なプログラムで再現できないですかねー
そういえば、amd64と表示される件についてはこちらを見つけましたよ
https://forum.nim-lang.org/t/7823
拙者のM2 mac miniもamd64となっていますね。
このページのbuild.shをやると最初CPU: amd64と表示されますね。。。
macなのにターミナルでuname -mを実行するとx86_64となるのですが、これはおかしいですかね
nimコンパイラをarmバイナリにビルドすることができるんですね!
公式からダウンロードするとx86_64バイナリのダウンロードになるので、結果が変わりそうですね https://nim-lang.org/install_unix.html
試してみます
choosenimではなくbrew install nimでやればいいんですかね
2.0.0が入りましたね!
% which nim
/opt/homebrew/bin/nim
(venv3.10) R-MBP14UBN-0441 [which nim] ~/ghq/github.com/nim-lang/nim 12:12AM
% nim --version
Nim Compiler Version 2.0.0 [MacOSX: arm64]
Compiled at 2023-08-01
Copyright (c) 2006-2023 by Andreas Rumpf
active boot switches: -d:release -d:nimUseLinenoise
Araq氏曰く
The next major release will officially support the M1. (I use Nim on it successfully on a daily basis.)
version 2.0.0 から公式でサポートしているので、1.6の場合は自分でビルドしないといけない? choosenim便利なんですけどね
一応、提出前にローカルで試すとかのときはatcoderとバージョンを統一したいですねー2.0.0でoj-verifyやるとどうなるのでしょうか?
失敗しました
しかし、エラーがセグフォに変わっていました
% .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out < ~/ghq/github.com/zer0-star/Nim-ACL/src/.verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/test/large_cycle_00.in
zsh: segmentation fault .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out <
バイナリはarm64が吐かれていることを確認
% lipo -archs .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out
arm64
% .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out < ~/ghq/github.com/zer0-star/Nim-ACL/src/.verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/test/large_cycle_00.in
rosetta error: unexpectedly need to EmulateForward on a synchronous exception x86_rip=0x4329093056 arm_pc=0x4329286428 num_insts=4 inst_index=3 x86 instruction bytes: 0x6215344901283465301 0x9604197274868732993
zsh: trace trap .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out <
% lipo -archs .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out
x86_64
セグフォなら、debugモードでコンパイルすればどこでセグフォかを調べることができますかねーこちらでも再現するか試してみますかね。
lldbよくわかってませんが、lldbからセグフォが見れました
% lldb .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out
(lldb) target create ".verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out"
Current executable set to '/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/.verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out' (arm64).
(lldb) settings set target.input-path /Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/.verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/test/large_cycle_00.in
(lldb) process launch
Process 38813 launched: '/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/.verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out' (arm64)
Process 38813 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x16f603fd0)
frame #0: 0x0000000100009658 a.out`add__OOZatcoderZinternal95scc_u732(tySequence__qwqHTkRvwhrRyENtudHQ7g&, long long)
a.out`add__OOZatcoderZinternal95scc_u732:
-> 0x100009658 <+0>: stp x24, x23, [sp, #-0x40]!
0x10000965c <+4>: stp x22, x21, [sp, #0x10]
0x100009660 <+8>: stp x20, x19, [sp, #0x20]
0x100009664 <+12>: stp x29, x30, [sp, #0x30]
Target 0: (a.out) stopped.
debugモードでコンパイルするとNimのコードの何行目がセグフォになってるみたいなのわかってそっちの方が手がかりになりませんかね?
逆アセンブルすると、__Z34add__OOZatcoderZinternal95scc_u732R34tySequence__qwqHTkRvwhrRyENtudHQ7gx
のstack popで死んでる?
% otool -tvV .verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/a.out | grep -C 5 100009658
0000000100009648 ldp x29, x30, [sp, #0x20]
000000010000964c ldp x20, x19, [sp, #0x10]
0000000100009650 ldp x22, x21, [sp], #0x30
0000000100009654 ret
__Z34add__OOZatcoderZinternal95scc_u732R34tySequence__qwqHTkRvwhrRyENtudHQ7gx:
0000000100009658 stp x24, x23, [sp, #-0x40]!
000000010000965c stp x22, x21, [sp, #0x10]
0000000100009660 stp x20, x19, [sp, #0x20]
0000000100009664 stp x29, x30, [sp, #0x30]
0000000100009668 add x29, sp, #0x30
000000010000966c mov x19, x1
debugモードでコンパイル試してみます
わからないですけど、配列の範囲外アクセスとかですかねーarmの場合だけセグフォになるというのがわかりませんが。。。
% nim c --debugger:native -o:a.out verify/scc_test.nim
% ./a.out < ~/ghq/github.com/zer0-star/Nim-ACL/src/.verify-helper/cache/a335ee98513bf56941c9dd2906bfa782/test/large_cycle_00.in
Traceback (most recent call last)
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/verify/scc_test.nim(13) scc_test
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/atcoder/scc.nim scc
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/atcoder/internal_scc.nim(59) scc
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/atcoder/internal_scc.nim(53) scc_ids
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/atcoder/internal_scc.nim(40) dfs
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/atcoder/internal_scc.nim(40) dfs
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/atcoder/internal_scc.nim(40) dfs
(略)
(1874 calls omitted) ...
(略)
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/atcoder/internal_scc.nim(40) dfs
/Users/xxx/ghq/github.com/zer0-star/Nim-ACL/src/atcoder/internal_scc.nim dfs
Error: call depth limit reached in a debug build (2000 function calls). You can change it with -d:nimCallDepthLimit=<int> but really try to avoid deep recursions instead.
オプション変えてビルドし直します
debugモードだと2000回しか再帰できないですね
なるほどです..
% nim c --debugger:native -d:nimCallDepthLimit=100000 -o:a.out verify/scc_test.nim
Hint: used config file '/opt/homebrew/Cellar/nim/2.0.0_1/nim/config/nim.cfg' [Conf]
Hint: used config file '/opt/homebrew/Cellar/nim/2.0.0_1/nim/config/config.nims' [Conf]
Hint: used config file '/Users/kazusaku/ghq/github.com/zer0-star/Nim-ACL/config.nims' [Conf]
...............................................
/opt/homebrew/Cellar/nim/2.0.0_1/nim/lib/system/excpt.nim(558, 21) Error: cannot convert 100000 to int16
65536にしても溢れちゃいました
65536以上にはできないんでしたっけ?競プロで使われるN=200000くらいは必要で余裕を持って1千万くらいにしといたほうがいいのではないですかねー
デバッグモードを使うと例外をスタックする関係であまり大きいのは許容してないんですかね >int16
dfsでコケているわけではなく大きいテストケースでデバッグモードが動かない 小さいテストケースあると良さそう
あとは、lldb 習得して落ちるケースを見つける方針もある
やっぱり再帰し過ぎ(スタック積みすぎ?)るとARMだとなにか問題が起こるということなんですかね。
ありえそうですね
EXC_BAD_ACCESS (code=2, address=0x16f603fd0)
とやらを見るにポインタが壊れているようにも見えます
明日また見てみます
ulimitに引っかかっていただけでした〜〜〜
無事scc_testと一部のテストは通りました
以下を指定するとscc_testなどは通るようになりました。また、x86-64版のnimコンパイラ(1.6.6)でも通りました。
ulimit -s 65000
そもそもoj-verifyがulimitについてのwarningを出していました どうやらmacOS catalina(10)以降で、python のresource.setrlimit()が失敗するようでした https://bugs.python.org/issue40518
このため深いスタックが作れずにセグフォが起きていたようです。(デバッグモードはnimコンパイラの制約なので別の話)
armで起きてintelで失敗しなかった理由は、想像なんですが、命令セットとアーキテクチャが違うのでarmのほうがスタックが深くなる可能性があるのかと思いました。intel cpuでもulimitに同じ制約があったので、テストケースによってはひっかかっていたかもしれません。
スタックポインタでセグフォが起きていたあたりから疑えればよかったです。rosettaにバイアスかかってしまいましたすみませんmm
以下のverifyがまだ通っていないので引き続き調査する必要がありそうです
ERROR:onlinejudge_verify.verify:3 tests failed
ERROR:onlinejudge_verify.verify:failed: verify/extra/geometry/aoj_cgl_2_d_distance_test.nim
ERROR:onlinejudge_verify.verify:failed: verify/extra/math/pow_of_formal_power_series_test.nim
ERROR:onlinejudge_verify.verify:failed: verify/extra/math/yosupo_factorization_test.nim
他の問題は別のissueに分けようと思います
このissueは、ulimitの件として引き続き開いておきます
Nim-ACLできることは、verifyの前にulimit -s
しましょうなREADME.mdを追記するぐらい?
https://github.com/zer0-star/Nim-ACL/issues/50
verify/extra/math/yosupo_factorization_test.nim
が通りました
以前伺った、apple siliconだとlong doubleが倍精度になってしまうという話は本当ですね..
Armの Procedure Call Standard for the Arm 64-bit Architecture ではlong doubleはbinary128(四倍精度)と定められていますが、Appleはこの規定を上書きして、long doubleはbinary64(倍精度、doubleと同じ)としました。 https://qiita.com/mod_poppo/items/fb18f2a1441e74af29a3
アーキテクチャはlong doubleを4倍精度としているのに、OSが倍精度と上書きしてしまっているため回避策もなさそう
verify/extra/geometry/aoj_cgl_2_d_distance_test.nim
通すのは諦めよう
verify/extra/math/pow_of_formal_power_series_test.nim
は別issueにしました
https://github.com/zer0-star/Nim-ACL/issues/52
以前伺った、apple siliconだとlong doubleが倍精度になってしまうという話は本当ですね..
Armの Procedure Call Standard for the Arm 64-bit Architecture ではlong doubleはbinary128(四倍精度)と定められていますが、Appleはこの規定を上書きして、long doubleはbinary64(倍精度、doubleと同じ)としました。 https://qiita.com/mod_poppo/items/fb18f2a1441e74af29a3
アーキテクチャはlong doubleを4倍精度としているのに、OSが倍精度と上書きしてしまっているため回避策もなさそう
verify/extra/geometry/aoj_cgl_2_d_distance_test.nim
通すのは諦めよう
Nim側ではlong doubleは公式にはサポートしていなくて使っていない(C++コードを埋め込めば使えなくもない)はずなのですが、正解データを生成する際のC++側でlong double使っているんですかね?
@chaemon long double関係なさそうだったのでReopenします
直したいケースを特定できました
include atcoder/header
import atcoder/extra/geometry/geometry_template
import atcoder/extra/other/internal_complex
var p = newSeqWith(4, initPoint(0.0,0.0))
p[0] = initPoint(100.0, 9)
p[1] = initPoint(-51.0, -42)
p[2] = initPoint(24.0, 99)
p[3] = initPoint(-107.0, 15)
let
s1 = initSegment(p[0], p[1])
s2 = initSegment(p[2], p[3])
echo distance(s1, s2) # 実際の出力 79.90619500389191
let r = s1.b.projection(s2)
echo abs(r - (s1.b)) # 期待出力 78.21068534399855
echo intersect(s2, s1.b) # false. trueになってほしい
echo ccw(s2.a, s2.b, r) # CLOCKWISE. ON_SEGMENTになってほしい
floatの精度は不完全なので、(float, float)の3点がうまく直線判定できません。 言い換えると、floatで扱う以上、SegmentとPointのintersectは必然的に不完全な実装になってしまいます。
なので、SegmentとPointのdistanceは上記のintersect無しに実装するのが良さそうです。
具体的には、以下で修正できそう?
頂いたサンプルで調べてみたところ、EPSが10^-12と非常に小さく設定されていてccwの判定においてON_SEGMENTではなく、CLOCKWISEとなってしまっていることがわかりました。10^-10くらいにすると全部通るようです。EPSをいくつにするのがいいか悩みどころです。
幾何の実装についてあまり詳しくないのですが、修正いただいたような方法ですとfloatの比較でEPSを使っていなくてロバスト性がなくて問題が起こる気がします(うまく言い表せないですが)
また、現状のditanceの実装でON_SEGMENTかCLOCKWISEかの違いで値が飛んじゃう実装になっているのでそこも直さないとかもです。
oj-verifyが失敗していました
環境
失敗したverify