OK, estás definiendo el problema donde parece que no hay mucho margen de mejora. Eso es bastante raro, en mi experiencia. Traté de explicar esto en un artículo del Dr. Dobbs en noviembre de 1993, comenzando desde un programa no trivial convencionalmente bien diseñado sin desperdicio obvio y llevándolo a través de una serie de optimizaciones hasta que su tiempo de reloj de pared se redujo de 48 segundos a 1.1 segundos, y el tamaño del código fuente se redujo en un factor de 4. Mi herramienta de diagnóstico fue esta . La secuencia de cambios fue esta:
El primer problema encontrado fue el uso de grupos de listas (ahora llamados "iteradores" y "clases de contenedor") que representan más de la mitad del tiempo. Estos fueron reemplazados por un código bastante simple, reduciendo el tiempo a 20 segundos.
Ahora, el que más tiempo hace es construir más listas. Como porcentaje, antes no era tan grande, pero ahora se debe a que se eliminó el problema más grande. Encuentro una manera de acelerarlo, y el tiempo cae a 17 segundos.
Ahora es más difícil encontrar culpables obvios, pero hay algunos más pequeños sobre los que puedo hacer algo, y el tiempo se reduce a 13 segundos.
Ahora parece que he golpeado una pared. Las muestras me dicen exactamente lo que está haciendo, pero parece que no puedo encontrar nada que pueda mejorar. Luego reflexiono sobre el diseño básico del programa, sobre su estructura impulsada por las transacciones, y pregunto si toda la búsqueda en la lista que está haciendo es obligatoria según los requisitos del problema.
Luego me topé con un rediseño, donde el código del programa se genera realmente (a través de macros de preprocesador) a partir de un conjunto de fuentes más pequeño, y en el que el programa no está constantemente descubriendo cosas que el programador sabe que son bastante predecibles. En otras palabras, no "interprete" la secuencia de cosas que hacer, "compílela".
- Ese rediseño se realiza, reduciendo el código fuente por un factor de 4, y el tiempo se reduce a 10 segundos.
Ahora, debido a que se está volviendo tan rápido, es difícil de probar, por lo que le doy 10 veces más trabajo, pero los siguientes tiempos se basan en la carga de trabajo original.
Más diagnóstico revela que está pasando tiempo en la gestión de colas. El forro interior reduce el tiempo a 7 segundos.
Ahora una gran toma de tiempo es la impresión de diagnóstico que había estado haciendo. Enjuague eso - 4 segundos.
Ahora, los que más tiempo toman son llamadas a malloc y gratuitas . Reciclar objetos - 2.6 segundos.
Continuando con la muestra, todavía encuentro operaciones que no son estrictamente necesarias: 1.1 segundos.
Factor de aceleración total: 43,6
Ahora no hay dos programas iguales, pero en el software que no es de juguete siempre he visto una progresión como esta. Primero obtienes las cosas fáciles, y luego las más difíciles, hasta que llegas a un punto de rendimientos decrecientes. Entonces, la información que obtenga puede conducir a un rediseño, comenzando una nueva ronda de aceleraciones, hasta que vuelva a alcanzar rendimientos decrecientes. Ahora bien, este es el punto en el que podría tener sentido a preguntarse si ++i
o i++
o for(;;)
o while(1)
son más rápidos: el tipo de preguntas que veo tan a menudo en desbordamiento de pila.
PD: Me pregunto por qué no utilicé un generador de perfiles. La respuesta es que casi cada uno de estos "problemas" era un sitio de llamada de función, que apila las muestras con precisión. Los perfiladores, incluso hoy, apenas llegan a la idea de que las declaraciones y las instrucciones de llamada son más importantes de localizar y más fáciles de corregir que las funciones completas.
Realmente construí un generador de perfiles para hacer esto, pero para una verdadera intimidad con lo que está haciendo el código, no hay sustituto para meter los dedos en él. No es un problema que el número de muestras sea pequeño, porque ninguno de los problemas encontrados es tan pequeño que se pueden pasar por alto fácilmente.
AGREGADO: jerryjvl solicitó algunos ejemplos. Aquí está el primer problema. Consiste en una pequeña cantidad de líneas de código separadas, que juntas toman más de la mitad del tiempo:
/* IF ALL TASKS DONE, SEND ITC_ACKOP, AND DELETE OP */
if (ptop->current_task >= ILST_LENGTH(ptop->tasklist){
. . .
/* FOR EACH OPERATION REQUEST */
for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist, ptop)){
. . .
/* GET CURRENT TASK */
ptask = ILST_NTH(ptop->tasklist, ptop->current_task)
Estos estaban usando el ILST del grupo de listas (similar a una clase de lista). Se implementan de la manera habitual, con "ocultación de información", lo que significa que no se suponía que los usuarios de la clase tuvieran que preocuparse por cómo se implementaron. Cuando se escribieron estas líneas (de aproximadamente 800 líneas de código) no se pensó en la idea de que podrían ser un "cuello de botella" (odio esa palabra). Son simplemente la forma recomendada de hacer las cosas. Es fácil decir en retrospectiva que deberían haberse evitado, pero en mi experiencia todos los problemas de rendimiento son así. En general, es bueno tratar de evitar crear problemas de rendimiento. Es incluso mejor encontrar y arreglar los que se crean, aunque "deberían haberse evitado" (en retrospectiva).
Aquí está el segundo problema, en dos líneas separadas:
/* ADD TASK TO TASK LIST */
ILST_APPEND(ptop->tasklist, ptask)
. . .
/* ADD TRANSACTION TO TRANSACTION QUEUE */
ILST_APPEND(trnque, ptrn)
Estas son listas de construcción agregando elementos a sus extremos. (La solución fue recopilar los elementos en matrices y crear las listas de una vez.) Lo interesante es que estas declaraciones solo cuestan (es decir, estaban en la pila de llamadas) 3/48 del tiempo original, por lo que no estaban en hecho un gran problema al principio . Sin embargo, después de eliminar el primer problema, costaban 3/20 del tiempo y ahora eran un "pez más grande". En general, así es como va.
Podría agregar que este proyecto fue destilado de un proyecto real en el que ayudé. En ese proyecto, los problemas de rendimiento fueron mucho más dramáticos (al igual que las aceleraciones), como llamar a una rutina de acceso a la base de datos dentro de un bucle interno para ver si una tarea estaba terminada.
REFERENCIA AGREGADA: El código fuente, tanto original como rediseñado, se puede encontrar en www.ddj.com , para 1993, en el archivo 9311.zip, archivos slug.asc y slug.zip.
EDITAR 26/11/2011: ahora hay un proyecto de SourceForge que contiene el código fuente en Visual C ++ y una descripción detallada de cómo se ajustó. Solo pasa por la primera mitad del escenario descrito anteriormente, y no sigue exactamente la misma secuencia, pero aún así obtiene una aceleración de orden de magnitud de 2-3.
Sugerencias:
Desventajas : si realmente se usan pocos de los valores precalculados, esto puede empeorar las cosas, también la búsqueda puede requerir una memoria significativa.
Desventajas : escribir código adicional significa más área de superficie para errores.
Desventajas : Bueno ... la respuesta no será exacta.
fuente
Cuando ya no pueda mejorar el rendimiento, vea si puede mejorar el rendimiento percibido .
Es posible que no pueda hacer que su algoritmo fooCalc sea más rápido, pero a menudo hay formas de hacer que su aplicación parezca más receptiva para el usuario.
Algunos ejemplos:
Esto no hará que su programa sea más rápido, pero podría hacer que sus usuarios estén más felices con la velocidad que tiene.
fuente
Paso la mayor parte de mi vida solo en este lugar. Los trazos generales son ejecutar su generador de perfiles y hacer que registre:
__restrict
generosamente para prometerle al compilador sobre alias.Y una cosa más que me gusta hacer:
fuente
Examples on the PowerPC ...
<- Es decir, algunas implementaciones de PowerPC. PowerPC es un ISA, no una CPU.lea
.¡Lanza más hardware!
fuente
Mas sugerencias:
Evite las E / S : cualquier E / S (disco, red, puertos, etc.) siempre será mucho más lenta que cualquier código que realice cálculos, por lo tanto, elimine cualquier E / S que no necesite estrictamente.
Mover E / S por adelantado : cargue todos los datos que necesitará para un cálculo por adelantado, de modo que no haya esperas repetidas de E / S dentro del núcleo de un algoritmo crítico (y tal vez como resultado repetido disco busca, al cargar todos los datos en un solo golpe puede evitar la búsqueda).
Retraso de E / S : no escriba sus resultados hasta que termine el cálculo, guárdelos en una estructura de datos y luego bótelos de una vez al final cuando el trabajo duro haya terminado.
E / S roscada : para aquellos lo suficientemente atrevidos, combine 'E / S por adelantado' o 'Retraso de E / S' con el cálculo real moviendo la carga a un hilo paralelo, de modo que mientras carga más datos pueda trabajar en un cálculo de los datos que ya tiene, o mientras calcula el siguiente lote de datos, puede escribir simultáneamente los resultados del último lote.
fuente
mmap()
para entrada, hacermadvise()
llamadas apropiadas y usaraio_write()
para escribir grandes porciones de salida (= algunos MiB).Dado que muchos de los problemas de rendimiento involucran problemas de la base de datos, le daré algunas cosas específicas que debe tener en cuenta al ajustar consultas y procedimientos almacenados.
Evite los cursores en la mayoría de las bases de datos. Evite bucles también. La mayoría de las veces, el acceso a los datos debe basarse en el conjunto, no en el procesamiento de registros. Esto incluye no reutilizar un solo procedimiento almacenado de registros cuando desee insertar 1,000,000 de registros a la vez.
Nunca use select *, solo devuelva los campos que realmente necesita. Esto es especialmente cierto si hay uniones ya que los campos de unión se repetirán y, por lo tanto, causarán una carga innecesaria tanto en el servidor como en la red.
Evite el uso de subconsultas correlacionadas. Utilice combinaciones (incluidas combinaciones a tablas derivadas cuando sea posible) (Sé que esto es cierto para Microsoft SQL Server, pero pruebe los consejos cuando utilice un backend diferente).
Índice, índice, índice. Y obtenga esas estadísticas actualizadas si corresponde a su base de datos.
Haga la consulta sargable . Es decir, evitar cosas que hacen imposible usar los índices, como el uso de un comodín en el primer carácter de una cláusula like o una función en la unión o como la parte izquierda de una instrucción where.
Use los tipos de datos correctos. Es más rápido hacer cálculos de fechas en un campo de fecha que tener que intentar convertir un tipo de datos de cadena en un tipo de datos de fecha, luego hacer el cálculo.
¡Nunca ponga un bucle de ningún tipo en un gatillo!
La mayoría de las bases de datos tienen una forma de verificar cómo se realizará la ejecución de la consulta. En Microsoft SQL Server esto se llama plan de ejecución. Verifíquelos primero para ver dónde se encuentran las áreas problemáticas.
Considere la frecuencia con la que se ejecuta la consulta, así como el tiempo que tarda en ejecutarse al determinar qué debe optimizarse. A veces puede obtener más rendimiento de un ligero ajuste a una consulta que se ejecuta millones de veces al día de lo que puede eliminar el tiempo de una consulta de larga duración que solo se ejecuta una vez al mes.
Use algún tipo de herramienta de perfil para averiguar qué se está enviando realmente desde y hacia la base de datos. Recuerdo una vez en el pasado en la que no podíamos entender por qué la página tardaba tanto en cargarse cuando el procedimiento almacenado era rápido y descubría que la página web solicitaba la consulta muchas veces en lugar de una.
El generador de perfiles también lo ayudará a encontrar quién está bloqueando a quién. Algunas consultas que se ejecutan rápidamente mientras se ejecutan solas pueden volverse realmente lentas debido a bloqueos de otras consultas.
fuente
El factor limitante más importante hoy en día es la memoria limitada bandwitdh . Los multinúcleos solo empeoran esto, ya que el ancho de banda se comparte entre los núcleos. Además, el área limitada de chips dedicada a la implementación de cachés también se divide entre los núcleos y los hilos, lo que empeora aún más este problema. Finalmente, la señalización entre chips necesaria para mantener coherentes los diferentes cachés también aumenta con un mayor número de núcleos. Esto también agrega una penalización.
Estos son los efectos que debes gestionar. A veces a través de la micro gestión de su código, pero a veces a través de una cuidadosa consideración y refactorización.
Muchos comentarios ya mencionan el código amigable de caché. Hay al menos dos sabores distintos de esto:
El primer problema tiene que ver específicamente con hacer que sus patrones de acceso a datos sean más regulares, permitiendo que el prefetcher de hardware funcione de manera eficiente. Evite la asignación dinámica de memoria que extiende sus objetos de datos en la memoria. Use contenedores lineales en lugar de listas vinculadas, hashes y árboles.
El segundo problema tiene que ver con mejorar la reutilización de datos. Modifique sus algoritmos para trabajar en subconjuntos de sus datos que se ajusten a la memoria caché disponible, y reutilice esos datos tanto como sea posible mientras todavía está en la memoria caché.
Empacar los datos más estrictos y asegurarse de usar todos los datos en las líneas de caché en los bucles activos ayudará a evitar estos otros efectos y permitirá ajustar datos más útiles en el caché.
fuente
fuente
Aunque me gusta la respuesta de Mike Dunlavey, de hecho es una gran respuesta con un ejemplo de apoyo, creo que podría expresarse de manera muy simple:
Averigüe primero qué toma la mayor cantidad de tiempo y comprenda por qué.
Es el proceso de identificación de los cerdos del tiempo lo que le ayuda a comprender dónde debe refinar su algoritmo. Esta es la única respuesta agnóstica de lenguaje integral que puedo encontrar a un problema que ya se supone que está completamente optimizado. También suponiendo que desea ser independiente de la arquitectura en su búsqueda de velocidad.
Entonces, si bien el algoritmo puede optimizarse, su implementación puede no serlo. La identificación le permite saber qué parte es cuál: algoritmo o implementación. Por lo tanto, el que más tiempo ocupa es su principal candidato para la revisión. Pero dado que dice que desea exprimir los últimos%, es posible que también desee examinar las partes menores, las partes que no ha examinado tan de cerca al principio.
Por último, un poco de prueba y error con cifras de rendimiento sobre diferentes formas de implementar la misma solución, o algoritmos potencialmente diferentes, puede aportar información que ayude a identificar el desperdicio de tiempo y el ahorro de tiempo.
HPH, muévete en voz alta.
fuente
Probablemente debería considerar la "perspectiva de Google", es decir, determinar cómo su aplicación puede volverse paralelizada y concurrente en gran medida, lo que inevitablemente también significará en algún momento considerar la distribución de su aplicación en diferentes máquinas y redes, para que pueda escalar idealmente casi linealmente con el hardware que le arrojas.
Por otro lado, la gente de Google también es conocida por aportar una gran cantidad de mano de obra y recursos para resolver algunos de los problemas en los proyectos, herramientas e infraestructura que están utilizando, como por ejemplo la optimización de todo el programa para gcc al tener un equipo dedicado de ingenieros piratería interna de gcc para prepararlo para los escenarios de casos de uso típicos de Google.
Del mismo modo, perfilar una aplicación ya no significa simplemente perfilar el código del programa, sino también todos sus sistemas e infraestructura circundantes (redes de pensamiento, conmutadores, servidores, matrices RAID) para identificar redundancias y potencial de optimización desde el punto de vista de un sistema.
fuente
fuente
fuente
Divide y conquistaras
Si el conjunto de datos que se procesa es demasiado grande, repítalo en trozos. Si ha hecho bien su código, la implementación debería ser fácil. Si tiene un programa monolítico, ahora lo sabe mejor.
fuente
En primer lugar, como se mencionó en varias respuestas anteriores, aprenda lo que muerde su rendimiento, ya sea memoria o procesador o red o base de datos o algo más. Dependiendo de eso ...
... si es memoria, encuentre uno de los libros escritos hace mucho tiempo por Knuth, uno de la serie "El arte de la programación de computadoras". Lo más probable es que se trate de ordenar y buscar: si mi memoria está mal, tendrás que averiguar en qué habla sobre cómo lidiar con el almacenamiento lento de datos en cinta. Transforme mentalmente su par de memoria / cinta en su par de caché / memoria principal (o en un par de caché L1 / L2) respectivamente. Estudie todos los trucos que describe: si no encuentra algo que resuelva su problema, contrate a un informático profesional para que realice una investigación profesional. Si su problema de memoria es casual con FFT (la memoria caché falla en los índices de inversión de bits al hacer mariposas radix-2), entonces no contrate a un científico; en su lugar, optimice manualmente los pases uno por uno hasta que ' re ganar o llegar al callejón sin salida. Mencionasteexprimir hasta el último porcentaje correcto? Si son pocos , lo más probable es que ganes.
... si es procesador, cambie al lenguaje ensamblador. Estudie la especificación del procesador: lo que requiere ticks , VLIW, SIMD. Las llamadas a funciones son muy probablemente reemplazables. Aprenda las transformaciones de bucle: canalización, desenrollado. Las multiplicaciones y divisiones pueden ser reemplazables / interpoladas con cambios de bits (las multiplicaciones por enteros pequeños pueden ser reemplazables con adiciones). Pruebe trucos con datos más cortos: si tiene suerte, una instrucción con 64 bits podría ser reemplazable por dos en 32 o incluso 4 en 16 u 8 en 8 bits. Prueba también por más tiempodatos, por ejemplo, sus cálculos flotantes pueden ser más lentos que los dobles en un procesador particular. Si tienes cosas trigonométricas, lucha con tablas precalculadas; También tenga en cuenta que el seno de valor pequeño podría reemplazarse con ese valor si la pérdida de precisión se encuentra dentro de los límites permitidos.
... si es una red, piense en comprimir los datos que pasa por encima. Reemplazar transferencia XML con binario. Protocolos de estudio. Pruebe UDP en lugar de TCP si de alguna manera puede manejar la pérdida de datos.
... si se trata de una base de datos, vaya a cualquier foro de base de datos y pida consejo Cuadrícula de datos en memoria, optimización del plan de consulta, etc., etc.
HTH :)
fuente
Almacenamiento en caché! Una forma económica (en esfuerzo del programador) de hacer casi cualquier cosa más rápido es agregar una capa de abstracción de almacenamiento en caché a cualquier área de movimiento de datos de su programa. Ya sea E / S o simplemente pasar / crear objetos o estructuras. A menudo es fácil agregar cachés a clases de fábrica y lectores / escritores.
A veces, el caché no te dará mucho, pero es un método fácil para agregar el almacenamiento en caché por todas partes y luego deshabilitarlo donde no ayuda. A menudo he encontrado que esto gana un gran rendimiento sin tener que microanalizar el código.
fuente
Creo que esto ya se ha dicho de una manera diferente. Pero cuando se trata de un algoritmo de procesador intensivo, debe simplificar todo dentro del bucle más interno a expensas de todo lo demás.
Eso puede parecer obvio para algunos, pero es algo en lo que trato de enfocarme independientemente del idioma con el que estoy trabajando. Si se trata de bucles anidados, por ejemplo, y encuentra la oportunidad de reducir el nivel de un código, en algunos casos puede acelerar drásticamente su código. Como otro ejemplo, hay algunas pequeñas cosas en las que pensar, como trabajar con números enteros en lugar de variables de coma flotante siempre que puede, y usar la multiplicación en lugar de la división siempre que puede. Nuevamente, estas son cosas que deben considerarse para su ciclo más interno.
A veces puede encontrar el beneficio de realizar sus operaciones matemáticas en un número entero dentro del bucle interno, y luego reducirlo a una variable de punto flotante con la que pueda trabajar después. Ese es un ejemplo de sacrificar la velocidad en una sección para mejorar la velocidad en otra, pero en algunos casos la recompensa puede valer la pena.
fuente
Pasé algún tiempo trabajando en la optimización de los sistemas empresariales de cliente / servidor que operan en redes de bajo ancho de banda y larga latencia (por ejemplo, satélite, remoto, en alta mar), y pude lograr algunas mejoras dramáticas de rendimiento con un proceso bastante repetible.
Medida : Comience por comprender la capacidad y la topología subyacentes de la red. Hablar con las personas de redes relevantes en el negocio y utilizar herramientas básicas como ping y traceroute para establecer (como mínimo) la latencia de la red desde la ubicación de cada cliente, durante los períodos operativos típicos. Luego, tome medidas precisas de tiempo de funciones específicas del usuario final que muestren los síntomas problemáticos. Registre todas estas medidas, junto con sus ubicaciones, fechas y horas. Considere incorporar la funcionalidad de "prueba de rendimiento de red" del usuario final en su aplicación cliente, permitiendo que sus usuarios avanzados participen en el proceso de mejora; empoderarlos de esta manera puede tener un gran impacto psicológico cuando se trata de usuarios frustrados por un sistema de bajo rendimiento.
Analizar : utilizando cualquiera y todos los métodos de registro disponibles para establecer exactamente qué datos se transmiten y reciben durante la ejecución de las operaciones afectadas. Idealmente, su aplicación puede capturar datos transmitidos y recibidos tanto por el cliente como por el servidor. Si estos incluyen marcas de tiempo también, incluso mejor. Si no hay suficiente registro disponible (p. Ej., Sistema cerrado o incapacidad para implementar modificaciones en un entorno de producción), use un sniffer de red y asegúrese de comprender realmente lo que está sucediendo a nivel de red.
Caché : busque casos en los que los datos estáticos o modificados con poca frecuencia se transmitan de forma repetitiva y considere una estrategia de almacenamiento en caché adecuada. Los ejemplos típicos incluyen valores de "lista de selección" u otras "entidades de referencia", que pueden ser sorprendentemente grandes en algunas aplicaciones comerciales. En muchos casos, los usuarios pueden aceptar que deben reiniciar o actualizar la aplicación para actualizar los datos que se actualizan con poca frecuencia, especialmente si puede ahorrar un tiempo considerable desde la visualización de los elementos de la interfaz de usuario de uso común. Asegúrese de comprender el comportamiento real de los elementos de almacenamiento en caché ya implementados: muchos métodos de almacenamiento en caché comunes (por ejemplo, HTTP ETag) todavía requieren un viaje de ida y vuelta de red para garantizar la coherencia, y donde la latencia de red es costosa, puede evitarlo por completo con Un enfoque diferente de almacenamiento en caché.
Paralelo : busque transacciones secuenciales que lógicamente no se deban emitir de forma estricta y vuelva a trabajar el sistema para emitirlas en paralelo. Me ocupé de un caso en el que una solicitud de extremo a extremo tenía un retraso de red inherente de ~ 2 segundos, que no era un problema para una sola transacción, pero cuando se requerían 6 viajes de ida y vuelta secuenciales de 2 segundos antes de que el usuario recuperara el control de la aplicación del cliente , se convirtió en una gran fuente de frustración. Descubrir que estas transacciones eran de hecho independientes les permitió ejecutarse en paralelo, reduciendo el retraso del usuario final a muy cerca del costo de un solo viaje de ida y vuelta.
Combinar : cuando las solicitudes secuenciales deben ejecutarse secuencialmente, busque oportunidades para combinarlas en una sola solicitud más completa. Los ejemplos típicos incluyen la creación de nuevas entidades, seguido de solicitudes para relacionar esas entidades con otras entidades existentes.
Comprimir : busque oportunidades para aprovechar la compresión de la carga útil, ya sea reemplazando una forma textual por una binaria, o utilizando la tecnología de compresión real. Muchas pilas de tecnología moderna (es decir, dentro de una década) admiten esto de forma casi transparente, así que asegúrese de que esté configurado. A menudo me ha sorprendido el impacto significativo de la compresión, donde parecía claro que el problema era fundamentalmente la latencia en lugar del ancho de banda, descubriendo después de que permitía que la transacción encajara en un solo paquete o, de lo contrario, evitara la pérdida de paquetes y, por lo tanto, tuviera un tamaño excesivo impacto en el rendimiento.
Repita : regrese al principio y vuelva a medir sus operaciones (en los mismos lugares y horarios) con las mejoras implementadas, registre e informe sus resultados. Como con toda optimización, algunos problemas pueden haberse resuelto exponiendo otros que ahora dominan.
En los pasos anteriores, me concentro en el proceso de optimización relacionado con la aplicación, pero, por supuesto, debe asegurarse de que la red subyacente esté configurada de la manera más eficiente para admitir su aplicación también. Involucre a los especialistas en redes en el negocio y determine si pueden aplicar mejoras de capacidad, QoS, compresión de red u otras técnicas para abordar el problema. Por lo general, no entenderán las necesidades de su aplicación, por lo que es importante que esté equipado (después del paso Analizar) para discutirlo con ellos, y también para exponer el caso comercial de los costos en los que les va a pedir que incurran. . Encontré casos en los que la configuración de red errónea provocó que los datos de las aplicaciones se transmitieran a través de un enlace de satélite lento en lugar de un enlace terrestre, simplemente porque estaba usando un puerto TCP que no era "bien conocido" por los especialistas en redes; Obviamente, la rectificación de un problema como este puede tener un impacto dramático en el rendimiento, sin necesidad de ningún código de software o cambios de configuración necesarios.
fuente
Muy difícil dar una respuesta genérica a esta pregunta. Realmente depende del dominio del problema y la implementación técnica. Una técnica general que es bastante neutral en cuanto al lenguaje: identifique puntos de acceso de código que no se pueden eliminar y optimice manualmente el código del ensamblador.
fuente
El último% es algo muy dependiente de la CPU y la aplicación ...
La lista continúa ... Pero este tipo de cosas realmente son el último recurso ...
Compile para x86 y ejecute Valgrind / Cachegrind contra el código para obtener un perfil de rendimiento adecuado. O CCStudio de Texas Instruments tiene un dulce perfilador. Entonces realmente sabrás dónde concentrarte ...
fuente
Did you know that a CAT6 cable is capable of 10x better shielding off extrenal inteferences than a default Cat5e UTP cable?
Para cualquier proyecto que no esté fuera de línea, mientras tenga el mejor software y el mejor hardware, si su rendimiento total es débil, esa delgada línea va a comprimir los datos y le dará demoras, aunque sea en milisegundos ... pero si está hablando de las últimas caídas , eso son algunas gotas obtenidas, 24/7 para cualquier paquete enviado o recibido.
fuente
No es tan profundo o complejo como las respuestas anteriores, pero aquí va: (estos son más nivel principiante / intermedio)
fuente
Imposible decirlo. Depende de cómo se vea el código. Si podemos suponer que el código ya existe, entonces podemos simplemente mirarlo y descubrir a partir de eso, cómo optimizarlo.
Mejor localidad de caché, desenrollamiento de bucle. Intente eliminar cadenas de dependencia largas para obtener un mejor paralelismo a nivel de instrucción. Prefiere movimientos condicionales sobre ramas cuando sea posible. Explote las instrucciones SIMD cuando sea posible.
Comprenda lo que hace su código y comprenda el hardware en el que se ejecuta. Luego, se vuelve bastante simple determinar lo que debe hacer para mejorar el rendimiento de su código. Ese es realmente el único consejo realmente general que se me ocurre.
Bueno, eso y "Mostrar el código en SO y pedir consejos de optimización para ese fragmento de código específico".
fuente
Si un mejor hardware es una opción, entonces definitivamente ve por eso. De otra manera
fuente
La forma de google es una opción "Cachéarla ... Siempre que sea posible no toque el disco"
fuente
Aquí hay algunas técnicas de optimización rápidas y sucias que uso. Considero que esta es una optimización de 'primer paso'.
Aprenda dónde se gasta el tiempo. Descubra exactamente lo que le toma el tiempo. ¿Es el archivo IO? ¿Es tiempo de CPU? ¿Es la red? ¿Es la base de datos? Es inútil optimizar para IO si ese no es el cuello de botella.
Conozca su entorno Saber dónde optimizar normalmente depende del entorno de desarrollo. En VB6, por ejemplo, pasar por referencia es más lento que pasar por valor, pero en C y C ++, por referencia es mucho más rápido. En C, es razonable probar algo y hacer algo diferente si un código de retorno indica una falla, mientras que en Dot Net, la captura de excepciones es mucho más lenta que la verificación de una condición válida antes de intentarlo.
Índices Cree índices en los campos de base de datos consultados con frecuencia. Casi siempre puedes intercambiar espacio por velocidad.
Evite búsquedas Dentro del ciclo para ser optimizado, evito tener que hacer búsquedas. Encuentre el desplazamiento y / o índice fuera del bucle y reutilice los datos dentro.
Minimice IO intente diseñar de una manera que reduzca la cantidad de veces que tiene que leer o escribir, especialmente a través de una conexión en red
Reduzca las abstracciones Cuantas más capas de abstracción tenga que procesar el código, más lento será. Dentro del ciclo crítico, reduzca las abstracciones (por ejemplo, revele métodos de nivel inferior que eviten el código adicional)
Generar subprocesos para proyectos con una interfaz de usuario, generar un nuevo subproceso para realizar tareas más lentas hace que la aplicación se sienta más receptiva, aunque no lo es.
Preproceso Generalmente puede intercambiar espacio por velocidad. Si hay cálculos u otras operaciones intensas, vea si puede calcular previamente parte de la información antes de estar en el ciclo crítico.
fuente
Si tiene muchas matemáticas de punto flotante altamente paralelas, especialmente de precisión simple, intente descargarlo en un procesador de gráficos (si está presente) usando OpenCL o (para chips NVidia) CUDA. Las GPU tienen una inmensa potencia informática de punto flotante en sus sombreadores, que es mucho mayor que la de una CPU.
fuente
Agregando esta respuesta ya que no la vi incluida en todas las demás.
Minimice la conversión implícita entre tipos y signos:
Esto se aplica al menos a C / C ++, incluso si ya crees que estás libre de conversiones, a veces es bueno probar agregar advertencias del compilador en torno a funciones que requieren rendimiento, especialmente cuidado con las conversiones dentro de los bucles.
Específico de GCC: puede probar esto agregando algunos pragmas detallados alrededor de su código,
He visto casos en los que puede obtener una aceleración porcentual al reducir las conversiones generadas por advertencias como esta.
En algunos casos, tengo un encabezado con advertencias estrictas que mantengo incluidas para evitar conversiones accidentales, sin embargo, esto es una compensación, ya que puede terminar agregando una gran cantidad de conversiones a conversiones intencionales silenciosas que pueden hacer que el código esté más desordenado por un mínimo ganancias.
fuente
A veces, cambiar el diseño de sus datos puede ayudar. En C, puede cambiar de una matriz o estructuras a una estructura de matrices, o viceversa.
fuente
Ajustar el sistema operativo y el marco.
Puede parecer una exageración, pero piense así: los sistemas operativos y los marcos están diseñados para hacer muchas cosas. Su aplicación solo hace cosas muy específicas. Si pudiera hacer que el sistema operativo hiciera exactamente lo que su aplicación necesita y que su aplicación entienda cómo funciona el marco (php, .net, java), podría obtener mucho mejor de su hardware.
Facebook, por ejemplo, cambió algunas cosas a nivel de kernel en Linux, cambió cómo funciona memcached (por ejemplo, escribieron un proxy memcached y usaron udp en lugar de tcp ).
Otro ejemplo de esto es Window2008. Win2K8 tiene una versión donde puede instalar solo el sistema operativo básico necesario para ejecutar aplicaciones X (por ejemplo, aplicaciones web, aplicaciones de servidor). Esto reduce gran parte de la sobrecarga que el sistema operativo tiene en los procesos en ejecución y le brinda un mejor rendimiento.
Por supuesto, siempre debe agregar más hardware como primer paso ...
fuente