2010-11-17 13 views
17

Escribí parte de un programa que realiza un trabajo pesado con cadenas en C#. Inicialmente elegí C# no solo porque era más fácil usar las estructuras de datos de .NET, sino también porque necesito usar este programa para analizar unos 2-3 millones de registros de texto en una base de datos, y es mucho más fácil conectarme a bases de datos usando C# .Mejoras en el rendimiento al volver a escribir el código C# en C/C++

Hubo una parte del programa que ralentizaba todo el código, y decidí reescribirla en C usando punteros para acceder a todos los caracteres de la cadena, y ahora la parte del código que tardó unos 119 segundos en analizar 10,000,000 cadenas en C# toma el código C solo 5 segundos! El rendimiento es una prioridad, entonces estoy considerando volver a escribir todo el programa en C, compilarlo en un dll (algo que no sabía cómo hacer cuando comencé a escribir el programa) y usar DllImport desde C# para usar sus métodos para trabajar con las cadenas de la base de datos.

Dado que reescribir todo el programa tomará algún tiempo, y dado que el uso de DllImport para trabajar con cadenas de C# 's requiere de clasificación y tales cosas, mi pregunta es la voluntad de las mejoras de rendimiento de manejo de cadenas más rápido de la DLL de C son mayores que el impacto en el rendimiento de tener para ordenar repetidamente cadenas para acceder al dll C desde C#?

+18

Creo que debería mostrar cómo se manejan las cadenas en C#. Si bien es cierto que los programas C deberían ser más rápidos, también es cierto que no debería haber una diferencia de rendimiento ** ENORME ** entre los dos. –

+10

Probablemente pueda acelerar su código C# original usando StringBuilder. Difícil de decir sin ver ningún código. – Henrik

+1

Hay más de una formas de implementar un programa en C#. ¿Has elegido la implementación más eficiente? –

Respuesta

10

En primer lugar, el perfil de su código. Es posible que encuentre un verdadero cabezal que acelera enormemente el código C#.

En segundo lugar, escribir el código en C usando punteros no es realmente una comparación justa. Si va a utilizar punteros, ¿por qué no escribirlo en lenguaje ensamblador y obtener un rendimiento real? (No realmente, solo reductio ad absurdam.) Una mejor comparación para el código nativo sería usar std::string.De esta forma, todavía recibe mucha ayuda de la clase string y C++ excepción-seguridad.

Teniendo en cuenta que usted tiene que leer 2-3 millones de registros de la base de datos para hacer este trabajo, dudo mucho que el tiempo dedicado a agrietarse las cuerdas va a pesar más que el transcurrido tiempo necesario para cargar los datos de la DB. Por lo tanto, considere en cambio cómo estructurar su código para que pueda comenzar el procesamiento de cadenas mientras la carga de DB está en progreso.

Si usa SqlDataReader (por ejemplo) para cargar las filas secuencialmente, debería ser posible combinar N filas lo más rápido posible y transferirlas a una secuencia separada para el procesamiento posterior que es su dolor de cabeza y motivo actuales para esta pregunta. Si está en .Net 4.0, esto es lo más simple de hacer usando Task Parallel Library, y System.Collections.Concurrent también podría ser útil para la comparación de resultados entre los subprocesos.

Este enfoque debería significar que ni la latencia DB ni el procesamiento de cadenas son un cuello de botella que se detiene, porque ocurren en paralelo. Esto se aplica al incluso si se encuentra en una máquina de un solo procesador porque su aplicación puede procesar cadenas mientras espera que el siguiente lote de datos regrese del DB a través de la red. Si encuentra que el procesamiento de cadena es el más lento, use más hilos (es decir, Task s) para eso. Si el DB es el cuello de botella, entonces debe considerar los medios externos para mejorar su rendimiento: hardware o esquema de DB, infraestructura de red. Si necesita algunos resultados antes de procesar más datos, TPL permite que se creen dependencias entre Task sy el hilo coordinador.

Mi punto es que dudo que valga la pena reiniciar toda la aplicación en C nativa o lo que sea. Hay muchas maneras de despellejar a este gato.

