Resumen de 2021 Huawei Software Elite Challenge


Prefacio

Inserte la descripción de la imagen aquí

Con el final de la ronda preliminar de la competencia, nuestro viaje llegó a su fin. Aunque no llegamos a las semifinales, también logramos el resultado 52 en la División Hangzhou-Xiamen (subimos tres lugares después de verificar los duplicados ), que también se considera entre los primeros 64 en la competencia regional. Aunque es un poco lamentable, no es tan malo como estudiante de segundo año participar en este tipo de competencia por primera vez.
Inserte la descripción de la imagen aquí

Aprendí mucho en esta competencia, tanto en términos de codificación como de forma de pensar, todo ha mejorado enormemente. Al mismo tiempo, también me di cuenta de mis defectos y de la brecha entre esos grandes y yo. En resumen, tengo muchas reflexiones, por lo que este artículo puede considerarse como una revisión y resumen de la experiencia de este juego.

1. Preguntas sobre competencia

La pregunta para esta competencia es la asignación y programación de recursos del servidor en el contexto de la computación en nube. Para obtener más información, consulte el sitio web oficial.También pondré los archivos del concurso en mi casa junto con mi código, como referencia para los recién llegados.

2. Revisión de la competencia

Aquí, principalmente comparto mi proceso de juego. (La idea de la pregunta se discutirá en el proceso de pensamiento)

1. Forme un equipo

Antes de que se publicara la pregunta, había un médico de la Universidad de Zhejiang que estaba buscando una compañera de equipo en el gran grupo de competencia, y yo solo cumplí con sus requisitos (principalmente porque no era buena escribiendo código), así que conversé con ella en privado y más tarde se contaba como un equipo. El acuerdo original Después de que salieron las preguntas de la competencia, discutimos y analizamos juntas, pero cada vez que le enviaba un mensaje, no respondía mucho, diciendo que estaba demasiado ocupado (tal vez estaba demasiado ocupada), y finalmente decidió no participar en la competencia una semana después.

Afortunadamente, no esperé este período de tiempo, escribí mi propio análisis e ideas de la competencia.
Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí

Luego pregunté si faltaban compañeros en el grupo de competición y poco después alguien me envió una invitación, que es mi compañero actual.
Inserte la descripción de la imagen aquí

2. Reunión para discutir

Cuando se formó el equipo, había pasado una semana y solo faltaba un poco más de una semana para el partido oficial. La noche de la formación del equipo, celebramos una reunión para discutir brevemente la siguiente pregunta. Pero debido a que los idiomas que usan cada uno son diferentes, y los otros dos compañeros y yo no somos de la misma escuela (los otros dos compañeros se conocen), no es conveniente comunicarse entre sí, así que finalmente decidí hacerlo. escribir nuestro propio código.

Por supuesto, hubo varias reuniones seguidas en los próximos días, así que no hablaré de eso aquí.

3. Iteración de modificación de código, corrección de errores

Programe de acuerdo con la idea del código (explicaré la idea de la pregunta de la competencia en el proceso de pensamiento). Después de todo el arduo trabajo, finalmente escribí una versión de la línea de base que se puede enviar y luego optimiza continuamente sobre la base Por supuesto, habrá varias cosas en este proceso: varios errores y varias dificultades desconocidas.

4. La lucha de los últimos días

Para ser honesto, si no tiene compañeros de equipo, probablemente no podrá persistir en los últimos días, porque constantemente hay un rápido ascenso en la tabla de clasificación. En este momento, estará bajo una gran presión, especialmente cuando su El código encuentra una variedad de Este sentimiento es especialmente obvio cuando el error está perdido. Estarás muy ansioso en esta etapa, y yo también, realmente casi me rindo muchas veces. (De hecho, muchos equipos se rindieron a la mitad)

En una semana de optimización de alta intensidad, los últimos días estarán muy agotados. Esto es muy obvio para mí el último día, porque estamos a solo 20 millones de los 32, pero nuestros planes están frustrados y queremos entrar en 32. Fue muy incómodo no volver a entrar. En ese momento, tenía muchas ganas de rendirme.

Tres, el proceso de pensamiento

Aquí registro aproximadamente el proceso de pensamiento en ese momento.

1. Pensamiento preliminar

Al comienzo del análisis del problema de la competencia, lo consideré como un problema de mochila y quería usar programación dinámica para resolverlo. Sin embargo, después de considerarlo cuidadosamente, descubrí que esto es diferente del problema habitual de la mochila 01. Es multidimensional y tiene varias restricciones complicadas. Después de consultar la información relevante, la solución de programación dinámica finalmente fue rechazada.
Porque el estado óptimo de cada etapa no se puede obtener directamente de uno o algunos estados de una etapa anterior.
Para obtener más información, consulte la explicación popular de programación dinámica (DP) de este blog .

