¿Por qué es difícil otorgar eficiencia al usar bibliotecas?


10

Cualquier procesamiento de base de datos pequeño puede ser abordado fácilmente por scripts Python / Perl / ..., que usan bibliotecas y / o incluso utilidades del lenguaje mismo. Sin embargo, cuando se trata de rendimiento, las personas tienden a buscar C / C ++ / lenguajes de bajo nivel. La posibilidad de adaptar el código a las necesidades parece ser lo que hace que estos lenguajes sean tan atractivos para BigData, ya sea en relación con la administración de memoria, el paralelismo, el acceso al disco o incluso las optimizaciones de bajo nivel (a través de construcciones de ensamblaje en el nivel C / C ++).

Por supuesto, este conjunto de beneficios no tendría un costo: escribir el código y, a veces, incluso reinventar la rueda , puede ser bastante costoso / agotador. Aunque hay muchas bibliotecas disponibles, las personas se inclinan a escribir el código por sí mismas cuando necesitan otorgar rendimiento. ¿Qué deshabilita las afirmaciones de rendimiento del uso de bibliotecas al procesar grandes bases de datos?

Por ejemplo, considere una empresa que rastrea continuamente páginas web y analiza los datos recopilados. Para cada ventana deslizante, se ejecutan diferentes algoritmos de minería de datos sobre los datos extraídos. ¿Por qué los desarrolladores dejarían de usar las bibliotecas / marcos disponibles (ya sea para rastreo, procesamiento de texto y minería de datos)? Usar cosas ya implementadas no solo aliviaría la carga de codificar todo el proceso, sino que también ahorraría mucho tiempo.

En un solo disparo :

  • ¿Qué hace que escribir el código por sí mismo sea una garantía de rendimiento?
  • ¿Por qué es arriesgado confiar en marcos / bibliotecas cuando debe garantizar un alto rendimiento?

1
¿Puedes aclarar la pregunta exacta? Quizás algunas respuestas posibles que tenga en mente también puedan ayudar.
Amir Ali Akbari

@AmirAliAkbari SeanOwen publicó una respuesta y noté la falta de especificidad en mi pregunta. He agregado un comentario a su publicación. Por favor, siéntase libre de sugerir cualquier mejora en la publicación; de lo contrario, estoy planeando eliminarla.
Rubens

Respuestas:


4

Habiendo hecho el juego de reescritura una y otra vez (y aún lo hago), mi reacción inmediata fue la adaptabilidad .

Si bien los marcos y las bibliotecas tienen un enorme arsenal de rutinas (posiblemente entrelazadas) para tareas estándar, su propiedad de marco a menudo (¿siempre?) No permite accesos directos. De hecho, la mayoría de los marcos tienen algún tipo de infraestructura central alrededor de la cual se implementa una capa central de funcionalidad básica. La funcionalidad más específica hace uso de la capa básica y se coloca en una segunda capa alrededor del núcleo.

Ahora por atajos me refiero a pasar directamente de una rutina de segunda capa a otra rutina de segunda capa sin usar el núcleo. Un ejemplo típico (de mi dominio) serían las marcas de tiempo: tiene un origen de datos con algún tipo de marca de tiempo. Hasta ahora, el trabajo es simplemente leer los datos del cable y pasarlos al núcleo para que su otro código pueda deleitarse con ellos.

Ahora su industria cambia el formato de marca de tiempo predeterminado por una muy buena razón (en mi caso, pasaron del tiempo de Unix al tiempo de GPS). A menos que su marco sea específico de la industria, es muy poco probable que estén dispuestos a cambiar la representación central del tiempo, por lo que terminará usando un marco que casi hace lo que desea. Cada vez que acceda a sus datos, primero debe convertirlos al formato de tiempo de la industria, y cada vez que desee modificarlos, debe volver a convertirlos a lo que el núcleo considere apropiado. No hay forma de que pueda transferir datos directamente desde la fuente a un sumidero sin doble conversión.

Aquí es donde sus marcos hechos a mano brillarán, es solo un cambio menor y usted está volviendo a modelar el mundo real, mientras que todos los otros marcos (no específicos de la industria) ahora tendrán una desventaja de rendimiento.

