アセンブラ初心者スレッド 2©2ch.net
■ このスレッドは過去ログ倉庫に格納されています
>>108 通常のプログラムが扱えるのは論理的なアドレスだからな 関係なくないよ >>85 の mov al,my_mojiretu[rbx] ;3 のような下位32bitの絶対アドレスで指定する場合、扱えるデータの量が減る 上の例では0から2GBの範囲しかアクセスできないのに 論理アドレスで1GB付近からロードされたら扱えるスタティックに割り当てられたデータの量が極端に減ってしまう だからこそ >>85 の mov al,my_mojiretu[rbx] ;3 のようなコードを書くとリンカがエラーを吐くようになってるんだろうね 下位32bitの絶対アドレスで指定するなと ほとんどのCPUでは64bitのアドレスを直接指定する方法は限定されてて、実行速度も遅くなる だからWindowsやLinuxでは64bitでもデフォルトではスタティックに割り当てられたシンボルは だいたい32bitの値として扱ってる (Linuxではコンパイラのオプションでメモリモデルを指定できて スタティックなシンボルを64bitの値として扱うこともできる) 64bit Windowsの場合、かなり高位のアドレスにロードされるようだから RIP相対の32bitの値として扱ってるのだろう ちなみにこれはスタティックに割り当てられたデータだけで動的に割り当てられたデータは 64bitのポインタ値で扱われるのでユーザプログラムが扱える全仮想メモリ領域に配置できる 64bitARMの場合、Linuxではスタティックなシンボル値読み込む場合 adrp x0, :pg_hi21:hogehoge add x0, x0, :lo12:hogehoge これで読み込む これで33bitのページ単位でPC相対のアドレスを読み込める つまり64bitのARMではスタティックなシンボル値は33bitのPC相対アドレスとして扱ってる (相対なのはページ単位なので下位12bitは動かせない、下位12bitは絶対アドレス値を足してるので) 64bitのARMのadrp命令と同等の命令はRISC-Vや最近発表されたnanoMIPSなどでも採用されてる RISC-VやnanoMIPSは33bitではなく32bitだが スタティックなシンボル値の制約はアセンブラではなくあくまでコンパイラの仕様だが (コンパイラ側でシンボルを扱う場合にあえて下位32bitしか読み込まない仕様にしてる) 最近のアセンブラプログラミングでは高級言語とリンクすることが多いので必須な知識 >>111 > ちなみにこれはスタティックに割り当てられたデータだけで動的に割り当てられたデータは > 64bitのポインタ値で扱われるのでユーザプログラムが扱える全仮想メモリ領域に配置できる > ほとんどのCPUでは64bitのアドレスを直接指定する方法は限定されてて、実行速度も遅くなる つまりこういうこと? ・動的に割り当てられたデータは64bitでアクセスできるけど実行速度が遅くなる ・静的に割り当てられたデータは32bitでアクセスするように制限されてるけど実行速度は速い。 ・ただしLinuxの場合、メモリモデルを指定して再コンパイルすればこの制限はなくなる >>115 >・動的に割り当てられたデータは64bitでアクセスできるけど実行速度が遅くなる そもそも動的なデータは64bitのポインタで管理されるので最初から全アドレスにアクセスできる >・静的に割り当てられたデータは32bitでアクセスするように制限されてるけど実行速度は速い。 違う、あくまでシンボルを32bitのアドレスとして読み込んでるだけでデータサイズは関係ない x86_64でも64bitのアドレスを指定して読み込む命令は限られてるし、 実行速度の面でも効率が悪いのでRIP相対で32bit分だけを使ってる たとえば、 mov rcx, data01 とした場合、data01の部分はRIP相対の32bitアドレスになる、読み込まれるデータは64bitの値 RISC CPUだとアドレスを読み込んでからデータに読み書きするという2段階になるので たとえば、64bitのARMだと adrp x0, :pg_hi21:data01 add x0, x0, :lo12:data01 ldr x0, [x0] と下位33bitでPC相対アドレスとしてシンボルが読み込まれる RISC-Vの絶対アドレスだと lui a0, a0, data01 addi a0, a0, data01 ld a0, (a0) PC相対だと 1: auipc a0, %pcrel_hi(data01) addi a0, a0, %pcrel_lo(1b) ld a0, (a0) こうなる (%pcrel_lo()では対応するpcrel_hi()のラベルを指定) RISC-Vの絶対アドレスのところが間違ってた 不正解 lui a0, a0, data01 addi a0, a0, data01 ld a0, (a0) 正解 lui a0, %hi(data01) addi a0, a0, %lo(data01) ld a0, (a0) >>116 アドレッシングの話をしているだと思ったのではしょったけどこう書けばいいですかね ・動的に割り当てられたデータは64bitアドレッシングでアクセスできるけど実行速度が遅くなる ・静的に割り当てられたデータは32bitアドレッシングでアクセスするように制限されてるけど実行速度は速い ・どちらの場合も読み書きできるデータサイズに制限はない ・ただしLinuxの場合、メモリモデルを指定して再コンパイルすればこの制限はなくなる 何で、動的に割り当てられた変数と静的に割り当てられた変数の アクセス速度を比較するのか意味不明だが 一番アクセスが速いのはローカル変数だと思うぞ ローカル変数はdisplacement付きのスタックポインタ間接アドレッシングでアクセスできるので ほとんどのCPUでdisplacementが届く範囲なら1命令でロード、ストアができるからね >>119 今までの話の流れで言うと、速度を比較するのが目的ではなくて、 なぜ静的に割り当てられたデータは32bitアドレッシングでアクセスするように制限されているのか? ですよ。普通に考えたら64bitアドレッシングが制限なくできて当たり前だろ。なんで?ってことです。 x86の64bitモードでのアドレッシングは全て64bit >>120 命令サイズを節約してるだけ バラバラにコンパイルされた.oに対して後から命令長は変えられないんでデフォでそうなってる 変えたければラージモデルの類のコンパイルオプションがあるはず >>120 64bitのデータだろうが32bitデータだろうがデータがキャッシュに入ってれば キャッシュからデータを読み出す速度は変わらないが、 64bitアドレスを即値で指定すると CISC CPUの場合、64bitアドレスの分、命令のオペランドの長さが長くなるので 今時の複数の命令を同時発行できるCPUだと一度に発行できる命令の数が減るので遅くなる 64bitのアドレスだけで8バイトにもなるからね RISC CPUの場合は64bitのアドレスを一度に読み込めないので複数の命令に分けて読み込むが 64bitのアドレスを読み込むと命令数が増える 結局、静的データやジャンプ先のラベルはは32bitアドレスに限定したほうが結果的に速くなる たいていのプログラムで静的データやジャンプ先のラベルで 32bitの範囲を超えるような応用プログラムは極一部の分野だけだしね その極一部の応用プログラムのために性能を落とす必要はない スパコンでも使われるLinuxではメモリモデルとして 静的データやジャンプ先のラベルを64bitで扱うようにコンパイルするオプションがある このページはPGIのコンパイラのページだがわかりやすく説明してある https://www.softek.co.jp/SPG/Pgi/TIPS/opt_64.html はこのページ ARMやx86_64、POWERのgccでも-mcmodel=largeや-mcmodel=mediumがあったりする (ARMでは-mcmodel=largeはあるが-mcmodel=mediumはない) https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html https://gcc.gnu.org/onlinedocs/gcc/RS_002f6000-and-PowerPC-Options.html >>104 確か、そのリンカオプションは、DLL作成時に指定するとエラーになる。 また、アプリの場合だと、relocation 情報が strip されて、EXE ファイルの中にはなくなっているのに対し、DLLだと relocation 情報が最後まで残されたままになっている。 >>105 あなたが使っている用語が x86 や x64 とは合わずめちゃくちゃなので何を聞いているのか分からないが、 hogehoge db 'xxxx', 0 mov cl, hogehoe 1 とすると、 mov cl,[offset hogehode] と言う意味になり、encode としては、 mov cl, [RIP + (offset hogehode - offset label1)] label1: のように、RIP 相対の disp mov cl, [RIP + disp32] に encode される。この場合の disp32 は、意味としては、rel32 で、 RIP からの32BIT相対アドレス。 一方、似て非なるものとして、 mov cl, hogehode[rbx] とすると、 mov cl, [offset hogehode + rbx] 即ち、 mov cl, [rbx + disp32] と翻訳される。この場合の、disp32 は、32BIT絶対アドレス。 同じ、disp32 でも、意味は異なる。しかし、disp は、32BITまでで、 64BIT 値のものは存在していない。一方、64BITアドレスも一応は使えるように、 mov REG64, ADDR64 ; REG64 は、RAX, RBX, RCX, EDX, RBP, RSI, EDI など。 や mov ACC, [ADDR64] ; ACC は、al, ax, eax, rax という命令が、 専用命令として特別扱いとして存在しているが、特殊中の特殊。 >>124 訂正: hogehoge db 'xxxx', 0 mov cl, hogehoge とすると、 mov cl,[offset hogehoge] と言う意味になり、encode としては、 mov cl, [RIP + (offset hogehoge - offset label1)] label1: のように、RIP 相対の disp mov cl, [RIP + disp32] に encode される。この場合の disp32 は、意味としては、rel32 で、 RIP からの32BIT相対アドレス。 ちなみに、masm では、 hoge1 db 'aaa' ;1 と hoge2: db 'aaa' ;2 は明確に区別されており、hoge1 は、byte 型の配列変数のような取り扱いになり、 mov al, hoge1 ;1 は、masm には通らないが、旧来のアセンブラの感覚で言えば、意味的には、 mov al, qword [offset hoge1] ;3 となり、hoge2 の方は、直後の db 命令とは全く関係の無い単なる near アドレスと解釈され、 mov rax, hoge2 ;4 は、意味的には、 mov rax, offset hoge2 ;5 となる、ただし、4は、もしかするとそれ自体が文法上、masm の構文に合わないかもしれない。 masm は、「マクロアセンブラ」であって、通常のアセンブラとは結構異なるので。 >>125 誤: mov al, qword [offset hoge1] ;3 正: mov al, byte [offset hoge1] ;3 これも、masm 流には、 mov al, byte ptr [offset hoge1] ;3 nasm 流には、 mov.b al, [offset hoge1] ;3 みたいな感覚。みたいなだけで、実際にはそのまま書くと、エラーになる かも知れない。 >>124 さらに補足すると、 mov cl, [rbx + disp32] の disp32 は、rbx の中身によって意味が変わってきて、 1. disp = 32BIT 相対アドレス (rbx が絶対アドレスの場合) 2. disp = 32BIT 絶対アドレス (rbx が相対アドレスの場合) となる。2. の例としては、 rbx = 配列添え字 * (配列の要素のバイト数) のような場合。1. の例としては、 rbx = 構造体の先頭アドレス のような場合。 >>127 今やってみたが 64bitアプリを通常にコンパイルした場合のWinMainのアドレス 0x000000013f291770 /largeaddressaware:noを付けてコンパイルした場合のWinMainのアドレス 0x0000000001241770 /largeaddressaware:noを付けるとプログラムがロードされるアドレスが変わる 64bitのアプリを通常にコンパイルするとWinMainのアドレスが0x000000013f291770 およそ先頭から5GBの位置 つまり、32bit絶対アドレスでは届かない位置にプログラムがロードされてる /largeaddressaware:noをつけると先頭から18MBくらいの位置 エラーが出るのは32bit絶対アドレスでは届かない位置にロードされる可能性があるからだろうな 補足: /largeaddressaware:noを付けると64bitアプリでもメモリは2GBまでしか使えなくなる つまり、>>128 が示してることは Windowsの64bitアプリでは mov al,my_mojiretu[rbx] のような書き方はしてはいけないということ >>124 XvleiWNbとは別人だよね? /LARGEADDRESSAWARE:NOの辺りからなんか違和感あって、「そうなん?」って感じだったんだけど。 ARMやコンパイラの仕様だとかまで出てきて訳わからなくなるところだったよ。 >一方、似て非なるものとして、 > mov cl, hogehode[rbx] >とすると、 > mov cl, [offset hogehode + rbx] >即ち、 > mov cl, [rbx + disp32] >と翻訳される。この場合の、disp32 は、32BIT絶対アドレス。 同じ、disp32 でも、意味は異なる。 >Windowsの64bitアプリでは >mov al,my_mojiretu[rbx] >のような書き方はしてはいけないということ 今までなんで絶対アドレスが出てくるのか疑問だったけど、コンパイラはこういうコードは吐かない、ってことだよね。 >>131 >>124 XvleiWNbとは別人だよね? 別人。ARMは使ったこと無い。 >>130 でも現実は、複雑。 なぜなら、COFFの仕様的には、obj ではない Image(EXEやDLLの事) のためだけに ある .reloc section には、64BIT 絶対アドレスの再配置も行えるようになっているから。 現状の MS 製の link.exe がどうなっているかはともかく。 >>133 すまん。間違った。 mov al,my_mojiretu[rbx] は、意味的には、 mov al, [rbx + offset my_mojiretu] となって、最後は、 mov al, [rbx + disp32] となるが、disp32 の部分は、disp64 の命令は存在していないので、 .reloc section が 64BIT 絶対アドレスに対応していても、無理だった。 勘違いした。 >>128 一応、論理的には、WinMain は、code (.text) section に置かれる。 my_mojiretu みたいなものは、.data section (など)に置かれる。 my_mojiretu みたいなものは、初期化(初期値)データなので、 2GB も使えれば十分ではあるはず。もし、2GB を超えるのなら、exe ファイルの サイズも 2GB を超えるはず。 という事で、初期化データのおかれた section のアドレスが、2GB 未満 に置かれるならなんとかなるはず。 というか、通常、exe ファイルは、relocation 情報が strip されているので、 確か、Optional Header の BaseOfCode に配置したいアドレスを入れておける。 だから、その値を小さめにしておけば、初期化データのアドレスを 2GB 未満 の位置に配置する事はそんなに難しくないはず。 確か、32BIT 時代は、0x40000 位の値だった。 >>135 正しくは、BaseOfCode ではなく、ImageBase の方だった。 正しい値は、0x40000 ではなく、1桁大きい、0x400000 だった。 64BIT COFF では、変わってるかもしれない。確か、PREFERED_BASE などという名前も記憶にある。 ImageBase Preferred address of first byte of image when loaded into memory; must be a multiple of 64K. The default for DLLs is 0x10000000. The default for Windows CE EXEs is 0x00010000. The default for Windows NT, Windows 95, and Windows 98 is 0x00400000. >>128 自分の勘だと、そのアドレスは、WinMain よりも、DLL の DllMain が置かれるような 値になってるね。 不思議だ。WinMain をそんな大きなアドレスに置く必要性は余り無いハズなので。 初期化データが 2GB 未満に置かれていても、malloc() や、new で確保されるデータは、 64BIT アドレスにできるはずだし。なお、 1_3f29_1770 ↑は、32BIT を超えて、33BIT の値だな・・・。なんちゅう大きな値だろう。 64bitもアドレス空間があるんだから 多少大きくても何の問題も無いだろ >>134 32bitのフラットメモリモデルだと、静的変数はイメージにはオフセットを入れといて、 実行時にローダで書き換えてたんだと思うんだけど、64bitでは絶対アドレスは制約が大きくなるから変だと思ったんだ。 絶対アドレスはコンパイラでも使えなくはないけど、デバイスドライバでメモリマップトI/O操作するような時しか使わないと思う。 64bitでイメージベースが大きくなったのは、セキュリティ関係でランダマイズしたりとか、 mov al,my_mojiretu[rbx] みたいなコードが例外吐いて特定困難なバグが発生するのを防止してるのでは? >>135 コマンドライン用の簡単なC言語の64bitのプログラムで試したが main関数のアドレス=0x000000013f7f1000 data sectionで定義した変数data01のアドレス=0x000000013f7fc087 だったぞ 完全に32bit絶対アドレスの範囲を超えてる 64bitのWindowsアプリを作って.data sectionの変数のアドレスも表示してみた WinMain address = 0x000000013fd419a0 data sectionの変数のアドレス data01 address = 0x000000013fd4d000 /largeaddressaware:noを付けた場合 WinMain address = 0x00000000013619a0 data sectionの変数のアドレス data01 address = 0x000000000136d000 >>139 >64bitでイメージベースが大きくなったのは、セキュリティ関係でランダマイズしたりとか、 >mov al,my_mojiretu[rbx] >みたいなコードが例外吐いて特定困難なバグが発生するのを防止してるのでは? 意味不明だ。別に、 mov al,my_mojiretu[rbx] というコードが悪いわけではない。 むしろ、最適化のためには使った方が効率が良くなる。 >>141-142 もしそうだとすると、VC++ の吐くコードがx64の命令を上手く使いきれてないという事になる。 本来であれば、アドレスの配置を上手く行うだけで、64BIT モードでも特に問題なく mov al,my_mojiretu[rbx] という命令は使えて、かつ、64BIT アドレスの制限を受けるわけでもないのだから。 ちなみに、アプリの EXE は、リンク後は固定アドレスで、ローダーがアドレスを再配置 する事はない。だから、ImageBase を小さい値になるようにリンクしさえすれば、 問題が生じない。 >>139 >64bitでイメージベースが大きくなったのは、セキュリティ関係でランダマイズしたりとか、 通常、コンピュータソフト、特にOSのセキュリティーというのは、そういう人間的なもの ではなくて、もっと厳密な物だ。 ランダマイズして撹乱して相手の目をくらます、などという方法は通常取られない。 実際、EXE ファイルを解析した経験からしても、ランダマイズなどは全く行われていない。 何回リンクしても、同じアセンブリソースや、同じC++ソースなら、全く同じアドレスになる。 異なるソースであっても、ベースアドレスなどは、ほとんどの場合、固定値。 >>138 実際には問題がある。なぜなら、そんなにアドレスが大きいと、さっきから話題の mov al, my_mojiretu[rbx] という命令が使えなくなるからだ。 これは、グローバルな配列変数を、添え字でアクセスするような場合に良いコードに なる事がある。僅かではあるが。もし、この命令が使えないとなると、 mov rdx,offset my_mojiretu mov al,[rdx + rbx] のように、2つの命令を使わなくてはならなくなり、最適化上、不利になる。 そんな事言い出したら 4GBや64KBに限定した方が小さいコードになるから compactモデルにしよう とかいう話にもなる 昔に戻りたい? 何のための64bit? >>147 だから、そういうことじゃなく、EXEファイルの中に、2GBを越える初期化データを 誰が入れたいかって話なんだ。 別に、ImageBase を小さい値にしていても、malloc() や、new するデータは、 6GBでも理論上は確保できるわけで、制限されるのは、EXEファイルの中の 初期化データのサイズが2GBまで、ってだけなんだ。 それで、使えるマシン語の間接オペランドの種類が1つ増やせる。 mov 命令だけでなく、add, sub, mul, div, idiv, lea, addps, addss などにも 全て影響する貴重な間接オペランド。 >>147 8086時代の 64KB では制限が大きすぎて、ほとんどのプログラムで、制限が足かせ になっていたので、32BIT になって、アドレスが 4GB に拡張されて非常に便利になった。 ところが、64BIT 時代になっても、そのときのようなメリットが無いと思うんだ。 AMD vs Intel の競争の結果出てきた産物かも知れない。そういうの、時々ある。 >>145 WindowsではASLRでランダム化普通にやってるらしいですよ http://07c00.hatenablog.com/entry/2013/08/07/033443 OSがプロセスをロードするときに、ランダムな場所にモジュールを配置するセキュリティ機能です。 実際はモジュールだけじゃなく、スタックやヒープなどもランダマイズされたりします。 >>150 少なくとも、EXE の .text, .data セクションは再配置される事は無いはず。 なぜなら、.reloc section が存在せず、再配置する事が原理的に出来なくなっているから。 DLL は、.reloc section が残されているので再配置できる。ただし、再配置しなくても、 全てのシステムのDLLは、最初からアドレスが重ならないような ImageBase になっている ので、再配置されずにそのままおかれるのが、昔は基本であった。 >>148 64bitコードで絶対番地に依存したコードとか 考え方が古いねえ ていうか、 イヤなら2GB限定のコードにすればいい コードもデータも2GBの範囲に割り当てられるから >>152 じゃあなんで、MS純正VSは、いまだに 32BIT コードで動いてるのさ。 x64 は、レジスタが増えた事と、memmove() などが多分、倍の速度で動く事が 最大のメリットじゃないの? アドレス幅が64BITになって現実的なメリットはどこにあるのかな? なにが「じゃあ」だか メリットが無いと思うなら2GB限定のアプリにすれば良い って書いたのが見えなかった? >>154 だから、メリットはあるよ。 レジスタが増えること、AVX, AVX2, AVX512 で、YMM, ZMM レジスタでベクトル の次元が大きくなったSIMD 命令が使えること、3オペランドの以上のSIMD命令が使える 事、メモリ転送の速度が倍近くになること。malloc(), new が、2GB を超えて行える事。 初期化サイズが2GBを越えるメリットは余り無いと思ってるけど。 >>149 アドレス空間は広いほうがいいに決まっている ユーザーアドレス空間の断片化は基本防止できない、だったら入れものが広いほういい >>148 結局、 mov al,my_mojiretu[rbx] こんなコードを書くと64bitではLINKする時にエラーが出る /largeaddressaware:noを指定するとLINKでエラーは出なくなるが、 /largeaddressaware:noを指定すると動的メモリも含め2GB以下のメモリしか扱えなくなる >>159 それでいいじゃん 何種類作るつもりだよ 32bit Windowsは切り捨てに入ってきてるからね ゲームでも64bit版のみのも出てきてる NVIDIA、GPUドライバーの32bit版OSサポートを終了へ 〜次期版からは64bit版だけに https://forest.watch.impress.co.jp/docs/news/1098673.html バーチャルYoutuberなんかでも使われるソフト(エロゲ)だが 64bit Windowsのみ対応 動画配信などで結構使われてる CUSTOM ORDER MAID 3D2 http://com3d2.jp/main.html OS Windows® 7/8.1/10 全て64bitのみ対応 ※6 ※6:32bitOSには対応しておりません。 >>162 いや、 32bit/64bitアプリの話じゃなくて 64bitの2GB制限/制限なしアプリの話 >>153 10年位前に出来たオンラインゲームでも、今はもう2GBのメモリだともう足りなくて 32bitのゲームでも/LARGEADDRESSAWAREのフラグ立てて延命してるのもあるよ 64bit WindowsならLAAフラグを立てると32bitアプリでも4GBまで使えるようになる LAAで延命してるゲームで32bit Windowsで遊ぶとメモリ不足で高確率で落ちるゲームとか既にある こんな記事が出るくらい、2GBではメモリが足りなくなってるアプリがたくさんあるぞ 64bit Windowsを前提とした32bitアプリケーション延命法 〜 LAAオプションで32bitアプリケーションのメモリ不足問題を解消 https://www.webtech.co.jp/blog/optpix_labs/programing/6387/ ちなみにこのブログ書いてるところはWindows用ソフトもいくつか出してるところだよ >>167 >>165 のこと? 一応、32bitWindowsもサポートしてるがメモリが足りなくなってよく落ちるので 実際は64bit Windowsでしかまともに遊べないってこと そういうゲームが既にある >>146 実際にどの程度、速度が変わるのか試してみて欲しいもんだな ただの机上の空論じゃないのか? 大体、配列で何回もデータにアクセスするなら 32bitのDISPを毎回指定するオーバヘッドだってあるんだが 32bitアプリも2G限定アプリも32bit OSも絶滅して良い Windowsの話 mov rcx, offset HOGEHOGE01 mov rax, qword ptr [rcx + rbx] mov rax, qword ptr [rcx + rbx] mov rax, qword ptr [rcx + rbx] mov rax, qword ptr [rcx + rbx] これと mov rax, HOGEHOGE01[rbx] mov rax, HOGEHOGE01[rbx] mov rax, HOGEHOGE01[rbx] mov rax, HOGEHOGE01[rbx] これを250個並べて1000万回ループさせてみたが、1%くらいしか差はないよ なんで訳分からなくなりそうになったのか理解できたw 4LRopBJnが変なんだな。 Microsoftが64bitでは絶対アドレスのインデックスは使わないって決めたんだから、その流儀に従うのが高級言語とリンクだよ。 >>167 メモリが厳しいなら、ヒープが断片化すればメモリ確保できなくなっても不思議じゃないよ。 それなら確率的に落ちると思う。 >>159 いや、それはあなたの勘違い。 そのオプションを指定しても、EXEの中に入っている「初期化データ」のアドレス が2GB以下に限定されるだけで、動的メモリは、理論上は、100GB でも一気に 確保できる。それは、再三言ってる。あなたは理解出来てないと思う。 >>164 再三言ってるけど、あの/LARGEADDRESSAWARE:NO オプションを 付けても、「初期化データ」のアドレスが2GBに制限されるだけで、 new や malloc() などの、ヒープから確保される動的メモリは、64GBの アドレス空間どこにあっても問題ないんだよ。 >>173 そりゃ分かってるよ。 でも逆に、EXEファイルの中に最初から入っている「初期化データ」 の2GB制限とどっちが良いかの話になるんだよ。もちろん、動的メモリ は何の制限も受けず、64GBアドレスが好きなように使える。 初期化データというのは、何かの埋め込みテーブルのための配列 だとか、文字列データとか、そういうものだよ。 巨大な3Dモデルデータ、テクスチャ、マップデータなどは、外部ファイル にでも置いておけば、2GBの制限は受けず、仮想アドレスとしては 64BITまるまる使えるから。 実際、EXEの中にある初期化データが2GBを越えるという事は、 EXEファイルが2GBを越えるという事だから、そのアプリの起動 時には、有無を言わさず HDDなどから、2GBの読み込みが 始まってしまうことになる。 >>179 >>180 【誤字訂正】 誤:64GBアドレス 正:64BITアドレス 言葉は間違っているが、中身は間違ってない。数学は100点、国語は赤点 の学生時代だったし。 >>173 それでもやはり、1%の速度低下にはなったんだよね。 「初期化データの2GB制限」は、現実的には何の制限にもなってない 事は理解出来てる???? 動的メモリは、64BIT 自由に使える んだよ。 >>178 、179 実際にやってみれば? C言語でmallocでもvirtualallocでも /largeaddressaware:noを付けると動的メモリも2GB以下しか確保できなくなるから 上のプログラムだと/largeaddressaware:noを付けない時に 無限に確保するので4500MBで終わるように少し改変してみた これで試してみな #include <windows.h> int PASCAL WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmd, int n) { int step = 10; //10MB int count = 0; char temp[20]; for (;;) { void *p = GlobalAlloc(GPTR, step * 1024 * 1024); if (p == NULL) { break; } count++; if(count > 400 + 50) { break; } } wsprintf(temp, "Total %d MB\n", count * step); MessageBox(NULL, temp, "alloc test", MB_OK); } 上のプログラムは64bitでも、32bitでもコンパイルは通るよ そもそもWindowsは/largeaddressaware:noを付けなくても静的データは2GBまでしか使えない 動的データだけがOSが対応してるメモリ分だけ制限なく使える WindowsにはLinuxのgccのような-mcmodel=largeや-mcmodel=mediumのようなオプションは存在しない /largeaddressaware:noを付けると64bitアプリでも動的メモリも含めてすべてのメモリで2GB以下までしか使えなくなる PGIのコンパイラのページでわかりやすく解説してあるよ https://www.softek.co.jp/SPG/Pgi/TIPS/opt_64.html >>173 これは最適化を知らないコード 250個並べちゃいけない >>182 そこだけ抜き出して、 しかも最適化してないコードで1% こんなコードは全体の中では非常にわずかだろうし タイムクリティカル部分であれば最適化される 実質差が無いメリットの為に絶対アドレスに依存したコードにする 時代に逆行だ それでも逆行したければ 0〜2Gのアドレスしか割り当てられない2G限定アプリにしろ >>188 250個並べても7KBくらい 1次命令キャッシュに丸ごと収まるぞ >>191 25個並べることにしてループ回数を一桁増やしたが早くならないぞ そういうのを机上の空論というんだ なんか初心者スレの趣旨とずれまくりなんだけど。 コンパイラのABIが「変わった」のなら、それに合わせたのを紹介しないと。 Visual Cは64bitではインラインアセンブラも使えなくなったし、AVXやSSEは組み込み関数で大概は満足できるはずって判断されたのだろう。 今のご時世わざわざアセンブリ言語でプログラミングしたいって用途は動画のコーデックやフィルタプラグインDLL、将棋とかの思考ルーチンとかでしょ。 こういうのは2GB制限下でしか使えないコードでは困るはず。 >>189 静的配列だとサイズが決まってるから、恐らく最適化されてポインタとオフセット(ディスプレースメント)に変換されるよね。 >実質差が無いメリットの為に絶対アドレスに依存したコードにする >時代に逆行だ これに同意。 >>185 自分は 64BIT 用C/C++コンパイラをインストールして無いので、試せません。 link.exe に、 /largeaddressaware:no を渡した場合にのみ、 GlobalAlloc() が失敗する理由は何ですか? ポインタが32BITになる? それとも、GlobalAlloc の関数が別のものに 入れ替わってしまう? そのどちらでもないとしたら何が原因ですか? >>187 >/largeaddressaware:noを付けると64bitアプリでも動的メモリも含めてすべてのメモリで2GB以下までしか使えなくなる >PGIのコンパイラのページでわかりやすく解説してあるよ >https://www.softek.co.jp/SPG/Pgi/TIPS/opt_64.html リンク先のページを見たのですが、自分には、 「動的メモリの場合にも2GB以下しか使えなくなる」 という文書が見つかりませんでした。 どこに書かれているか、ご指摘いただければ幸いです。 実験したけど2GBまでだったよ malloc アドレスは32bitで保持出来る 32bitアプリからの移植用だろうね >>196 メモリはOS側の管理でしょ? OSがアプリ種別を判別してアドレスを割り当てる 31bit 32bit 64bit の3種類かな? >>199 リンカオプションによって、API の挙動まで変わってしまうんでしょうか。 そして、Win64 API の GlobalAlloc() などが返す値が 31 BIT までに なるので、malloc() もそれにつられて、勝手に 31BIT までになると? 64BIT COFF だと、COFF Image に、/largeaddressaware:no かどうか を示す BIT か何かが追加された??? >>196 無料のVisual Studio Expressでも2013以降は64bitのビルドにも対応してるよ 64bitのWindowsプログラミングに興味があるなら入れてみれば? プログラムのコードを見ればわかるように 10MBずつメモリを確保していき メモリが確保できなくてGlobalAllocの戻り値にNULLが返るか 確保したメモリが4500MBを越えたらbreakでループを抜けて 確保したメモリ容量を表示して終了するようになってる で、/largeaddressaware:noを指定すると 俺の環境では1970MB確保したところでループを抜けて終了してる つまり、1970MB確保したところで、 次にGlobalAllocを実行するとメモリが確保できなくてNULLが返る これは、動的メモリも2GB以下しか確保できないということを示してる /largeaddressaware:noを指定せずにコンパイルすると 4510MBまで確保して終了する >>200 Visual Studioに入ってるdumpbin.exeでコマンドプロンプトで dumpbin /headers hogehoge.exe とやると/largeaddressawareを指定した状態(/largeaddressaware:noを指定しない状態)だと FILE HEADER VALUESの項目とのところに Application can handle large (>2GB) addresses これが表示される なので詳しくは知らないが実行ファイルの中に/lageaddressawareの指定をする部分があるはず これまたVisual Studioに入ってるeditbin.exeを使うと 実行ファイルそのものに/largeaddressawareや/lageaddressaware:noの指定ができる LAAを有効にするには editbin /lageaddressaware hogehoge.exe LAAを無効にするには editbin /lageaddressaware:no hogehoge.exe VCで64bitの静的配列にアクセスするコードを調べてみた。 #include <math.h> volatile char str[1024]; volatile char c; main(int argc, char *argv[]) { int idx; idx = (int)(rand() * 1024); c = str[idx]; } これの配列アクセス部分が ; 11 : c = str[idx]; 0001c 48 63 44 24 20 movsxd rax, DWORD PTR idx$[rsp] 00021 48 8d 0d 00 00 00 00 lea rcx, OFFSET FLAT:str 00028 0f b6 04 01 movzx eax, BYTE PTR [rcx+rax] 0002c 88 05 00 00 00 00 mov BYTE PTR c, al 00028は[rcx+rax]で配列アクセスしてる そして、00021と0002cはrip相対アドレッシングだよね?(エンコードは自信ないので) >>198 時々ポインタをDWORDに変換して構造体に突っ込んでたりすることがあるからね。 argeaddressawareは、こういうコードが含まれたアプリをどうしても64bitで動かさなきゃいけなくなった時とかに仕方なく使うオプションでしょう。 でないと、GlobalAlloc()が大きなアドレス返したら動かなくなるし。 >>200 プロセスを起動する時にexeのLAAフラグを見てメモリとかハンドルを返すAPIが32bitに収まる値を返すように設定されるんだと思う。 こういうのはプロセスごとに32bitとかにも切り替えられる(この場合はシステムDLLは64bitとは別だけど)ので、 互換性を確保する仕組みが裏で動いてるはずだよ。 >>203 のコードバイトの折り返し部分のタブが詰められて見にくいので 0001c 48 63 44 24 20 movsxd rax, DWORD PTR idx$[rsp] 00021 48 8d 0d 00 00 00 00 lea rcx, OFFSET FLAT:str 00028 0f b6 04 01 movzx eax, BYTE PTR [rcx+rax] 0002c 88 05 00 00 00 00 mov BYTE PTR c, al >>203 VC++だと/FAオプションを指定するとアセンブラ出力もされるよ ただ、64bitのVC++コンパイラが吐き出すアセンブラコードは そのままではml64.exeでアセンブルできないけど ml64.exeでアセンブルできるように修正できたらml64.exeでアセンブルする時に /Flオプションを指定するとリスティングファイルを出力してくれる >>207 >>203 は/FAscで出力された.codから抜粋したもので、リンク前だから変数のアドレスが変な値になってる。 最適化オプションつけ忘れてるのでimul使ってるけど、/O2でも配列へのアクセスはインデックスレジスタ付きなのは変わらなかった。 このサンプルでは配列の添え字がコンパイル時に推定できないようにしたのでインデックスレジスタ付きになってるけど、 普通のループでは、この部分は最適化されてポインタに変更されて、レジスタ間接とポインタ操作するコードになるはず。 ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.4 2024/05/19 Walang Kapalit ★ | Donguri System Team 5ちゃんねる