何もしないコードの中身は本当に何も無くなる。

CompJapan的ソフトを作ったらどうなるか試してみる。

驚速メモリを使って、20年ほど前に「メモリ最適化を行うとうたった実際には何もしないソフトをシェアウェアとして販売した」事案があったのを思い出した。今でもGoogle Playストアに行けばこの手のソフトには山ほど出逢えるからこんな子供にいちいち付き合ってるほど今のインターネットには余裕が無い。

Google Playトップ人気の有料アプリ、実態は中身なしの詐欺アプリhttps://www.itmedia.co.jp/news/articles/1404/08/news037.html

探せば誰かがアーカイブしたデジタルタトゥーが出てくるが、手っ取り早くWikipediaで調べなおした。それによると「一つの変数に定数を足して引くことを36万回繰り返す」処理を行っているらしい。VBのソースも載っている。

For Bbbb = 1 To 3000
Print #1, Jjjj 'CPU処理のためのPrint文
For Kkkk = 1 To 120
Kkkk = Kkkk + 65468543
Kkkk = Kkkk - 65468543
Next Kkkk
Next Bbbb

確かに時間かせぎ以外は何もしていない。メモリの消費も最小限だし。VB.netで書き直してみた。 前後が載ってないからファイル番号の#1が何なのか良くわからない。(←後で判明した)

'newmem.bas
Module Module1
    Sub Main()
        For Bbbb = 1 To 3000
            'Print #1, Jjjj 'CPU処理のためのPrint文
            For Kkkk = 1 To 120
                Kkkk = Kkkk + 65468543
                Kkkk = Kkkk - 65468543
            Next Kkkk
        Next Bbbb
    End Sub
End Module

vbcでコンパイルする。vbc newmem.bas
vbcのバージョンはMicrosoft (R) Visual Basic Compiler バージョン 3.100.119.28106 (58a4b1e7)。実行しても一瞬で終了して何も起こらないので、どういう処理がなされたか逆アセンブルしてみる。ildasm newmem.exe で、Module1の中身を見てみる。

.method public static void  Main() cil managed
 {
   .entrypoint
   .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
   // コード サイズ       43 (0x2b)
   .maxstack  2
   .locals init (int32 V_0,
            int32 V_1)
   IL_0000:  nop
   IL_0001:  ldc.i4.1
   IL_0002:  stloc.0
   IL_0003:  ldc.i4.1
   IL_0004:  stloc.1
   IL_0005:  ldloc.1
   IL_0006:  ldc.i4     0x3e6f87f
   IL_000b:  add.ovf
   IL_000c:  stloc.1
   IL_000d:  ldloc.1
   IL_000e:  ldc.i4     0x3e6f87f
   IL_0013:  sub.ovf
   IL_0014:  stloc.1
   IL_0015:  ldloc.1
   IL_0016:  ldc.i4.1
   IL_0017:  add.ovf
   IL_0018:  stloc.1
   IL_0019:  ldloc.1
   IL_001a:  ldc.i4.s   120
   IL_001c:  ble.s      IL_0005
   IL_001e:  ldloc.0
   IL_001f:  ldc.i4.1
   IL_0020:  add.ovf
   IL_0021:  stloc.0
   IL_0022:  ldloc.0
   IL_0023:  ldc.i4     0xbb8
   IL_0028:  ble.s      IL_0003
   IL_002a:  ret
 } // end of method Module1::Main

0x3e6f87fは10進で65468543で、その直後にadd.ovf、sub.ovfのオペコードがある。他にも120とか、0xbb8(10進で3000)とか出てるし、忠実にilアセンブラに置き換わっているようだ。当のシェアウェア作家もVBを使っていたということだから、Standardでなければ最適化機能もあった気がするし、こういうコードは無視されるんじゃないかと思った。

今どきのVB.netコンパイラ vbcで最適化を有効にしてコンパイルしてみる。vbc -optimize+ -debug- newmem.bas
ilアセンブラは次のような感じ。

.method public static void  Main() cil managed
 {
   .entrypoint
   .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
   // コード サイズ       42 (0x2a)
   .maxstack  2
   .locals init (int32 V_0,
            int32 V_1)
   IL_0000:  ldc.i4.1
   IL_0001:  stloc.0
   IL_0002:  ldc.i4.1
   IL_0003:  stloc.1
   IL_0004:  ldloc.1
   IL_0005:  ldc.i4     0x3e6f87f
   IL_000a:  add.ovf
   IL_000b:  stloc.1
   IL_000c:  ldloc.1
   IL_000d:  ldc.i4     0x3e6f87f
   IL_0012:  sub.ovf
   IL_0013:  stloc.1
   IL_0014:  ldloc.1
   IL_0015:  ldc.i4.1
   IL_0016:  add.ovf
   IL_0017:  stloc.1
   IL_0018:  ldloc.1
   IL_0019:  ldc.i4.s   120
   IL_001b:  ble.s      IL_0004
   IL_001d:  ldloc.0
   IL_001e:  ldc.i4.1
   IL_001f:  add.ovf
   IL_0020:  stloc.0
   IL_0021:  ldloc.0
   IL_0022:  ldc.i4     0xbb8
   IL_0027:  ble.s      IL_0002
   IL_0029:  ret
 } // end of method Module1::Main