Con el tiempo, la discrepancia entre el mundo real y el modelo se sumará. Con un marco off-the-shelf que pronto iba a enfrentar a preguntas como: ¿Cómo puedo representar thisen thato cómo hacer la rutina Xaceptar / producirlo Y.

Hasta ahora no se trataba de C / C ++. Pero si, por alguna razón, no puede cambiar el marco, es decir, si tiene que soportar una doble conversión de datos para ir de un extremo a otro, entonces normalmente empleará algo que minimice la sobrecarga adicional. En mi caso, un convertidor TAI-> UTC o UTC-> TAI es mejor dejarlo en bruto C (o un FPGA). No hay elegancia posible, ni una estructura de datos inteligentes profunda que haga que el problema sea trivial. Es solo una aburrida declaración de cambio, y ¿por qué no usar un lenguaje cuyos compiladores son buenos para optimizar exactamente eso?


1
+1 Puede ser mi culpa por no ser muy claro en mi publicación, por lo que otros no lo habían entendido antes. Este es seguramente el tipo de respuesta que estaba buscando. Gracias.
Rubens

7

No creo que todos busquen C / C ++ cuando el rendimiento es un problema.

La ventaja de escribir código de bajo nivel es usar menos ciclos de CPU o, a veces, menos memoria. Pero me gustaría señalar que los idiomas de nivel superior pueden recurrir a los idiomas de nivel inferior, y lo hacen, para obtener algo de este valor. Los lenguajes Python y JVM pueden hacer esto.

El científico de datos que usa, por ejemplo, scikit-learn en su escritorio ya está llamando a rutinas nativas altamente optimizadas para hacer el cálculo de números. No tiene sentido escribir un nuevo código para la velocidad.

En el contexto de "big data" distribuido, es más típico que se produzca un cuello de botella en el movimiento de datos: transferencia de red y E / S. El código nativo no ayuda. Lo que ayuda no es escribir el mismo código para que se ejecute más rápido, sino escribir un código más inteligente.

Los lenguajes de nivel superior le permitirán implementar algoritmos distribuidos más sofisticados en una cantidad determinada de tiempo de desarrollador que C / C ++. A escala, el algoritmo más inteligente con mejor movimiento de datos vencerá al código nativo tonto.

También suele ser cierto que el tiempo del desarrollador y los errores cuestan mucho más que el hardware nuevo. Un año del tiempo de un desarrollador senior podría ser de $ 200K completamente cargado; durante un año que también alquila cientos de servidores por valor de tiempo de cálculo. Puede que no tenga sentido en la mayoría de los casos molestarse en optimizar el lanzamiento de más hardware.

¿No entiendo el seguimiento sobre "conceder" y "deshabilitar" y "afirmar"?


Perdón por el malentendido. Mi intención era presentar respuestas sobre la importancia de tener control sobre una aplicación y cómo las bibliotecas aflojan este control . Por supuesto, puede asumir cosas sobre ellos (las personas normalmente no reescriben pthreads), pero si los datos cambian (carga, rendimiento, ...), es posible que deba acceder a la fuente lib para garantizar el rendimiento. Y sí, no es necesariamente C / C ++, aunque generalmente son los idiomas elegidos para hpc. ¿Puedo eliminar mi pregunta o le gustaría cambiarla por algo más específico? Acepto cualquier sugerencia para mejorarlo.
Rubens

1
No, es una buena pregunta, puede reflejar sus comentarios aquí en ediciones de la pregunta si lo desea.
Sean Owen

Por favor, verifique si la pregunta tiene sentido ahora. He agregado un pequeño caso para hacerlo más sencillo. En caso de que desee agregar alguna consideración a la pregunta, no dude en editarla.
Rubens

4

Como todos sabemos, en el mundo digital hay muchas maneras de hacer el mismo trabajo / obtener los resultados esperados.

Y las responsabilidades / riesgos que provienen del código están en los hombros de los desarrolladores.

Esto es pequeño pero supongo que es un ejemplo muy útil del mundo .NET.

Muchos desarrolladores de .NET usan el BinaryReader incorporado - BinaryWriter en su serialización de datos para el rendimiento / obtener control sobre el proceso.

Este es el código fuente de CSharp de FrameWork integrado en la clase BinaryWriter, uno de los métodos de escritura sobrecargados:

