¿Cuáles son algunos de los principales factores que pueden afectar la velocidad de ejecución de un algoritmo?

Algunas sugerencias ordenadas por “mejora potencial con el tiempo que necesita invertir”:

  1. Tenga en cuenta cómo se implementan varias operaciones integradas y tipos de datos en el idioma que elija. Puede resultar que algo que pensabas toma tiempo constante, en realidad toma mucho más tiempo. Entonces puedes optimizarlo.
    Ejemplos: operaciones en cadenas y matrices, como su tamaño, tomar una porción o incluso encontrar el enésimo elemento (en una cadena unicode).
  2. Localidad de la memoria: tenga en cuenta cómo funcionan la memoria virtual y el caché de la CPU.
    Todos los datos que su programa está procesando en algún momento deben llevarse a (a) memoria física y (b) caché de la CPU. Cuanto más se dispersan, más operaciones de intercambio / intercambio se necesitan y su programa efectivamente necesita mucho más tiempo para acceder a la memoria.
    Puede verificar las estadísticas del sistema para ver si su programa está entrando y saliendo (memoria virtual). Si hay suficiente RAM, eso no debería ser un problema. Pero exactamente el mismo mecanismo funciona en una escala menor en el nivel de caché de la CPU.
    Ejemplo : resumir todos los elementos en una matriz de números.
    1. La peor solución: la matriz se almacena como una lista vinculada de listas vinculadas de registros que pueden dispersarse arbitrariamente en la memoria. En primer lugar, usas mucha memoria. En segundo lugar, los elementos consecutivos a los que se accede no tienen que estar cerca uno del otro.
    2. Mejor solución: matriz 2D de números, iterar sobre “columnas” agregando todos los elementos en una columna. Se utiliza menos memoria, pero los números consecutivos a los que se accede están lejos el uno del otro y el caché de la CPU realiza más intercambios.
    3. La mejor solución: matriz 2D de números, iterar sobre “filas” agregando todos los elementos en una fila. Los números consecutivos a los que se accede están uno al lado del otro, lo que minimiza el intercambio, tanto para el caché de la CPU como para la memoria virtual.
  3. Haga que su programa sea paralelo y use todos los núcleos de CPU que tenga.
    Las CPU no serán mucho más rápidas en el futuro. Por un lado, la información no puede viajar más rápido que la luz (para un reloj de 3 GHz es de 10 cm). Por otro lado, los transistores no pueden ser infinitamente pequeños, ya que están hechos de átomos.
    Por lo tanto, es más fácil poner más núcleos de CPU que se ejecutan en paralelo.
    Para hacer uso de ese poder, debe reescribir su algoritmo para que se ejecute simultáneamente varios hilos. Es difícil. Un problema es repensar su algoritmo. Otra es sincronizar los hilos que operan en una estructura de datos común. Pero la parte más difícil es asegurarse de que su programa sea correcto. En la programación concurrente, las pruebas no ayudan mucho, ya que no verifica todas las formas posibles en que los hilos pueden intercalar su ejecución.

Una pregunta justa. Es imprescindible separar las cosas para comprender mejor los detalles.

En primer lugar, todo se reduce a un presupuesto de ejecución . Para una entrada dada (y suponiendo que nada cambie en la ejecución de una entrada dada), su algoritmo requiere N instrucciones para ejecutarse. Hay oportunidades intrínsecas que el hardware subyacente puede explotar, por lo que también deben ser capturadas:

[matemáticas] f (x, i) = \ frac {B (x, i)} {P (x, i)} [/ matemáticas]

Definiciones:

  • [matemática] B (x, i) [/ matemática]: instrucciones ejecutadas para una entrada dada [matemática] i [/ matemática] del algoritmo [matemática] x [/ matemática].
  • [matemática] P (x, i) [/ matemática]: paralelismo inherente disponible mientras se ejecuta el algoritmo [matemática] x [/ matemática] para una entrada dada [matemática] i [/ matemática]. Similar a la aceleración, pero más elaborado.

En esta formulación, hay dos factores básicos que afectan la velocidad de ejecución, y se pueden combinar:

  1. Instrucciones ejecutadas: en pocas palabras, su algoritmo debe apuntar a reducir el número de instrucciones. Sin embargo, debe ejecutar una secuencia de instrucciones que tengan el menor costo de ejecución . Para ello, un compilador optimizador utiliza modelos de máquina que proporcionan este tipo de información al seleccionar instrucciones. Puede medir este presupuesto con herramientas como Perf: Perf Wiki
  2. Paralelismo disponible: ya sea paralelismo a nivel de instrucción, de paralelismo explícito como subprocesamiento / agrupamiento, cargas de trabajo de reducción de mapas. El hardware realizará el paralelismo a nivel de instrucción siempre que sea posible y posible. Los niveles más altos de paralelismo, como el enhebrado, imponen costos adicionales a su presupuesto de ejecución. Esto se puede medir comparando el tiempo empleado en la ejecución en serie con otro en el que se permite el paralelismo explícito (hilos, descarga de GPU, etc.). El componente de hardware también es medible, solo que más difícil (puede requerir herramientas específicas).

Sabiendo esto, tienes un punto de partida. Su objetivo es minimizar [matemáticas] f (x, i) [/ matemáticas], ya sea minimizando [matemáticas] B (x, i) [/ matemáticas] y / o maximizando [matemáticas] P (x, i) [/mates].

Con frecuencia, las mayores ganancias se deben a la mejora de su algoritmo. Comprender cómo se construye el componente [matemático] B (x, i) [/ matemático] es clave, ya que describirá las áreas que necesitan más trabajo o porciones que puedan ser paralelizadas efectivamente (no todos los algoritmos son paralelizables, y no todos los algoritmos paralelizables se benefician de ello).

