¿Cuál es el propósito de estudiar pequeñas mejoras (como usar dos hilos o evitar la basura) mientras puedo reducir la complejidad de los algoritmos?

Asumiré que está aprendiendo técnicas introductorias, ya que mencionó pequeñas mejoras.

Los algoritmos son realmente extremadamente importantes, así que felicitaciones por estar tan motivado. También es cierto que, para algunos problemas, uno logra una solución óptima (incluso si es un mínimo / máximo local), para reducir el tiempo de ejecución, necesita realizar más trabajo por unidad de tiempo. Una forma de lograr esto es la paralelización .

Si implementa un rastreador de rayos, se dará cuenta rápidamente de que puede lograr aceleraciones algo proporcionales al número de unidades informáticas que puede usar (núcleos de CPU o unidades de GPU). Esto significa que un rastreador de rayos paralelo será aproximadamente N veces más rápido que uno en serie, con un algoritmo de rastreo equivalente.

Esto requiere otro algoritmo: coordinación de trabajo. Cuando está bien diseñado, puede obtener aceleraciones superlineales (algo difíciles), si sus unidades de ejecución también son capaces de explotar el paralelismo subyacente (la mayoría de las CPU son superescalares, lo que significa que pueden ejecutar múltiples instrucciones en paralelo en un mismo núcleo).

En otras palabras, una vez que recogió todas las frutas bajas, aumentar el rendimiento requiere una comprensión más profunda de las cosas que afectan la ejecución de su algoritmo, como la arquitectura del procesador, el bus del sistema (memoria y ancho de banda del dispositivo), y la entrada para su algoritmo.

Piénsalo de esta manera:

Tienes un producto que habla con los clientes, para darles resultados.
Su base de datos y su código y su servidor están todos en el mismo cuadro, y la aplicación funciona con un solo subproceso, confiando en la memoria compartida para mejorar la velocidad.

Tu aplicación se vuelve popular.
Su base de usuarios se duplica.
Pero ahora estás golpeando un muro de rendimiento. Las conexiones que entran están empantanando la caja, debido a sus búsquedas de base de datos, y debido a sus algoritmos con los resultados finales en el otro lado.
Te conduces a la tierra, escribiendo a mano tus consultas SQL para tu versión particular de tu motor de base de datos particular.
Te rompes, confiando en más caché en memoria, bucles de desenrollado manual y más algoritmos hardcore.

A pesar de todo eso, su tiempo de ejecución es 100% más rápido de lo que era antes, un gran logro.

Entonces su producto y su velocidad de entrega se mencionan en Reddit.

¿Qué haces cuando tu base de usuarios aumenta por decenas de miles de personas?
¿Puedes mejorar tanto tus algoritmos?
Por supuesto que no, a menos que fueran terribles para empezar.

Entonces tratas de multiprocesarlo …
Todo el almacenamiento en caché en memoria y el acceso a recursos en memoria ahora está siendo pisoteado por hilos competidores.
Incluso después de solucionarlo (una tarea repugnante que es ridícula depurar de cualquier manera comprobable), su servidor está teniendo dificultades para generar suficientes hilos para servir a todos los consumidores.
Entonces imaginas que sacaste otra caja, con la misma configuración y eres dorado.

… pero recuerda que su producto es llamar y responder, y toda la comunicación que un usuario tiene debe estar con la misma caja, para siempre, porque la base de datos (y sus datos) son parte de esa máquina.

Pones una fachada y escribes un servicio para acceder a la base de datos a través de la red, reemplazando cada línea de acceso a la base de datos en toda tu aplicación con este servicio en red (y ahora pagando 40 ms adicionales por consulta) para que los usuarios ya no estén bloqueados en un cuadro por sus datos.

Sin embargo, los usuarios todavía están bloqueados en un cuadro para su sesión. El equilibrio de carga sigue siendo totalmente impredecible, porque los servidores deben ser equilibrados por usuario, no por solicitud, y la carga generada por usuario es mucho menos predecible que la carga generada por solicitud.

Mientras tanto, en otro universo, alguien escribe un producto similar de arriba hacia abajo.
Lo construyen utilizando mucha más memoria inicial, pero sus procesos son puros (las variables se definen pero nunca cambian, etc.).
Su aplicación es segura para subprocesos.
Tienen un servicio frente a su capa de base de datos para comenzar.
Todos sus algoritmos son bastante modestos, pero ninguno de los clientes sabe realmente la diferencia entre algo que tarda 0.4 segundos en regresar y 0.6 segundos.

Su tráfico se duplica y no se dan cuenta, porque sus modestos algoritmos ahora se ejecutan en paralelo para diferentes usuarios en diferentes subprocesos.
Los usuarios tampoco notan el aumento de carga.
Reciben una mención de Digg, y su base de usuarios se dispara.
Por lo tanto, mueven el DB a la nube, configuran un clúster en la nube y el equilibrador realiza las solicitudes.

En el futuro, pueden paralelizar aún más sus algoritmos, ya que son simples y puros, o pueden hacer llamadas de red a una granja de servidores altamente paralelos, para dicho cálculo de números.

Es importante porque mejorar un tiempo de ejecución ayuda a una persona.
Eso no ayuda mucho cuando tienes un millón de personas.
No hasta que ese sea el nivel más alto de optimización que le queda, de todos modos.

Si está escribiendo controladores de mouse, no dude en ignorar este ejemplo.

Por lo general, un programa mejor y más simple puede llevar el rendimiento de la aplicación a otro nivel. Pero, en algunos casos, cuando enfrenta una cantidad increíble de procesamiento y una gran cantidad de datos, las pequeñas mejoras hacen una gran diferencia.

Siempre es bueno saber acerca de todo esto, ya que no puede predecir cuándo una de estas mejoras se convierte en una necesidad.