2009-06-29 13 views
16

Estamos buscando migrar una aplicación de rendimiento crítico a .Net y encontrar que la versión de C# es 30% a 100% más lenta que Win32/C dependiendo del procesador (diferencia más marcada en el procesador T7200 móvil). Tengo una muestra muy simple de código que demuestra esto. Por razones de brevedad me acaba de mostrar la versión C - C# es una traducción directa:¿Por qué la diferencia de rendimiento entre C# (bastante más lento) y Win32/C?

#include "stdafx.h" 
#include "Windows.h" 

int array1[100000]; 
int array2[100000]; 

int Test(); 

int main(int argc, char* argv[]) 
{ 
    int res = Test(); 

    return 0; 
} 

int Test() 
{ 
    int calc,i,k; 
    calc = 0; 

    for (i = 0; i < 50000; i++) array1[i] = i + 2; 

    for (i = 0; i < 50000; i++) array2[i] = 2 * i - 2; 

    for (i = 0; i < 50000; i++) 
    { 
     for (k = 0; k < 50000; k++) 
     { 
      if (array1[i] == array2[k]) calc = calc - array2[i] + array1[k]; 
      else calc = calc + array1[i] - array2[k]; 
     } 
    } 
    return calc; 
} 

Si nos fijamos en el desmontaje de Win32 para la 'cosa' que tenemos:

35:    else calc = calc + array1[i] - array2[k]; 
004011A0 jmp   Test+0FCh (004011bc) 
004011A2 mov   eax,dword ptr [ebp-8] 
004011A5 mov   ecx,dword ptr [ebp-4] 
004011A8 add   ecx,dword ptr [eax*4+48DA70h] 
004011AF mov   edx,dword ptr [ebp-0Ch] 
004011B2 sub   ecx,dword ptr [edx*4+42BFF0h] 
004011B9 mov   dword ptr [ebp-4],ecx 

(esto es en la depuración pero hay que tener conmigo)

el desmontaje de la versión optimizada C# utilizando el depurador de CLR en el exe optimizado:

    else calc = calc + pev_tmp[i] - gat_tmp[k]; 
000000a7 mov   eax,dword ptr [ebp-4] 
000000aa mov   edx,dword ptr [ebp-8] 
000000ad mov   ecx,dword ptr [ebp-10h] 
000000b0 mov   ecx,dword ptr [ecx] 
000000b2 cmp   edx,dword ptr [ecx+4] 
000000b5 jb   000000BC 
000000b7 call  792BC16C 
000000bc add   eax,dword ptr [ecx+edx*4+8] 
000000c0 mov   edx,dword ptr [ebp-0Ch] 
000000c3 mov   ecx,dword ptr [ebp-14h] 
000000c6 mov   ecx,dword ptr [ecx] 
000000c8 cmp   edx,dword ptr [ecx+4] 
000000cb jb   000000D2 
000000cd call  792BC16C 
000000d2 sub   eax,dword ptr [ecx+edx*4+8] 
000000d6 mov   dword ptr [ebp-4],eax 

Muchas más instrucciones, presumiblemente la causa de la diferencia de rendimiento.

SO3 preguntas realmente:

  1. estoy mirando el desmontaje correcto para los 2 programas o me están engañando a las herramientas?

  2. Si la diferencia en el número de instrucciones generadas no es la causa de la diferencia ¿cuál es?

  3. ¿Qué podemos hacer al respecto aparte de mantener nuestro código crítico de rendimiento en una DLL nativa?

Gracias de antemano Steve

PS Recibí una invitación recientemente a una articulación MS/Intel seminario titulado algo así como 'aplicaciones nativas de rendimiento crítico Edificio' Hmm ...

+0

¿Podría eliminar todas las líneas nuevas entre las instrucciones de ensamblaje? –

+0

Como siempre, perfílalo para ver exactamente cuánto cuesta el mayor rendimiento. (No hay forma de que podamos ver lo que toma tiempo en su código, así que no tiene sentido preguntarnos. Pregúntele a un generador de perfiles en su lugar) Aparte de eso, un truco simple podría ser ejecutar su código C# a través de NGen. Eso debería aumentar el rendimiento bastante. – jalf

+0

¿Qué versión del CLR está comparando? Por lo que sé, el compilador .NET 3.5 SP1 JIT es más eficiente que los anteriores. Además, el optimizador x64 JIT es más agresivo que x86 one. –