Tener su algoritmo en buena forma, analizar cómo interactúa con el hardware subyacente puede generar más ganancias. Hay muchas pistas a seguir, que incluyen:

  • ¿Está sufriendo demasiados errores de caché (medibles por muchas herramientas)? Las fallas provocan muchas penalizaciones y reducen el componente [matemático] P (x, i) [/ matemático]. No se pueden emitir instrucciones si no se han cumplido sus dependencias.
  • ¿Está sobreutilizando algunas unidades de CPU mientras se subutiliza otras? Un compilador optimizador hará todo lo posible para garantizar que las unidades se utilicen adecuadamente. Sin embargo, demasiados puestos (medibles) pueden sugerir una mezcla desigual de instrucciones, lo que lleva al agotamiento de la unidad.
  • ¿El predictor de rama funciona mal? La CPU podría ser engañada para especular ramas como tomadas / no tomadas, solo para descubrir que algunas instrucciones deberán descartarse y emitirse otra transmisión. Las dependencias pueden tardar mucho en resolverse, o el código reflexivo / auto modificable entra en conflicto con la forma en que funciona el predictor de rama.

Este es un largo camino, por cierto. Siempre hay algo que mejorar, una CPU más nueva para sintonizar.

Estás preguntando el clásico “¿Cómo puedo acelerar mi código?” pregunta. Aquí hay algunos básicos (y espero respuestas obvias):

1) Orientación por hardware: esto permite que un proveedor (fabricante) ajuste las funciones comunes de la biblioteca para trabajar de manera más eficiente en su hardware. Cray le quitó un montón de ventas a los CDC en los años 80 al hacer esto mismo.

2) Paralelismo: a partir de mediados de los 80, ajustando las habilidades de un compilador para comprender el potencial de paralelismo en el código para que múltiples CPU puedan ejecutar un código.

3) Optimización de bucle interno-externo y reducción de dependencias que inhiben la paralelización.

4) Reevaluar la estructura de código total para maximizar la capacidad de los compiladores para paralelizar sus códigos. (¿Acabo de mencionar esto? ¡Es **** ese **** importante!)

Un factor es qué tan bien interactúa el programa con el caché. Las personas a menudo no escriben código teniendo en cuenta las memorias caché.

Otro factor es qué tan bien el compilador puede optimizar su código. Los compiladores solo pueden optimizar el código si la optimización es inequívocamente idéntica en ejecución al programa original.

Mucho depende de su plataforma y algoritmo.

Por lo general, dejando solo el algoritmo, se puede abordar la velocidad considerando la entrada, salida, uso de memoria y el número de CPU o su velocidad.

Las utilidades de clasificación a menudo tienen parámetros de almacenamiento temporal o de almacenamiento intermedio. Las secuencias de entrada y salida pueden almacenarse o dirigirse al disco virtual en memoria en lugar de al almacenamiento físico.

A veces, el algoritmo puede mantenerse intacto, pero distribuido entre múltiples procesadores.

Si hay muchos movimientos de datos que involucran múltiples sistemas, es posible que pueda mejorar la capacidad / velocidad de las transferencias de datos con ajustes de red.

La mejora de la sobrecarga de la red puede incluir la reubicación de funciones distribuidas en un centro de datos común.

Las ayudas para el desarrollo, como los registros de seguimiento y los avisos de pasos del proceso, se pueden eliminar.

Se puede asignar más memoria física, en lugar de paginada.

En algunos casos, el tiempo para cargar el programa para ejecutar puede ser significativo porque la solución requiere muchas ejecuciones. Esto podría resolverse haciendo que el programa sea residente.

Si la solución se codificó en un idioma interpretado, puede reescribirse en un lenguaje compilado.

Algunos algoritmos pueden crear y descartar el almacenamiento variable temporal de forma iterativa, eventualmente robando los ciclos de ejecución para la recolección de basura. Los cambios de código en la declaración y asignación de variables pueden limitar este comportamiento.

More Interesting

¿Cómo funciona el algoritmo de 'forma de relleno' en los programas de dibujo?

¿Por qué es mejor usar los elementos del marco de la colección que usar una matriz de objetos?

¿Cómo se puede averiguar el número de veces que se repite una palabra en una cadena usando Java?

¿Cuáles son los beneficios del algoritmo de retropropagación frente a la estimación numérica del gradiente?

¿Con qué frecuencia los desarrolladores de iOS requieren estructuras de datos y diseño de algoritmos?

¿Cuál es el algoritmo más simple que permite a un robot descubrir e inventar?

¿Cuáles son algunos problemas prácticos en los que no se puede evitar el uso de algoritmos con big-O muy grande?

¿Aprender la construcción del compilador mejora la habilidad / visión de resolución de problemas de programación? ¿Si es así, cómo? ¿O por qué no?

¿Por qué no usar Dijkstra o Bellman-Ford para encontrar el camino más corto entre dos personas en Facebook y por qué no usar BFS bidireccional en DVR o LSR?

¿Qué es un algoritmo para darme sistemáticamente todas las combinaciones de elementos r de una matriz de elementos K?

¿Cómo es la búsqueda tan rápida por los motores de búsqueda? Generan millones de instrucciones en menos de un segundo. ¿Qué algoritmo usan?

Cómo resolver radicales anidados como [math] (a + \ sqrt b \,) ^ {1/3} [/ math]

Cómo mostrar que la distancia más corta entre 2 curvas que no se cruzan siempre se encuentra a lo largo de su normal común

Cómo encontrar la subcadena común más larga de tres o más cadenas usando una matriz de sufijos

¿Qué algoritmos usa Bing para clasificar los resultados de búsqueda? ¿La patente de Google les impide usar PageRank? Análisis de enlaces en general?