// Writes a boolean to this stream. A single byte is written to the stream
// with the value 0 representing false or the value 1 representing true.
// 
public virtual void Write(bool value) 
{
     //_buffer is a byte array which declared in ctor / init codes of the class
    _buffer = ((byte) (value? 1:0));

    //OutStream is the stream instance which BinaryWriter Writes the value(s) into it.
    OutStream.WriteByte(_buffer[0]);
}

Como puede ver, este método podría escribirse sin la asignación adicional a la variable _buffer:

public virtual void Write(bool value) 
{
    OutStream.WriteByte((byte) (value ? 1 : 0));
}

Sin asignar, podríamos ganar unos pocos milisegundos ... Estos pocos milisegundos pueden aceptar como "casi nada", pero ¿y si hay miles de escritos (es decir, en un proceso de servidor)?

Supongamos que "pocos" es 2 (milisegundos) y las instancias de miles de veces son solo 2.000. Esto significa 4 segundos más de tiempo de proceso ...

Si continuamos sujetos desde .NET y si puede verificar los códigos fuente de BCL - Biblioteca de clase base .NET - desde MSDN, puede ver una gran cantidad de pérdidas de rendimiento del desarrollador decide.

Cualquiera de los puntos de la fuente BCL Es normal que vea que el desarrollador decidió usar los bucles while () o foreach () que podrían implementar un bucle for () más rápido en su código.

Estas pequeñas ganancias nos dan el rendimiento total.

Y si volvemos al Método BinaryWriter.Write () ...

En realidad, la asignación adicional a una implementación de _buffer no es un error del desarrollador ... ¡Esto es exactamente decidir "mantenerse a salvo"!

Supongamos que decidimos no usar _buffer y decidimos implementar el segundo método ... Si intentamos enviar varios miles de bytes a través de un cable (es decir, cargar / descargar datos BLOB o CLOB) con el segundo método, puede fallar comúnmente porque de conexión perdida ... Porque tratamos de enviar todos los datos sin ninguna verificación ni mecanismo de control. Cuando se pierde la conexión, tanto el servidor como el Cliente nunca saben si los datos enviados se completaron o no.

Si el desarrollador decide "mantenerse a salvo", entonces normalmente significa que los costos de rendimiento dependen de los mecanismos implementados de "mantenerse a salvo".

Pero si el desarrollador decide "arriesgarse, ganar rendimiento", esto no es un error también ... Hasta que haya algunas discusiones sobre la codificación "arriesgada".

Y como una pequeña nota: los desarrolladores de bibliotecas comerciales siempre intentan mantenerse a salvo porque no pueden saber dónde usará su código.


4

Desde la perspectiva de los programadores, los marcos rara vez apuntan al rendimiento como la máxima prioridad. Si su biblioteca va a ser ampliamente aprovechada, las cosas que las personas probablemente valorarán más son la facilidad de uso, la flexibilidad y la confiabilidad.

El rendimiento generalmente se valora en bibliotecas secundarias competitivas. "La biblioteca X es mejor porque es más rápida". Incluso entonces, con mucha frecuencia, esas bibliotecas intercambiarán la solución más óptima por una que pueda ser ampliamente aprovechada.

Al utilizar cualquier marco de trabajo, está asumiendo el riesgo de que exista una solución más rápida. Podría ir tan lejos como para decir que casi siempre existe una solución más rápida.

Escribir algo usted mismo no es una garantía de rendimiento, pero si sabe lo que está haciendo y tiene un conjunto bastante limitado de requisitos, puede ayudar.

Un ejemplo podría ser el análisis JSON. Existen cientos de bibliotecas para una variedad de idiomas que convertirán a JSON en un objeto de referencia y viceversa. Sé de una implementación que lo hace todo en los registros de la CPU. Es notablemente más rápido que todos los demás analizadores, pero también es muy limitado y esa limitación variará según la CPU con la que esté trabajando.

¿Es la tarea de construir un analizador JSON específico de entorno de alto rendimiento una buena idea? Aprovecharía una biblioteca respetada 99 de cada 100 veces. En esa instancia separada, unos pocos ciclos de CPU adicionales multiplicados por un millón de iteraciones harían que el tiempo de desarrollo valiera la pena.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.