Después de negar la planificación dinámica, decidí cambiar mi perspectiva, no desde la perspectiva de las máquinas virtuales para elegir qué servidor poner, sino desde el punto de vista del servidor, para pensar en qué máquina virtual poner.

Según la pregunta, la dividí en tres pasos: compra, migración e implementación.

Y cada paso solo necesita hacer lo siguiente:
1. Comprar: gastar lo menos posible cuando se cumpla con la solicitud del día
2. Migración: El propósito es integrar los recursos tanto como sea posible para que el servidor sea gratuito, y en el Al mismo tiempo, se puede instalar mejor El próximo servidor
3. Implementación: En la medida de lo posible, para implementar la solicitud de la máquina virtual del día, use los recursos existentes tanto como sea posible

¿Cómo puede hacerse esto?
Mi idea inicial es migrar e integrar recursos primero, y luego usar los recursos actuales tanto como sea posible para la implementación inicial. Cuando hay una solicitud de una máquina virtual que no se puede acomodar, compraré un servidor y luego implementaré el nuevo servidor comprado.

Al mismo tiempo, clasifico las solicitudes de mayor a menor (primero los nodos duales, primero los recursos), para llenar el servidor tanto como sea posible.

2. La primera versión del código

Con una idea general, decidí escribir primero una versión del código de acuerdo con la idea básica. Por supuesto, este proceso encontró muchas dificultades y encontré muchos errores, pero al final escribí una versión. Este proceso tomó alrededor de dos días. Aunque salió la primera versión (en realidad no se pudo enviar debido a varias razones, como problemas con el pedido de solicitud, problemas con el formato de salida), los resultados de las pruebas locales no fueron ideales.

3. Mejora de la idea

① Despliegue equilibrado y despliegue desequilibrado

En la primera versión del código, utilicé datos a pequeña escala para la depuración y el análisis, y encontré un problema, es decir, la utilización de recursos del servidor está muy desequilibrada y algunos núcleos de máquinas virtuales son incluso más de 100 o menos de 0.01 ( esto es puramente repugnante), en realidad, ¿cómo puede haber un servidor con 1000 núcleos y poca memoria?
Inserte la descripción de la imagen aquí

Como resultado, al implementar máquinas virtuales, los recursos del servidor a menudo se desperdician debido a estas máquinas virtuales extremas.

Para resolver este problema, pensé en una solución, que es una implementación equilibrada.
La llamada implementación equilibrada es una estrategia de mejora para la situación anterior. Consiste en realizar una verificación de equilibrio antes de la implementación. Si esta máquina virtual se inserta en este servidor, hará que la proporción de memoria del núcleo del servidor sea demasiado alta o demasiado baja. (es decir, desequilibrio), luego rechace esta inserción.

El estándar de desequilibrio específico es el siguiente: si los recursos restantes después de la inserción son menores que un cierto valor, entonces no hay necesidad de juzgar la relación de memoria del núcleo (porque no tiene sentido) y pasar directamente; de ​​lo contrario, el juicio de relación de memoria del núcleo es realizado, si los recursos restantes son mayores que el índice de memoria del kernel Si es menor o mayor que un cierto valor, es decir, hay un desequilibrio (como se muestra en la figura anterior), entonces la inserción se rechaza.

Al mismo tiempo, después de la implementación equilibrada, es decir, después de que la solicitud del día intente equilibrar la implementación en este servidor, realice una implementación desequilibrada (detección desequilibrada), para asegurarse de que la máquina virtual adecuada se inserta en el servidor apropiado en la mayor medida posible, pero también utilice los recursos tanto como sea posible.

Después de esta mejora, la tasa de utilización de recursos se ha mejorado significativamente. Algunos servidores (generalmente núcleos de servidor, la memoria ronda los 500), los recursos restantes llegan incluso a 1 o 2.
Inserte la descripción de la imagen aquí


②Actualización dinámica de la estrategia

Pero al mismo tiempo, también encontré un problema. Después de la depuración, descubrí que la tasa de utilización de los primeros servidores era muy alta (como se muestra en la figura anterior), pero la tasa de utilización de los siguientes servidores mostraba un abismo -como declive.
Inserte la descripción de la imagen aquí

Razón: La conjetura es que las solicitudes de la máquina virtual no se distribuyen de manera uniforme todos los días y la brecha de solicitudes (kernel / memoria) del día es demasiado grande.

Este es un problema a la hora de comprar un servidor, ¿qué debo hacer?