Respuesta

13

creo está viendo los resultados de las verificaciones de límites en las matrices. Puede evitar las verificaciones de límites utilizando un código no seguro.

Creo que el JITer puede reconocer patrones como bucles que van hasta array.Length y evita la comprobación de límites, pero no parece que su código pueda utilizar eso.

+9

Veo muchas de estas manzanas-naranjas "idénticas código "intenta hacer comparaciones con el código de juguete. Sin embargo, nunca veo una comparación negativa con un código completo de calidad comparable. Tal vez porque C# no es realmente más lento. –

+1

@Greg D: Estoy de acuerdo. Trabajo casi exclusivamente en procesamiento numérico de alto rendimiento y orientado a la ciencia. C# tiene un rendimiento muy diferente. perfil que C++, sin embargo, por lo que la creación de perfiles es fundamental, pero en general, puede obtener C# para ser tan rápido como C++ con el perfil adecuado y los ajustes en el código. –

+2

@Greg, Reed: la mayoría de los problemas que veo con el rendimiento del código administrado no están relacionados con el tiempo de CPU como este, sino con el tiempo de carga y la huella de memoria. Para estos, C++ todavía tiene una gran ventaja (aunque los programadores malos pueden negar fácilmente esa ventaja :) – Michael

0

Estoy seguro de que la optimización para C es diferente de C#. También debe esperar que al menos un poco de rendimiento disminuya. .NET agrega otra capa a la aplicación con el marco.

El intercambio es un desarrollo más rápido, grandes bibliotecas y funciones, para (lo que debería ser) una pequeña cantidad de velocidad.

2

C# está haciendo una comprobación de límites

cuando se ejecuta la parte de cálculo en C# código no seguro que no funciona tan bien como la implementación nativa?

18

Creo que su problema principal en este código será la comprobación de límites en sus matrices.

Si cambia a usar el código inseguro en C# y utiliza el cálculo del puntero, debería poder obtener el mismo código (o más rápido).

Este mismo problema fue previously discussed in detail in this question.

6

Como han dicho otros, uno de los aspectos es la comprobación de límites. También hay algo de redundancia en el código en términos de acceso a la matriz. He conseguido mejorar el rendimiento un poco cambiando el bloque interno a:

int tmp1 = array1[i]; 
int tmp2 = array2[k]; 
if (tmp1 == tmp2) 
{ 
    calc = calc - array2[i] + array1[k]; 
} 
else 
{ 
    calc = calc + tmp1 - tmp2; 
} 

Ese cambio golpeó el tiempo total desde ~ ~ 8.8s a 5s.

+0

@Jon: Tal vez me esté perdiendo algo, pero no puedo medir ninguna diferencia significativa de rendimiento entre su versión y la versión del OP. De hecho, tampoco esperaría que un cambio tan mínimo tuviera tanto impacto en el rendimiento. –

+0

Ni siquiera yo particularmente, pero ciertamente lo hago para mí, en .NET 3.5 y 4.0b1.Compilado con/o +/debug-on Vista de 32 bits como una aplicación de consola. También cambié el alcance de las variables iy k, pero dudo que eso sea significativo. –

+0

(Lo he probado lo suficiente para asegurarme de que no es solo una casualidad, por cierto :) –

1

Si la ruta crítica del rendimiento de su aplicación consiste enteramente en el procesamiento de la matriz no verificada, le aconsejo que no la reescriba en C#.

Pero entonces, si su aplicación ya funciona bien en el lenguaje X, te aconsejo que no volver a escribir en el lenguaje Y.

¿Qué desea obtener de la reescritura? Por lo menos, considere seriamente una solución de idioma mixto, use su código C ya depurado para las secciones de alto rendimiento y use C# para obtener una interfaz de usuario agradable o una integración conveniente con las últimas bibliotecas ricas de .NET.

A longer answer on a possibly related theme.

4

Sólo por diversión, he intentado construir esto en C# en Visual Studio 2010, y tomó un vistazo al desmontaje JITed:

    else 
         calc = calc + array1[i] - array2[k]; 
000000cf mov   eax,dword ptr [ebp-10h] 
000000d2 add   eax,dword ptr [ebp-14h] 
000000d5 sub   eax,edx 
000000d7 mov   dword ptr [ebp-10h],eax 

Hicieron una serie de mejoras a la fluctuación de 4.0 del CLR.

Cuestiones relacionadas