C # fora desempenho parâmetro
-
22-08-2019 - |
Pergunta
out parâmetros tem quaisquer implicações C # desempenho que eu deveria saber? (Exceções como)
Quer dizer, é uma boa idéia ter um método com um parâmetro out
em um loop que irá executar um par de milhões de vezes por segundo?
Eu sei que é feio, mas eu estou usando-o da mesma forma que Int32.TryParse
é usá-los -. Retornando um bool
de dizer se alguma validação foi bem sucedida e ter um parâmetro out
contendo alguns dados adicionais, se ele foi bem sucedido
Solução
Eu duvido que você vai encontrar qualquer penalidade de desempenho significativo para usando um parâmetro out
. Você tem que obter informações de volta para o chamador de alguma forma ou de outra - out
é apenas uma maneira diferente de fazê-lo. Você pode encontrar lá é alguns penalidade se você usar o parâmetro extensivamente dentro do método, pois pode muito bem significar um nível extra de redirecionamento para cada acesso. No entanto, eu não esperaria que ele seja significativo. Como normal, escrever o código mais legível e teste se o desempenho já é bom o suficiente antes de tentar otimizar ainda mais.
EDIT: O resto este é um aparte, de forma eficaz. É apenas realmente relevante para grandes tipos de valor, que deve ser evitada qualquer maneira:)
Não concordo com a afirmação de Konrad sobre os "valores de retorno para todos os tipos> 32 bit são tratados semelhantes ou idênticos aos de fora argumentos sobre o nível da máquina de qualquer maneira" embora. Aqui está um pequeno aplicativo de teste:
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
struct BigStruct
{
public Guid guid1, guid2, guid3, guid4;
public decimal dec1, dec2, dec3, dec4;
}
class Test
{
const int Iterations = 100000000;
static void Main()
{
decimal total = 0m;
// JIT first
ReturnValue();
BigStruct tmp;
OutParameter(out tmp);
Stopwatch sw = Stopwatch.StartNew();
for (int i=0; i < Iterations; i++)
{
BigStruct bs = ReturnValue();
total += bs.dec1;
}
sw.Stop();
Console.WriteLine("Using return value: {0}",
sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (int i=0; i < Iterations; i++)
{
BigStruct bs;
OutParameter(out bs);
total += bs.dec1;
}
Console.WriteLine("Using out parameter: {0}",
sw.ElapsedMilliseconds);
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static BigStruct ReturnValue()
{
return new BigStruct();
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static void OutParameter(out BigStruct x)
{
x = new BigStruct();
}
}
Resultado:
Using return value: 11316
Using out parameter: 7461
Basicamente, usando um out parâmetro estamos escrevendo os dados diretamente para o destino final, em vez de escrevê-lo para quadro de pilha pequena do método e, em seguida, copiá-lo de volta ao quadro de pilha do método principal.
Sinta-se livre para criticar o aplicativo de benchmark embora - Talvez eu tenha perdido alguma coisa
Outras dicas
Não é um problema de desempenho, mas algo que veio antes - você não pode usá-los com variância em C # 4.0 .
Pessoalmente, eu tendem a usar parâmetros out
uma quantidade razoável na minha privada código (ou seja, dentro de uma classe, tendo um método que retorna vários valores sem o uso de um tipo separado) - mas eu tendem a evitar -los na API público, exceto para o padrão bool Try{Something}(out result)
.
Não há implicações de desempenho. out
é basicamente o mesmo que qualquer velha passagem de argumento, de um ponto de vista técnico. Embora possa parecer plausível que enormes amounds de dados são copiados (por exemplo, para grandes estruturas), este é realmente o mesmo que para valores de retorno.
Na verdade, valores de retorno para todos os tipos> 32 bit são tratados semelhantes aos argumentos out
na> nível da máquina de qualquer maneira.
Por favor note que a última declaração não sugere que retornar um parâmetro out
valor == em .NET. benchmark mostra de Jon que este é, obviamente, (e infelizmente) não é o caso. Na verdade, para torná-lo idêntico, chamado valor de retorno otimização é empregue em compiladores C ++. Algo semelhante poderia ser feito em versões futuras do JIT para melhorar o desempenho de retorno de grandes estruturas (no entanto, uma vez que grandes estruturas são bastante raro em .NET, isso pode ser uma otimização desnecessário).
No entanto , (e com o meu conhecimento muito limitado de x86 assembly), retornando objetos de chamadas de função geralmente implica alocação de espaço suficiente no site da chamada, empurrando o endereço na pilha e preenchê-lo, copiando o valor de retorno para ele. Este é basicamente o mesmo que out
faz apenas omitindo uma cópia temporária desnecessária do valor desde o local de memória alvo pode ser acessado diretamente.
A principal razão para evitar os parâmetros é a legibilidade do código, em vez de desempenho.
Para tipos de valores não há nenhuma diferença real de qualquer maneira (eles sempre copiar) e para tipos de referência é basicamente o mesmo que passar por ref.
vezes Nove em cada dez é melhor você criar a sua própria classe de registro mudo, ao invés de usar um parâmetro de saída -. Isso é mais fácil de ler e entender quando você retornar para o código mais tarde
Fora são passados ??por ref. Assim, apenas um ponteiro passado na pilha.
Se o seu tipo de valor é grande, há menos cópia, mas então você tem que cancelar o ponteiro sobre cada uso variável.
Usando um parâmetro de saída faz desempenho não doer. parâmetro um fora é basicamente um parâmetro de referência, de modo que o chamador eo ponto receptor para o mesmo pedaço de memória.