だっさ。ほぼ変わってない。マネージドコードの実行時最適化もあるかもしれないがもうちょっと何とかならないのか。予想では、ループ内の処理がごっそり抜け落ちて「当時、件のソフトはソースに書いてあることすら本当に実行されてなかったんじゃないか」とか予想したかったのに。あまりにもつまらないのでC++で書き直してみる。<と;は全角文字に置き換え。

int main(void) {
 for(long Bbbb=1; Bbbb<=3000; Bbbb++) {
     for(long Kkkk=1; Kkkk<=120; Kkkk++) { 
        Kkkk = Kkkk + 65468543;
         Kkkk = Kkkk - 65468543;
     }
 } 
return 0;
 }

cl /Fa newmem.cpp でアセンブラソースを書き出す。clのバージョンは最新の Microsoft(R) C/C++ Optimizing Compiler Version 19.21.27702.2 for x64。

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.21.27702.2 
 include listing.inc
 INCLUDELIB LIBCMT
 INCLUDELIB OLDNAMES
 PUBLIC    main
 pdata    SEGMENT
 $pdata$main DD    imagerel $LN9
     DD  imagerel $LN9+90
     DD  imagerel $unwind$main
 pdata    ENDS
 xdata    SEGMENT
 $unwind$main DD    010401H
     DD  02204H
 xdata    ENDS
 ; Function compile flags: /Odtp
 _TEXT    SEGMENT
 Kkkk$1 = 0
 Bbbb$2 = 4
 main    PROC
 ; File C:\Users\xxx\newmem.cpp
 ; Line 10
 $LN9:
     sub rsp, 24
 ; Line 12
     mov DWORD PTR Bbbb$2[rsp], 1
     jmp SHORT $LN4@main
 $LN2@main:
     mov eax, DWORD PTR Bbbb$2[rsp]
     inc eax
     mov DWORD PTR Bbbb$2[rsp], eax
 $LN4@main:
     cmp DWORD PTR Bbbb$2[rsp], 3000        ; 00000bb8H
     jg  SHORT $LN3@main
 ; Line 14
     mov DWORD PTR Kkkk$1[rsp], 1
     jmp SHORT $LN7@main
 $LN5@main:
     mov eax, DWORD PTR Kkkk$1[rsp]
     inc eax
     mov DWORD PTR Kkkk$1[rsp], eax
 $LN7@main:
     cmp DWORD PTR Kkkk$1[rsp], 120     ; 00000078H
     jg  SHORT $LN6@main
 ; Line 15
     mov eax, DWORD PTR Kkkk$1[rsp]
     add eax, 65468543               ; 03e6f87fH
     mov DWORD PTR Kkkk$1[rsp], eax
 ; Line 16
     mov eax, DWORD PTR Kkkk$1[rsp]
     sub eax, 65468543               ; 03e6f87fH
     mov DWORD PTR Kkkk$1[rsp], eax
 ; Line 17
     jmp SHORT $LN5@main
 $LN6@main:
 ; Line 18
     jmp SHORT $LN2@main
 $LN3@main:
 ; Line 19
     xor eax, eax
 ; Line 20
     add rsp, 24
     ret 0
 main    ENDP
 _TEXT    ENDS
 END

忠実にコンパイルというか、トランスレートされている。
次に最適化有効でコンパイル。cl /O2 /Fa newmem.cpp

; Listing generated by Microsoft (R) Optimizing Compiler Version 19.21.27702.2 
 include listing.inc
 INCLUDELIB LIBCMT
 INCLUDELIB OLDNAMES
 PUBLIC    main
 ; Function compile flags: /Ogtpy
 ;    COMDAT main
 _TEXT    SEGMENT
 main    PROC                        ; COMDAT
 ; File C:\Users\xxx\newmem.cpp
 ; Line 19
     xor eax, eax
 ; Line 20
     ret 0
 main    ENDP
 _TEXT    ENDS
 END

やった。ものの見事に何も無くなった。VB.netコンパイラは怪しいが、C++コンパイラは本当に無駄なことはきちんと除外してくれる。C/C++ Optimizing Compilerの名を冠しているだけのことはあるね。


探したらすぐにオリジナルのソースも出てきて、謎のPrint #1, Jjjjの処理内容も分かったから、メモリ最適化処理を忠実に移植(?)したバージョンができた。powershell -C Measure-Command {.\newmem.exe} で確認したら実行にかかる時間は420ミリ秒くらい。

Module Module1
     Sub Main()
         Dim FileNumber1 As Integer = FreeFile()
         Dim AppPath As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)
         FileOpen(FileNumber1, AppPath & "\log.dat", OpenMode.Output)
         Dim Jjjj = "Data Error none…"
         For Bbbb = 1 To 3000
             PrintLine(FileNumber1, Jjjj)
             For Kkkk = 1 To 120
                 Kkkk = Kkkk + 65468543
                 Kkkk = Kkkk - 65468543
             Next Kkkk
         Next Bbbb
         FileClose(FileNumber1)
         System.IO.File.Delete(AppPath & "\log.dat")
     End Sub
 End Module

外側のループ1回ごとファイルに”Data Error none…”と書いて最後に消す。これならファイル書込時キャッシュ領域作成と破棄の動作で物理メモリを若干解放できるかもしれない。まあ、他にもやってない処理をやってますみたいに見せかけたのはいただけないから擁護のしようもないけど。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中