Pergunta

Eu sou portagem de um aplicativo existente para C # e quer melhorar o desempenho sempre que possível. Muitos contadores de loop existentes e referências de matriz são definidos como System.UInt32, em vez do Int32 eu teria usado.

Existe alguma diferença significativa de desempenho para o uso UInt32 vs Int32?

Foi útil?

Solução

Eu não acho que existem quaisquer considerações de desempenho, que não possível diferença entre aritmética assinados e não assinados no nível do processador, mas naquele momento eu acho que as diferenças são discutíveis.

A maior diferença está no cumprimento CLS como os tipos não assinados não são compatível com CLS como nem todos os idiomas apoiá-los.

Outras dicas

A resposta curta é "Não. Qualquer impacto de desempenho será insignificante".

A resposta correta é "Depende".

A melhor pergunta é: "Devo usar uint quando estou certo de que eu não preciso de um sinal?"

A razão que você não pode dar um desempenho definitivo "sim" ou "não" no que diz respeito ao desempenho é porque a plataforma de destino acabará por determinar. Ou seja, o desempenho é ditada por qualquer processador vai ser executar o código e as instruções disponíveis. Seu código .NET compila até Intermediate Language (IL ou código de bytes). Estas instruções são então compilados para a plataforma de destino pelo Just-In-Time (JIT) como parte do Common Language Runtime (CLR). Você não pode controlar ou prever o código será gerado para cada usuário.

Então, sabendo que o hardware é o árbitro final da performance, a questão torna-se, "Quão diferente é o código .NET gera para uma assinado contra inteiro sem sinal?" e "Será que o impacto diferença a minha candidatura e as minhas plataformas de destino?"

A melhor maneira de responder a estas perguntas é executar um teste.

class Program
{
  static void Main(string[] args)
  {
    const int iterations = 100;
    Console.WriteLine($"Signed:      {Iterate(TestSigned, iterations)}");
    Console.WriteLine($"Unsigned:    {Iterate(TestUnsigned, iterations)}");
    Console.Read();
  }

  private static void TestUnsigned()
  {
    uint accumulator = 0;
    var max = (uint)Int32.MaxValue;
    for (uint i = 0; i < max; i++) ++accumulator;
  }

  static void TestSigned()
  {
    int accumulator = 0;
    var max = Int32.MaxValue;
    for (int i = 0; i < max; i++) ++accumulator;
  }

  static TimeSpan Iterate(Action action, int count)
  {
    var elapsed = TimeSpan.Zero;
    for (int i = 0; i < count; i++)
      elapsed += Time(action);
    return new TimeSpan(elapsed.Ticks / count);
  }

  static TimeSpan Time(Action action)
  {
    var sw = new Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    return sw.Elapsed;
  }
}

Os dois métodos de ensaio, TestSigned e TestUnsigned , executar cada ~ 2 milhões de iterações de um simples aumento em um inteiro assinado e sem sinal, respectivamente. O código de teste é executado 100 iterações de cada teste e calcula a média dos resultados. Isso deve eliminar quaisquer inconsistências potenciais. Os resultados sobre a minha i7-5960X compilados para x64 foram:

Signed:      00:00:00.5066966

Unsigned:    00:00:00.5052279

Estes resultados são quase idênticos, mas para obter uma resposta definitiva, nós realmente precisamos de olhar para o bytecode gerado para o programa. Podemos usar ILDASM como parte do .NET SDK para inspeccionar o código no conjunto gerado pelo compilador.

 bytecode

Aqui, podemos ver que o compilador C # favores assinado inteiros e realmente executa a maioria das operações nativamente como assinados inteiros e sempre apenas trata o valor na memória como não assinado ao comparar para o ramo (a.k.a salto ou se). Apesar do fato de que estamos usando um inteiro sem sinal tanto para o iterador E o acumulador em TestUnsigned , o código é quase idêntico ao TestSigned método, exceto por uma única instrução: IL_0016 . Uma rápida olhada na ECMA especificação descreve a diferença:

blt.un.s: Filial de destino, se inferior a (não assinado ou não-ordenada), forma curta.