Pensé en un método (y una de mis ideas centrales), que es actualizar la estrategia de forma dinámica.
La llamada actualización dinámica de la estrategia significa que ajusto dinámicamente la estrategia de compra y la estrategia de implementación de acuerdo con las solicitudes diarias, que se materializa en la actualización del factor de saldo y el límite de saldo.

Método específico: hay un método updateStrategy en mi programa, lo que significa actualizar el estado. Lo que hace es contar el núcleo promedio y la memoria promedio de las solicitudes restantes en el día, actualizar el factor de saldo y el límite de saldo según su valor. y luego ajustar la estrategia de compra y equilibrar la estrategia de implementación. El factor de equilibrio es la relación entre el núcleo promedio y la memoria, y el límite de equilibrio es su suma y multiplicación por un coeficiente.
Inserte la descripción de la imagen aquí

Al mismo tiempo, para mejorar la utilización de los recursos, cambié la opción de comprar un servidor para seleccionar el servidor con la proporción de memoria del kernel más cercana en función de las solicitudes restantes actuales.

Después de esta mejora, la tasa de utilización del servidor ha mejorado considerablemente. Excepto por el último o los dos últimos servidores, la tasa de utilización de otros servidores es muy alta. Los recursos restantes son generalmente entre 10-20, y algunos incluso solo 1, 2 recursos .

Después de algunas mejoras (por supuesto que hay muchas mejoras en los detalles, no las repetiré aquí), nuestra versión sin migración corrió a 1,19 mil millones en la etapa de práctica y 1,53 mil millones en la competencia oficial.
Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí


③Optimización de la migración

En cuanto a la migración, basándome en las ideas anteriores, formulé rápidamente una estrategia de migración, es decir, primero migrar las máquinas virtuales que antes estaban desbalanceadas en la implementación con éxito, y tratar de equilibrar la implementación en otros servidores (por supuesto agregué algunos juicios, como como la implementación no balanceada previamente registrada). Si el servidor ya está balanceado, no es necesario migrarlo, de modo que el servidor se pueda reequilibrar y el servidor se pueda cargar con más máquinas virtuales.

Si hay más tiempos de migración, realice todas las migraciones, es decir, migre de un servidor con menos máquinas virtuales a un servidor con más máquinas virtuales, esto no solo consolidará recursos, pondrá más máquinas virtuales, sino que también ahorrará consumos energéticos innecesarios.

Sin embargo, esta migración tendrá un problema obvio: la complejidad del tiempo es demasiado alta, cada migración tarda aproximadamente 0,6 s, si el conjunto de datos del partido de práctica está a punto de ejecutarse más de 500 s, esto es fatal para el límite de los 90.

Así que optimicé el código, principalmente en los siguientes puntos:
1. Optimización del bucle externo: salida oportuna u omisión de bucles innecesarios y poda para algunas situaciones
2. Optimización de la estructura de datos: utilice una estructura de datos especial, Trate de reducir el consumo de memoria y tiempo
3 .Optimización de la operación interna: optimice las operaciones internas, como las operaciones de implementación, juzgue y devuelva directamente algunas situaciones innecesarias
4. Optimización de los detalles del código: como declaraciones de variables fuera del ciclo, etc. (por supuesto, esto es lo que lo he optimizado durante mucho tiempo atrás)

Después de la optimización anterior, el tiempo de ejecución de mi código se optimiza directamente a alrededor de 20. Esta es una optimización asombrosa. Nunca pensé que mi código podría optimizarse tanto antes.

Sin embargo, debido al aumento significativo en los conjuntos de datos en línea, que se ejecutan localmente durante más de 20 segundos y aún se agota el tiempo de espera en línea, solo puedo reducir el número de operaciones de migración, desde activar todas las migraciones todos los días hasta activar todas las operaciones de migración cuando la solicitud de eliminación es mayor que la solicitud de aumento.

Al final, nuestro puntaje llegó a 1,49 mil millones. Después del ajuste, el resultado finalmente llegó a 1,48 mil millones, y este fue nuestro mejor resultado.
Inserte la descripción de la imagen aquí

④ El último intento de optimización

En este momento, solo faltan dos días para el final de la competencia oficial. Luego de varias optimizaciones, encontramos que es difícil para nosotros reducir costos en las operaciones de migración y despliegue (las operaciones de migración se deben principalmente al tiempo de ejecución).

Así que dirigí mi atención a la operación de compra, porque mi operación de compra se seleccionó en función de la relación de memoria del kernel solicitada actual sin considerar el costo. Aunque la tasa de utilización era alta, el costo no podía reducirse, por lo que quise comprar el costo no se puede tomar en consideración (escribí una versión que consideraba la rentabilidad, pero el resultado no fue ideal y la tasa de utilización fue muy baja).

