これら 2 つのコマンドの結果は同じで、X を 1 ずつインクリメントするという印象を受けますが、後者の方がおそらく効率的です。
これが正しくない場合は、違いを説明してください。
それが正しければ、なぜ後者の方が効率的である必要があるのでしょうか? 両方とも同じ IL にコンパイルすべきではありませんか?
ありがとう。
これら 2 つのコマンドの結果は同じで、X を 1 ずつインクリメントするという印象を受けますが、後者の方がおそらく効率的です。
これが正しくない場合は、違いを説明してください。
それが正しければ、なぜ後者の方が効率的である必要があるのでしょうか? 両方とも同じ IL にコンパイルすべきではありませんか?
ありがとう。
+=の MSDN ライブラリから:
この演算子の使用は、結果が 1 回しか評価されないことを除いて、結果 = 結果 + 式を指定するのとほとんど同じです。
したがって、それらは同一ではないため、 x += 1 の方が効率的です。
更新: MSDN ライブラリのリンクがVB ページではなく JScript ページであることに気付きました。これには同じ引用が含まれていません。
したがって、さらなる調査とテストの結果、その答えは VB.NET には適用されません。私は間違っていた。サンプル コンソール アプリを次に示します。
Module Module1
Sub Main()
Dim x = 0
Console.WriteLine(PlusEqual1(x))
Console.WriteLine(Add1(x))
Console.WriteLine(PlusEqual2(x))
Console.WriteLine(Add2(x))
Console.ReadLine()
End Sub
Public Function PlusEqual1(ByVal x As Integer) As Integer
x += 1
Return x
End Function
Public Function Add1(ByVal x As Integer) As Integer
x = x + 1
Return x
End Function
Public Function PlusEqual2(ByVal x As Integer) As Integer
x += 2
Return x
End Function
Public Function Add2(ByVal x As Integer) As Integer
x = x + 2
Return x
End Function
End Module
PlusEqual1 と Add1 の両方の IL は実際には同一です。
.method public static int32 Add1(int32 x) cil managed
{
.maxstack 2
.locals init (
[0] int32 Add1)
L_0000: nop
L_0001: ldarg.0
L_0002: ldc.i4.1
L_0003: add.ovf
L_0004: starg.s x
L_0006: ldarg.0
L_0007: stloc.0
L_0008: br.s L_000a
L_000a: ldloc.0
L_000b: ret
}
PlusEqual2 と Add2 の IL もほぼ同じです。
.method public static int32 Add2(int32 x) cil managed
{
.maxstack 2
.locals init (
[0] int32 Add2)
L_0000: nop
L_0001: ldarg.0
L_0002: ldc.i4.2
L_0003: add.ovf
L_0004: starg.s x
L_0006: ldarg.0
L_0007: stloc.0
L_0008: br.s L_000a
L_000a: ldloc.0
L_000b: ret
}
簡単なコンソール アプリを作成しました。
static void Main(string[] args)
{
int i = 0;
i += 1;
i = i + 1;
Console.WriteLine(i);
}
Reflector を使用して分解したところ、次のようになりました。
private static void Main(string[] args)
{
int i = 0;
i++;
i++;
Console.WriteLine(i);
}
それらは同じです。
それらは同じにコンパイルされます.2番目は入力が簡単です.
+=
評価を指定する答えは、一般的な言語で何をするかという点で確かに正しいです。しかし、VB.NETではX
、OPで指定されているのは変数またはプロパティであると想定しています。
それらはおそらく同じ IL にコンパイルされます。
VB.NET は、プログラミング言語の仕様です。仕様で定義されているものに準拠するコンパイラはすべて、VB.NET 実装にすることができます。MS VB.NET コンパイラのソース コードを編集して、大文字と小文字を区別してくだらないコードを生成するX += 1
場合でも、VB.NET 仕様に準拠することになります (これは、VB.NET 仕様がどのように機能するかについて何も述べていないためです。まったく同じになるため、同じコードを生成することが論理的になります)。
コンパイラーは両方に対して同じコードを生成する可能性が非常に高いですが (実際にそうなっていると思います)、かなり複雑なソフトウェアです。まったく、同じコードが 2 回コンパイルされたときに、コンパイラがまったく同じコードを生成することを保証することさえできません!
100% 安心して言えることは (コンパイラのソース コードをよく知っている場合を除きます)、優れたコンパイラはパフォーマンスに関して同じコードを生成する必要があるということです。
非常に多くの憶測!Reflector に関する結論でさえ、逆アセンブル中に最適化を行うことができるため、必ずしも真実ではありません。
では、なぜ誰も IL コードを調べていないのでしょうか? 次の C# プログラムを見てください。
static void Main(string[] args)
{
int x = 2;
int y = 3;
x += 1;
y = y + 1;
Console.WriteLine(x);
Console.WriteLine(y);
}
このコード スニペットは、次のようにコンパイルされます。
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 25 (0x19)
.maxstack 2
.locals init ([0] int32 x,
[1] int32 y)
// some commands omitted here
IL_0004: ldloc.0
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stloc.0
IL_0008: ldloc.1
IL_0009: ldc.i4.1
IL_000a: add
IL_000b: stloc.1
// some commands omitted here
}
ご覧のとおり、実際にはまったく同じです。そして、それはなぜですか?IL の目的は、方法ではなく、何をすべきかを伝えることだからです。最適化は JIT コンパイラーの仕事になります。ところで、VB.Netでも同じです
x86 では、x がレジスタ eax にある場合、どちらも次のような結果になります。
inc eax;
そうです、コンパイル段階の後、ILは同じになります。
「オプティマイザを信頼してください」と答えることができる、このような種類の質問があります。
有名な神話は、
x++ です。
++x
より効率的ではありません。
一時的な値を保存する必要があるためです。一時的な値を使用しない場合、オプティマイザーはそのストアを削除します。
VB では同じかもしれません。それらは、C (演算子の由来) では必ずしも同じではありません。
x が int や float のような単純な型である場合、オプティマイザーはおそらく同じ結果を生成します。
他の言語を使用する場合 (ここでは VB の知識が限られています。+= をオーバーロードできますか?) x が 1 つの大きなクラクション オブジェクトである可能性がある場合、前者は数百メガバイトになる可能性のある追加のコピーを作成します。後者はそうではありません。
同じだ。
x=x+1
は数学的に矛盾していると見なされますが、
x+=1
ではなく、入力するのが軽いです。
+=、-=、*= などは暗黙のキャストを行うことに注意してください。
int i = 0;
i = i + 5.5; // doesn't compile.
i += 5.5; // compiles.
C++ では、xのデータ型と演算子の定義方法によって異なります。xが何らかのクラスのインスタンスである場合、まったく異なる結果が得られます。
または、質問を修正して、xが整数か何かであることを指定する必要があります。
違いはメモリ参照に使用される追加のクロックサイクルによるものだと思っていましたが、間違っていることが判明しました! これは自分では理解できない
instruction type example cycles
================================================== =================
ADD reg,reg add ax,bx 1
ADD mem,reg add total, cx 3
ADD reg,mem add cx,incr 2
ADD reg,immed add bx,6 1
ADD mem,immed add pointers[bx][si],6 3
ADD accum,immed add ax,10 1
INC reg inc bx 1
INC mem inc vpage 3
MOV reg,reg mov bp,sp 1
MOV mem,reg mov array[di],bx 1
MOV reg,mem mov bx,pointer 1
MOV mem,immed mov [bx],15 1
MOV reg,immed mov cx,256 1
MOV mem,accum mov total,ax 1
MOV accum,mem mov al,string 1
MOV segreg,reg16 mov ds,ax 2, 3
MOV segreg,mem16 mov es,psp 2, 3
MOV reg16,segreg mov ax,ds 1
MOV mem16,segreg mov stack_save,ss 1
MOV reg32,controlreg mov eax,cr0 22
mov eax,cr2 12
mov eax,cr3 21, 46
mov eax,cr4 14
MOV controlreg,reg32 mov cr0,eax 4
MOV reg32,debugreg mov edx,dr0 DR0-DR3,DR6,DR7=11;
DR4,DR5=12
MOV debugreg,reg32 mov dr0,ecx DR0-DR3,DR6,DR7=11;
DR4,DR5=12
ソース: http://turkish_rational.tripod.com/trdos/pentium.txt
指示は次のように翻訳できます。
;for i = i+1 ; cycles
mov ax, [i] ; 1
add ax, 1 ; 1
mov [i], ax ; 1
;for i += 1
; dunno the syntax of instruction. it should be the pointers one :S
;for i++
inc i ; 3
;or
mov ax, [i] ; 1
inc ax ; 1
mov [i], ax ; 1
;for ++i
mov ax, [i] ; 1
;do stuff ; matters not
inc ax ; 1
mov [i], ax ; 1
すべてが同じであることが判明しました :S 役立つデータがいくつかあります。コメントしてください!
x が単純な整数スカラー変数の場合、それらは同じでなければなりません。
x が大きな式の場合、副作用がある可能性があり、+=1
2++
倍の速度になるはずです。
多くの人々は、最適化がすべてであるかのように、この種の低レベルの最適化に集中しています。それがはるかに大きな主題であることを知っていると思います。
実行時には (少なくとも PERL では) 違いはありません。ただし、 x+=1 は x = x+1 よりも約 0.5 秒速く入力できます。
プログラムの効率に違いはありません。タイピング効率だけです。
1980 年代初頭、Lattice C Compiler の非常に優れた最適化の 1 つは、「x = x + 1;」、「x += 1;」というものでした。および「x++;」すべてがまったく同じマシンコードを生成しました。彼らがそれを行うことができれば、このミレニアムで書かれたコンパイラは間違いなくそれを行うことができるはずです.