Il limite nel seguente ciclo (12332 * 324234) viene calcolato una volta o ogni volta che viene eseguito il ciclo?I limiti dei cicli for vengono calcolati una volta o con ciascun ciclo?
for(int i=0; i<12332*324234;i++)
{
//Do something!
}
Il limite nel seguente ciclo (12332 * 324234) viene calcolato una volta o ogni volta che viene eseguito il ciclo?I limiti dei cicli for vengono calcolati una volta o con ciascun ciclo?
for(int i=0; i<12332*324234;i++)
{
//Do something!
}
Per questo è calcolato una volta, o più probabilmente 0 volte.
Il compilatore ottimizzerà la moltiplicazione per te.
Tuttavia questo non è sempre il caso se si dispone di qualcosa come.
for(int i=0; i<someFunction();i++)
{
//Do something!
}
Poiché il compilatore non è sempre in grado di vedere ciò che torneranno someFunction
. Quindi, anche se someFunction
restituisce un valore costante ogni volta, se il compilatore non lo sa, non può ottimizzarlo.
EDIT: Come ha detto MainMa in un commento, si è in questa situazione è possibile eliminare il costo facendo qualcosa di simile:
int limit = someFunction();
for(int i=0; i<limit ;i++)
{
//Do something!
}
SE si è certi che il valore di someFunction()
sarà non cambiare durante il ciclo.
Vero. Ad esempio, fare una query a un database in 'someFunction()' sarà davvero una cattiva idea. Puoi facilmente evitarlo creando un numero intero prima del ciclo, assegnandogli un valore e quindi usando questo numero intero in un ciclo. –
Ci sono due modi per interpretare la tua domanda:
Il rispondere a queste due domande diverse:
In altre parole:
for (int i = 0; i < someString.Length; i++)
Se la valutazione di someString.Length
è costoso, ci sará una penale su ogni iterazione del ciclo.
Dubito che String.Length funzioni come strlen. Tuttavia, "someString" potrebbe cambiare ad ogni ciclo di iterazione. – Luca
In realtà questo non verrà compilato perché verrà traboccato ma se lo fai un numero più piccolo e apri Reflector troverai qualcosa di simile.
for (int i = 0; i < 0x3cf7b0; i++)
{
}
Buon punto sull'overflow – KLee1
Come segnalato da @Chaos, non verrà compilato. Ma se usi un'espressione rappresentabile (come 100 * 100), il risultato sarà probabilmente codificato. Sul Mono, il CIL comprende:
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: add
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: ldc.i4 10000
IL_0011: blt IL_0007
Come si può vedere, 100 * 100 è codificato come 10000. Tuttavia, in generale sarà valutata di volta in volta, e se un metodo o una proprietà si chiama, questo probabilmente non può essere ottimizzato.
Sembra essere calcolato ogni volta. Smontaggio da VS2008.
0000003b nop
for (Int64 i = 0; i < (Int64)12332 * (Int64)324234; i++)
0000003c mov qword ptr [rsp+20h],0
00000045 jmp 000000000000005E
{
00000047 nop
bool h = false;
00000048 mov byte ptr [rsp+28h],0
}
0000004d nop
for (Int64 i = 0; i < (Int64)12332 * (Int64)324234; i++)
0000004e mov rax,qword ptr [rsp+20h]
00000053 add rax,1
00000059 mov qword ptr [rsp+20h],rax
0000005e xor ecx,ecx
00000060 mov eax,0EE538FB8h
00000065 cmp qword ptr [rsp+20h],rax
0000006a setl cl
0000006d mov dword ptr [rsp+2Ch],ecx
00000071 movzx eax,byte ptr [rsp+2Ch]
00000076 mov byte ptr [rsp+29h],al
0000007a movzx eax,byte ptr [rsp+29h]
0000007f test eax,eax
00000081 jne 0000000000000047
Non c'è alcun comando di moltiplicazione nello smontaggio. –
Questo è uno dei comportamenti più comunemente frainteso di loop in C#.
Ecco cosa è necessario sapere:
Loop Bounds calcoli, se non costante e coinvolge una variabile, proprietà di accesso, chiamata di funzione, o delegare invocazione si ri-calcolo il valore dei limiti prima di ogni iterazione del ciclo .
Così, ad esempio:
for(int i = 0; i < 1234*1234; i++) { ... }
In questo caso, l'espressione 1234*1234
è una fase di compilazione costante, e di conseguenza sarà non essere ricalcolata ad ogni iterazione. Infatti, viene calcolato al momento della compilazione e sostituito con una costante.
Tuttavia, in questo caso:
int k = 10;
for(int i = 0; i < k; i++) { k -= 1; ... }
Il valore di k
deve essere esaminato ogni iterazione. Dopo tutto può cambiare .. in questo esempio. Fortunatamente, dal momento che k
è semplicemente una variabile locale, il costo di accesso è molto basso - e in molti casi verrà conservato nella cache della CPU locale o forse verrà mantenuto in un registro (a seconda di come il JIT elabora ed emette il codice macchina).
Nel caso di qualcosa di simile al seguente:
IEnumerable<int> sequence = ...;
for(int i = 0; i < sequence.Count(); i++) { ... }
Il costo di calcolare il sequence.Count()
può essere molto costoso. E poiché viene valutato su ogni iterazione del ciclo, può sommarsi rapidamente.
Il compilatore non può ottimizzare via le chiamate ai metodi o proprietà che si verificano all'interno dell'espressione limiti ciclo perché possono anche cambiare con ogni iterazione. Immaginate se il loop di cui sopra è stato scritto come:
IEnumerable<int> sequence = ...;
for(int i = 0; i < sequence.Count(); i++) {
sequence = sequence.Concat(anotherItem);
}
Chiaramente sequence
sta cambiando ad ogni iterazione ... e quindi la Count()
è probabile che sia diverso su ogni iterazione. Il compilatore non tenta di eseguire alcune analisi statiche per determinare se l'espressione del limite di loop potrebbe essere costante ... che sarebbe estremamente complicata, se non impossibile. Invece, presuppone che se l'espressione non è una costante deve essere valutata su ogni iterazione.
Ora, nella maggior parte dei casi, il costo del calcolo del vincolo dei limiti per un ciclo sarà relativamente poco costoso, quindi non devi preoccuparti di questo. Ma devi capire come il compilatore tratta i limiti del ciclo come questo. Inoltre, come sviluppatore devi stare attento all'utilizzo di proprietà o metodi che hanno effetti collaterali come parte di un'espressione limite - dopo tutto, questi effetti collaterali si verificheranno su ogni iterazione del ciclo.
Sì, il valore di confronto viene calcolato ogni ciclo di ciclo.
Se è assolutamente necessario utilizzare per-loop, che raramente è davvero necessario più, per quanto ne so c'è solo un "buon" modello di ciclo:
for (int i = first(), last = last(); i != last; ++i)
{
// body
}
Avviso l'incremento del prefisso troppo.
Questo è fuorviato per molte ragioni. Non è una questione di essere "necessario". Perché i loop non sono mai necessari, ma a volte c'è il modo più pulito per esprimere qualcosa. Non tutte le condizioni usano un metodo. In effetti, quello nella domanda no. Di quelli che lo fanno, a volte il metodo * deve * essere chiamato ogni volta, e in altri casi, la differenza di prestazioni è trascurabile. Infine, pre e post-incremento non fanno assolutamente nessuna differenza pratica qui. –
Sono abbastanza sicuro che lo sviluppatore che utilizza questo modello sappia quando il metodo deve essere chiamato ogni volta e lo aggiusterà. Comunque, è un buon stile usare "for pattern" che è stato introdotto in C++ da Scott Meyers ed è comunque molto utile. – Grozz
'++ i' e' i ++ 'non fanno alcuna differenza perché il compilatore li ottimizzerà per essere semanticamente identico. – KLee1
Prima di tutto il ciclo for nella domanda non verrà compilato. Ma diciamo che era
for (int i = 0; i < 20; i++)
{
Console.WriteLine(i);
i++;
}
VS
for (int i = 0; i < 10*2; i++)
{
Console.WriteLine(i);
i++;
}
il codice IL è esattamente lo stesso.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: br.s L_0016
L_0005: nop
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(int32)
L_000c: nop
L_000d: ldloc.0
L_000e: ldc.i4.1
L_000f: add
L_0010: stloc.0
L_0011: nop
L_0012: ldloc.0
L_0013: ldc.i4.1
L_0014: add
L_0015: stloc.0
L_0016: ldloc.0
L_0017: ldc.i4.s 20
L_0019: clt
L_001b: stloc.1
L_001c: ldloc.1
L_001d: brtrue.s L_0005
L_001f: call int32 [mscorlib]System.Console::Read()
L_0024: pop
L_0025: ret
}
Ora, anche se sostituisco con una funzione nel ciclo come
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < Foo(); i++)
{
Console.WriteLine(i);
i++;
}
Console.Read();
}
private static int Foo()
{
return 20;
}
ottengo questo codice IL
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: br.s L_0016
L_0005: nop
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(int32)
L_000c: nop
L_000d: ldloc.0
L_000e: ldc.i4.1
L_000f: add
L_0010: stloc.0
L_0011: nop
L_0012: ldloc.0
L_0013: ldc.i4.1
L_0014: add
L_0015: stloc.0
L_0016: ldloc.0
L_0017: call int32 TestBedForums.Program::Foo()
L_001c: clt
L_001e: stloc.1
L_001f: ldloc.1
L_0020: brtrue.s L_0005
L_0022: call int32 [mscorlib]System.Console::Read()
L_0027: pop
L_0028: ret
}
che sembra lo stesso per me.
Quindi per me sembra che non ci siano differenze in FOR LOOP con limite finito, un altro con limite di calcolo e ultimo con limite proveniente da una funzione.
Quindi finché il codice viene copiato, sai cosa stai facendo in un loop mastodontico come questo e hai abbastanza memoria in corso penso che funzionerà e produrrà lo stesso perf. (in C#)
possibile duplicato di [C# - Interno interno del ciclo] (http://stackoverflow.com/questions/3369773/c-for-loop-internals) –
@Darin, non proprio. Questo ha usato un metodo/proprietà, non una costante. –
Cattivo esempio; il compilatore ottimizzerà i calcoli statici come quello ovunque li trovi. –