Manejo especial para circunstancias especiales

Una noche antes del final del juego, nos comunicamos con un tipo grande. Dijo que mi solución sería mejor para máquinas virtuales de servidor más equilibradas, pero que sufrió mucho para algunas máquinas virtuales y servidores más extremos. La sugerencia es sacar este tipo de solicitud de máquina virtual para procesamiento adicional y usar el servidor correspondiente para la implementación.

De hecho, mi plan es elegir el servidor de acuerdo con la proporción de memoria del kernel de la máquina virtual actual tanto como sea posible, pero el problema es que después de contar las solicitudes restantes de la máquina virtual actual, la proporción de memoria del kernel será cercana a 1 (aproximadamente 0.8- 1.2). Incluso si hay máquinas virtuales con ratios exagerados, estas características son neutralizadas por otras máquinas virtuales extremas o diluidas por máquinas virtuales con un ratio relativamente equilibrado. Es difícil que el programa reconozca estas características, por lo que el programa es generalmente Will compre el tipo de servidor con una proporción de memoria central cercana a 1, y este tipo de servidor a menudo no es rentable.
Inserte la descripción de la imagen aquí

Solo cuando las solicitudes de implementación continúen disminuyendo y las características de las solicitudes restantes continúen destacadas, el programa comprará el tipo de servidor con una relación de memoria central más grande, por lo que mi costo de compra no podrá disminuir.

El procesamiento por separado es de hecho un buen método. No significa que haya mucho desperdicio de recursos (porque mi utilización de recursos no es baja), pero si se compara la memoria del kernel con el servidor más exagerado para instalar la memoria del kernel que la misma Máquina virtual exagerada, por lo que el costo será menor.