+0

Buen punto acerca del perfilado. –

+0

@Grigory - gracias. Por lo general, este es el primer punto ... En cualquier caso, un diseño paralelizado debería ayudar, incluso si el código actual es óptimo cuando se ve de forma aislada. –

+0

@Steve: una implementación paralela no ayudará si el problema se debe a la presión del GC causada por millones de pequeñas asignaciones. Cada hilo se ahogará en el recolector de basura. –

2

Con cuerdas ser inmutable en .NET, no tengo duda de que un optimizado aplicación C superará un optimizado C# implementado - sin duda!

P/Invoke incurre en una sobrecarga pero si implementa la mayor parte de la lógica en C y solo expone una API muy granular para C#, creo que está en una forma mucho mejor.

Al final del día, escribir una implementación en C significa tomar más tiempo, pero eso le dará un mejor rendimiento si está preparado para un costo de desarrollo adicional.

+1

El OP dice que la aplicación está analizando cadenas, por lo que suponiendo que esto implique principalmente operaciones de lectura, la inmutabilidad puede no ser la culpable aquí. –

+2

Tengo serias dudas de que una versión * optimizada * C# sea más lenta. El límite fundamental es el ancho de banda del bus RAM. Es simplemente más difícil optimizar el código C# con la habilidad de la cuerda para crear copias y verificar la indexación de la matriz. –

+0

No lo dudo por un segundo. Con los calces de cuerdas y la optimización de cadenas pequeñas, puede hacer más cosas utilizando menos ancho de banda de RAM. – MSalters

10

Una opción es reescribir el código C como inseguro C#, que debería tener aproximadamente el mismo rendimiento y no incurrirá en ninguna penalización de interoperabilidad.

+4

No tendrá el mismo rendimiento, espero. Todavía hay cosas como diferencias de asignación dinámica y similares. – Puppy

+0

+1 por mencionarlo. Pero personalmente no lo haría. – Aliostad

+3

@DeadMG: la ventaja de C no es que malloc sea más rápido (de hecho, es mucho más lento), sino que puede reducir sustancialmente la cantidad de llamadas al asignador de memoria. Además, no hace controles fuera de límites cuando se accede a elementos de la matriz. C# inseguro tiene los mismos beneficios. –

4

No hay ninguna razón para escribir en C sobre C++, y C/C++ no existe.

Las implicaciones de rendimiento de la clasificación son bastante simples. Si tienes que ordenar cada cadena individualmente, tu actuación será mala. Si puede reunir los diez millones de cadenas en una sola llamada, la clasificación no tendrá ninguna importancia. P/Invoke no es la operación más rápida del mundo, pero si solo la invocas varias veces, en realidad no importará.

Puede ser más fácil volver a escribir su aplicación principal en C++ y luego usar C++/CLI para fusionarla con el final de la base de datos C#.

2

Familiarícese con conjuntos mixtos: esto es mejor que Interop. Interop es una forma rápida de manejar libs nativos, pero los ensambles mixtos funcionan mejor.
Mixed assemblies on MSDN
Como siempre lo más importante es probar y medir ...

0

Para la concatenación de cadenas largas o cadenas múltiples siempre use StringBuilder. Lo que no todo el mundo sabe es que StringBuilder no solo se puede usar para hacer cadenas de concatenación más rápidas, sino también para insertar, quitar y reemplazar caracteres.

Si esto no es lo suficientemente rápido para usted, puede usar matrices de bytes 0 en lugar de una cadena y operar en estos. Si ha terminado con la manipulación, puede convertir la matriz a una cadena.

También existe la opción en C# para usar código no seguro para obtener un puntero a una cadena y actualiza la cadena de otro modo inmutable, pero yo no recomendaría esto.

Como ya han dicho otros, puede usar administrado C++ (C++/CLI) para interoperar entre .NET y el código administrado.

¿Te importaría mostrarnos el código, quizás haya otras opciones para optimizar?

0

Cuando comienza a optimizar un programa en una etapa tardía (la aplicación se escribió sin tener en cuenta la optimización), entonces debe identificar los cuellos de botella.

