2010-10-22 11 views
15

Quindi, ho trovato una domanda simile here, ma le risposte riguardano più lo stile e se non si è in grado di farlo.Chiamare una funzione non vuoto senza utilizzare il suo valore di ritorno. Cosa succede realmente?

La mia domanda è, cosa succede in realtà quando si chiama una funzione non vuota che restituisce un oggetto, ma non si assegna o non si utilizza mai detto oggetto restituito? Quindi, meno se puoi o meno, perché so perfettamente che puoi e capisco l'altra domanda collegata sopra ... cosa fa l'ambiente compilatore/runtime?

Questa non è una domanda specifica per la lingua, ma se rispondi, specifica la lingua a cui ti riferisci, poiché i comportamenti saranno diversi.

risposta

15

Credo che sia per C# che per Java, il risultato finisce sullo stack e il compilatore forza quindi un'istruzione pop per ignorarlo. Il post sul blog di Eric Lippert su "The void is invariant" contiene ulteriori informazioni al riguardo.

Ad esempio, si consideri il seguente codice C#:

using System; 

public class Test 
{ 
    static int Foo() { return 10; } 

    public static void Main() 
    { 
     Foo(); 
     Foo(); 
    } 
} 

L'IL generato (dal MS C# 4 del compilatore) per il metodo Main è:

.method public hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    .maxstack 8 
    L_0000: call int32 Test::Foo() 
    L_0005: pop 
    L_0006: call int32 Test::Foo() 
    L_000b: pop 
    L_000c: ret 
} 

Nota le chiamate al pop - che scompaiono se si rende Foo un metodo vuoto.

+0

Forse C# non è così intelligente, ma in questo esempio, il compilatore non dovrebbe integrare il metodo? Inoltre, quanto costa fare lo stack per una tale chiamata? Potresti notare una differenza se questa fosse ripetuta 250k volte, ma per la tua chiamata al metodo medio non perderei il sonno per un pop o due extra, a meno che tu non abbia mai usato il valore di ritorno ovunque nel codice. – KeithS

+0

Ottima risposta, grazie! – DomenicDatti

+0

@KeithS Mi stavo chiedendo quanto sia costoso, ma nel mio caso particolare non è una preoccupazione. Ho un metodo che esegue il caching dietro le quinte in più mappe ma restituisce anche l'elenco richiesto e un altro metodo che si basa sulla memorizzazione nella cache e restituisce un elenco diverso da una delle mappe memorizzate nella cache che genera il primo metodo. Supponevo di poter refactoring della generazione di mappe per renderlo più pulito ed evitare del tutto questo pop extra, se necessario. – DomenicDatti

3

Dipende un po 'dalla convenzione di chiamata utilizzata. Per i tipi piccoli/semplici, il rendimento si verifica in genere in un registro. In questo caso, la funzione scriverà il valore nel registro, ma nient'altro presterà attenzione, e la prossima volta che quel registro è necessario (che di solito accade abbastanza rapidamente) verrà sovrascritto con qualcos'altro.

Per i tipi più grandi, il compilatore alloca normalmente una struttura per contenere il valore restituito. Esattamente dove/come fa quell'allocazione varia col compilatore - in alcuni casi sarà una struttura statica, e il contenuto sarà ignorato e sovrascritto la prossima volta che chiamerai una funzione che restituisce lo stesso tipo. In altri casi sarà in pila e, anche se non lo hai usato, deve ancora essere allocato prima che venga chiamata la funzione e liberato in seguito

+1

A quale lingua ti riferisci? – DomenicDatti

+0

@DomenicDatti: stavo cercando di mantenere la risposta abbastanza generale da applicare ragionevolmente bene a tutti e tre. Su una macchina virtuale, lo stack sarà virtualizzato e non può utilizzare i registri finché non viene eseguito il compilatore JIT, ma sia Java che .NET utilizzano normalmente un JIT. Tuttavia, nessuno dei due ha alcun effetto importante sul risultato. –

1

In .NET, se l'oggetto che viene restituito è un tipo di riferimento, e l'applicazione non ha altri riferimenti a quell'oggetto, quindi avrai ancora un oggetto che gira intorno alla memoria fino a quando il garbage collector deciderà di raccoglierlo.

Ciò è potenzialmente negativo se l'oggetto che si sta restituendo si trova nelle risorse. Se implementa l'interfaccia IDisposable, allora il tuo codice dovrebbe chiamare il metodo Dispose su di esso, ma in questo caso il metodo Dispose non verrebbe mai chiamato.

MODIFICA: corretto un errore di battitura.

1

Per C++, in genere, il compilatore ottimizzerà il ritorno della variabile, trasformandola in una funzione di vuoto, e se ciò consente ulteriori ottimizzazioni, il compilatore può ottimizzare l'intera chiamata di funzione o parti di esso che riguardano solo al valore di ritorno. Per Java e C#, ho poche idee.

7

cosa fa il compilatore?

Il compilatore genera un'istruzione pop che scarta il risultato dallo stack virtuale.

cosa fa l'ambiente di runtime?

In genere esegue il codice in codice che restituisce il valore restituito in un registro anziché in una posizione di stack. (Tipicamente EAX su architetture x86.)

Il jitter sa che il valore non verrà utilizzato, quindi probabilmente genera codice che cancella il registro. O forse lo lascia semplicemente in sospeso nel registro per un po '.

Quale ambiente di runtime ti interessa? Ce ne sono molti e hanno tutti un tremito diverso.

Problemi correlati