Así que temprano en la mañana del día siguiente comencé a cambiar el código, pero gradualmente descubrí que si quiero cambiarlo, una mañana y una tarde no es suficiente (porque esta idea implicará el cambio de mi estrategia de implementación, y mi La estrategia de implementación es. Los otros dos pasos están estrechamente relacionados y son el núcleo de todo el código. Es más difícil cambiar y depurar errores. En ese momento, no sabía si este cambio reduciría el costo o no. Cambié Todo la mañana y finalmente decidió Renunciar a este cambio de pensamiento.

Superposición para seleccionar la solución óptima

Dado que nuestro cuello de botella es que no consideramos el costo cuando compramos, traté de incorporar el costo. Entonces se me ocurrió otro plan, el plan específico es el siguiente:
superpongo las solicitudes en orden, y después de cada superposición, buscaré el servidor con el menor costo (costo + consumo de energía * días restantes) que pueda instalar el máquina virtual superpuesta. Escriba, registre la diferencia entre el recurso actual y el recurso del servidor seleccionado actualmente, y luego continúe superponiendo hasta que no pueda encontrar un servidor que pueda contener estas solicitudes. En este proceso, el tipo de servidor con el recurso más pequeño Se deja diferencia durante el proceso de superposición., Y este servidor es la solución óptima en este caso.
Inserte la descripción de la imagen aquí

Sin embargo, el costo de esta estrategia es similar al costo de mi estrategia anterior. Por supuesto, esto no significa que esta solución no sea buena. Tiene mejoras. Por ejemplo, la diferencia mínima en recursos no es el estándar para los mejores tipo de servidor. La estrategia de juicio se puede cambiar; por ejemplo, no es necesario superponer las máquinas virtuales en orden, etc., pero no quedaba mucho tiempo en ese momento, así que tuve que rendirme. Pero creo que este esquema es bastante inteligente.

Cuatro, resumen de ideas

En esta pregunta, creo que tengo dos estrategias que hago mejor, una es la implementación equilibrada y la otra es la actualización dinámica de la estrategia.

Imagine un río frente a usted con pozos (servidores) de diferentes tamaños, un montón de piedras de diferentes tamaños (solicitud de máquina virtual) rodando desde arriba, si el tamaño es apropiado (despliegue equilibrado), las piedras entrarán en el pozo, después de volcarse de nuevo, a excepción de los últimos pozos, los otros pozos casi estarán llenos. Y cada vez que la piedra rueda hacia abajo, el pozo cambiará de forma (la estrategia se actualiza dinámicamente), y será el objetivo de lidiar con la piedra que rueda hacia abajo esta vez.

Por supuesto, también existe una estrategia de compra imperfecta, que es superponer y seleccionar la solución óptima, este es también un método que vale la pena considerar.
(Vea la sección de proceso de pensamiento para más detalles)

Lo que no hice bien en esta competencia es que no consideré la relación precio / rendimiento en términos de estrategia de compra y no tomé en cuenta situaciones extremas. Y es por eso que no podemos ir más allá de 32.

Además de la falta de pensamiento, también tengo inconvenientes al jugar juegos con Java. Esto se refleja en el hecho de que no puedo migrarlos todos. Solo puedo abandonar parte de la migración debido al tiempo de ejecución. Si no hay Límite de tiempo de ejecución, entonces todavía deberíamos poder adelantar otros 10 millones a 20 millones.

Cinco, el camino de los insectos es largo

Después de optimizar esta ruta, encontré muchos errores, por lo que nuestro equipo se llama todos los errores. ¡Ay, triste todo el camino, solo puedo suspirar un largo camino de insectos!

A continuación se registran algunas de mis experiencias de búsqueda de errores.

1. Bucle sin fin

En ese momento, el código encontró un problema de tiempo de espera. Lo optimicé durante mucho tiempo, optimicé algo de lógica, eliminé el código innecesario y optimicé el código de más de 700 líneas a más de 380 líneas. Pero el problema sigue sin resolverse.

El siguiente es mi registro de errores en ese momento:

Inserte la descripción de la imagen aquí

2. Servidor invadido

A menudo encuentro este error durante el proceso de envío.
Inserte la descripción de la imagen aquí

Generalmente se trata del orden de las solicitudes.
El siguiente es mi registro de errores en ese momento:
Inserte la descripción de la imagen aquí

En ese momento, pensé que mi lógica para procesar la solicitud era incorrecta, por lo que debería usar el procesamiento secuencial para procesar la solicitud, pero el resultado fue que los recursos de la máquina virtual excedieron el límite.
Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

¡El último error resultó ser solo por un número! ! ! ¡Solo por un 1! ! !
¡ay Dios mío!

6. Sugerencias para los recién llegados

Si quieres participar en Huaduan en el futuro, basándome en mi experiencia en el juego, te daré las siguientes sugerencias:
1. Debes encontrar un buen compañero de equipo, no elijas al que se rinde en el medio. Muchos veces, los compañeros de equipo no pueden ayudarte a mejorar tu forma de pensar, pero pueden animarte a seguir adelante cuando quieras darte por vencido
2. Haz un buen trabajo en la gestión del código, una buena gestión del código puede ahorrarnos mucho esfuerzo en la optimización posterior
3. Trate de pensar con claridad antes de escribir el código, el costo de los cambios importantes posteriores será muy alto
4. Comuníquese más con los grandes, pero no los copie, pero piense si hay mejores ideas basadas en las ideas de los grandes. , para mejorar su propio pensamiento
5. diversifique para pensar en el problema, muchas veces la solución no solo se soluciona. Varias
6. Cuando cambie el error, use datos a pequeña escala (puede predecir gradualmente cómo enviarlo) para pruebas. Si encuentra que el proceso no cumple con sus expectativas, significa que hay un error en esta parte del código, y luego reduzca gradualmente el alcance para encontrar el error. Debe tener cuidado, cálmate, cálmate
7 . Si puedes usar c / c ++ para jugar, usa c / c ++ para jugar, porque otros lenguajes no son tan eficientes como él, por supuesto, si eres igual que yo No importa si desea utilizar un idioma específico, el tiempo de ejecución de esta competencia no es el factor decisivo

7. Resumen de impresiones

Sentí mucho esta competencia. Trabajé duro durante una semana y me salté una semana de clase (riendo y llorando). Aunque no llegué a las semifinales, me ayudó mucho. Especialmente en términos de algoritmo y optimización de código.

Realmente admiro a los grandes en la primera fila, que pueden mantener el costo tan bajo, lo cual está lejos de lo que puedo lograr.

Por supuesto, mi nivel actual todavía no es suficiente y tengo poca experiencia en este tipo de ajustes. Solía ​​concentrarme en aprender el conocimiento del desarrollo de back-end de Java, pero me olvidé de escribir y optimizar el código subyacente, por lo que tendré que Complemente el conocimiento de las estructuras de datos y los algoritmos en el futuro. Estudie mucho y esfuércese por ingresar a las semifinales en la próxima guerra el próximo año.


Por último adjuntar el código de dirección de gitee de esta época, que contiene los archivos relevantes de este concurso, si es necesario, puedes descargarlo tú mismo .


Que podamos tomar nuestros sueños como caballos y estar a la altura de nuestra juventud.
¡Anímate contigo!

Supongo que te gusta

Origin blog.csdn.net/qq_46101869/article/details/115284543
Recomendado
Clasificación