blt.s: Branch a meta, se menos de, forma curta.

Ser uma instrução tão comum, é seguro assumir que os processadores de alta potência mais modernas terão instruções de hardware para ambas as operações e que muito provavelmente vai executar no mesmo número de ciclos, mas isso não é garantido . Um processador de baixo consumo de energia pode ter menos instruções e não tem uma ramificação para unsigned int. Neste caso, o compilador JIT pode ter que emitir múltiplas instruções de hardware (conversão A primeira, então uma filial, por exemplo) para executar o blt.un.s instrução IL. Mesmo se este for o caso, essas instruções adicionais seria básica e provavelmente não iria afetar o desempenho significativamente.

Assim, em termos de performance, a resposta longa é "É improvável que haverá uma diferença de desempenho em tudo entre usar um assinado ou um inteiro sem sinal. Se houver uma diferença, é provável que seja insignificante."

Assim, pois, se o desempenho é idêntico, a próxima questão lógica é: "Devo usar um valor sem sinal quando estou certo de que eu não necessidade um sinal?"

Há duas coisas a considerar aqui: primeiro, inteiros sem sinal não são compatíveis com CLS , o que significa que você pode executar em problemas se você está expondo um inteiro sem sinal como parte de uma API que outro programa irá consumir (como se você está distribuindo um biblioteca reutilizável). Em segundo lugar, a maioria das operações em NET, incluindo as assinaturas dos métodos expostos pelo BCL (pela razão acima), utilizar um número inteiro assinado. Então, se você está pensando em realmente usando seu inteiro sem sinal, você provavelmente vai encontrar-se lançando-lo um pouco. Isso vai ter um pequeno impacto na performance e fará o seu código um pouco mais confusa. No final, não é provavelmente vale a pena.

TLDR; de volta no meu C ++ dias, eu diria "Use o que é mais adequado e deixar o compilador tipo o resto fora." C # não é tão cut-and-dry, então eu diria que este for .NET: Não há realmente nenhuma diferença de desempenho entre um inteiro assinado e não assinado em x86 / x64, mas a maioria das operações requerem um inteiro assinado, a menos que você realmente precisa restringir os valores para positiva apenas ou você realmente precisa da faixa extra que o bit de sinal come, vara com um inteiro assinado. Seu código será mais limpo no final.

Eu não tenho feito qualquer investigação sobre o assunto em .NET, mas nos velhos tempos de Win32 / C ++, se você queria lançar um "int assinado" a um "assinado longa", a CPU tinha de executar um op para estender o sinal. Para lançar um "int não assinado" a um material "unsigned long", ele só tinha zero nos bytes superiores. Poupança foi da ordem de um par de ciclos de relógio (ou seja, você tem que fazê-lo bilhões de vezes para ter uma diferença ainda perceptível)

Não há nenhuma diferença, desempenho sábio. cálculos de números inteiros simples são bem conhecidos e modernos de CPU são altamente otimizado para realizá-las rapidamente.

Estes tipos de otimizações são raramente vale o esforço. Use o tipo de dados que é mais adequado para a tarefa e deixar por isso mesmo. Se essa coisa tanto como toques um banco de dados que você poderia provavelmente encontrar uma dúzia de ajustes no projeto DB, sintaxe de consulta ou estratégia de indexação que compensar uma otimização de código em C # por algumas centenas de ordens de magnitude.

Seu ir para alocar a mesma quantidade de memória de qualquer forma (embora a um possível armazenar um valor maior, como não é economia de espaço para o sinal). Então, eu duvido que você vai ver uma diferença 'performance', a menos que você usar grandes valores / valores negativos que fará com que uma opção ou outra para explodir.

este não é realmente a ver com desempenho, em vez requisitos para o contador de loop.

Prehaps havia muitas iterações para completar

        Console.WriteLine(Int32.MaxValue);      // Max interation 2147483647
        Console.WriteLine(UInt32.MaxValue);     // Max interation 4294967295

O int não assinado pode estar lá por uma razão.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top