El perfilado es el primer paso para ver hacia dónde van todos esos ciclos de CPU.

Solo tenga en cuenta que los perfiles de C# solo perfilarán su aplicación .Net, no el servidor IIS implementado en el kernel ni la pila de red.

Y esto puede ser un cuello de botella invisible que late en varios órdenes de magnitud en lo que te estás enfocando cuando intentas avanzar.

Cree que no tiene influencia en IIS implementado como controlador de kernel, y tiene razón.

Pero puede prescindir de esto y ahorrar mucho tiempo y dinero.

Ponga su talento donde pueda marcar la diferencia, no donde se ve obligado a correr con los pies atados.

+0

- En ningún otro lugar este tema involucra a IIS, pero por alguna razón no puede dejar de mencionar g-wan. - He visto el código C# que falló tan rápido y lo primero que noté: abuso de cadenas. http://www.g-wan.com/source//loan.aspx.txt – ZippyV

+0

Estoy de acuerdo con ZippyV. La pregunta original es acerca de la alternativa C# y C o C++ sin procesar, y el acceso a la base de datos. Me pregunto si Jerome es fanático de los productos o si está probando algún tipo de colocación de productos ... – paercebal

3

Aquí hay algunas respuestas bastante buenas, especialmente @Steve Townsend's.

Sin embargo, sentí que vale la pena subrayar un punto clave: No hay intrínsecamente ninguna razón por la que el código C "será más rápido" que el código C#. Esa idea es un mito. Debajo del capó, ambos producen código de máquina que se ejecuta en la misma CPU. Siempre que no solicite al C# que haga más trabajo que el C, entonces puede funcionar igual de bien.

Al cambiar a C, se forzó a ser más frugal (evitó usar características de alto nivel como cadenas administradas, comprobación de límites, recolección de basura, manejo de excepciones, etc., y simplemente trató sus cadenas como bloques de bytes sin procesar) . Si aplica estas técnicas de bajo nivel a su código C# (es decir, tratando sus datos como bloques de bytes sin procesar como lo hizo en C), encontrará mucha menos diferencia en la velocidad.

Por ejemplo: La semana pasada reescribí (en C#) una clase que había escrito un junior (también en C#). Logré una mejora de velocidad de 25x con respecto al código original aplicando el mismo enfoque que utilizaría si lo escribía en C (es decir, pensando en sobre el rendimiento). Logré la misma aceleración que reclamas sin tener que cambiar a un idioma diferente.

Finalmente, el hecho de que un caso aislado se pueda hacer 24 veces más rápido, no significa que pueda hacer todo su programa 24 veces más rápido transfiriéndolo todo a C. Como dijo Steve, perfile para ver dónde está lento, y gastar su esfuerzo solo donde proporcionará beneficios significativos. Si convierte ciegamente a C, probablemente encontrará que ha dedicado mucho tiempo a hacer que un código que ya funciona sea mucho menos sostenible.

(PD Mi punto de vista proviene de 29 años de experiencia escribiendo código ensamblador, C, C++ y C# y entendiendo que el lenguaje es solo una herramienta para generar código máquina; en el caso de C# vs C++ vs. C es principalmente la habilidad del programador, no el lenguaje utilizado, lo que determina si el código se ejecutará de manera rápida o lenta. Los programadores C/C++ tienden a ser mejores que los programadores C# porque tienen que ser - C# te permite ser flojo y obtener el código escrito rápidamente, mientras que C/C++ te hace trabajar más y el código tarda más en escribir. Pero un buen programador puede obtener un gran rendimiento de C#, y un programador pobre puede arrebatar un rendimiento abismal de C/C++)

0

las diferencias inherentes se dan generalmente como 2x menos CPU, memoria 5x. En la práctica, pocas personas son lo suficientemente buenas en C++ o para obtener los beneficios.

Hay una ganancia adicional para escatimar en soporte Unicode, pero solo usted puede conocer su aplicación lo suficientemente bien como para saber si eso es seguro.

Utilice primero el perfilador, asegúrese de que no está vinculado a E/S.

Cuestiones relacionadas