Domanda

Ho pensato a dichiarazioni try / catch nidificato e ho iniziato a pensare a quali condizioni, se del caso, il JIT può eseguire l'ottimizzazione o la semplificazione del IL compilato.

Per illustrare, considerare le seguenti rappresentazioni funzionalmente equivalente di un gestore di eccezioni.

// Nested try/catch
try
{
  try
  {
    try
    {
      foo();
    }
    catch(ExceptionTypeA) { }
  }
  catch(ExceptionTypeB) { }
}
catch(ExceptionTypeC) { }

// Linear try/catch
try
{
  foo();
}
catch(ExceptionTypeA) { }
catch(ExceptionTypeB) { }
catch(ExceptionTypeC) { }

Supponendo che non ci sono riferimenti variabili aggiuntive o chiamate di funzione all'interno dei stack frame del try nidificato, può JIT concludere che gli stack frame possono essere ridotti a all'esempio lineare?

Ora come circa il seguente esempio?

void Try<TException>(Action action)
{
  try
  {
    action();
  }
  catch (TException) { }
}

void Main()
{
  Try<ExceptionC>(Try<ExceptionB>(Try<ExceptionA>(foo)));
}

Non credo che ci sia alcun modo per il JIT inline invocazioni delegato, in modo da questo esempio non può essere ridotto a quello precedente. Tuttavia, in caso di foo() lancio ExceptionC, non questa soluzione esegue più povera rispetto all'esempio lineare? Ho il sospetto che v'è un costo aggiuntivo per abbattere gli stack frame dalle invocazioni delegato, anche se i dati aggiuntivi contenuti nei fotogrammi è minima.

È stato utile?

Soluzione

Vale la pena notare che nel primo caso sono solo funzionalmente equivalente quando si sta facendo nulla all'interno del blocco catch. In caso contrario, considerare questo:

try
{
    foo();
}
catch (IOException)
{
    throw new ArgumentException(); // Bubbles up to caller
}
catch (ArgumentException)
{
    Console.WriteLine("Caught");
}

vs

try
{
    try
    {
        foo();
    }
    catch (IOException)
    {
        throw new ArgumentException(); // Caught by other handler
    }
}
catch (ArgumentException)
{
    Console.WriteLine("Caught");
}

Ora, in questo caso la differenza è evidente, ma se il blocco catch chiama un metodo arbitrario, come è il JIT scopo di sapere che cosa potrebbe essere gettato? Meglio essere prudenti.

Questo ci lascia con la possibilità di JIT eseguire ottimizzazioni per blocchi catch vuoti - una pratica che è fortemente sconsigliato, in primo luogo. Non voglio JIT di trascorrere del tempo cercando di rilevare codice cattivo e farlo correre molto leggermente più veloce -. Se davvero non c'è alcuna differenza di prestazioni in primo luogo

Altri suggerimenti

mia comprensione di try / catch / finally limitata rispetto alle prestazioni è che tali regioni sono trasparenti per l'esecuzione regolare di codice. Cioè, se il codice non genera alcuna eccezione per la cattura, quindi i try / catch / finally regioni hanno ZERO impatto sulle prestazioni l'esecuzione di codice.

Tuttavia, quando viene sollevata un'eccezione, il runtime inizia a camminare lo stack dal sito in cui è cresciuto, controllando le tabelle di metadati per verificare se il sito in questione è contenuta in uno degli altri blocchi try critici. Se ne viene trovata (e ha un blocco catch ammissibile o di una blocco finally) allora il gestore in questione viene identificato e rami di esecuzione a questo punto.

Il processo di raccolta e gestione delle eccezioni è costoso dal punto di vista delle prestazioni. I programmatori non dovrebbero utilizzare eccezioni come un modo di segnalare o controllare il flusso del programma in qualcosa di diverso da circostanze eccezionali (gioco di parole).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top