¿Simple lógica de "desfragmentación" que minimiza los cambios?

8

editar:

así que claramente no lo expliqué bien, así que déjenme intentarlo nuevamente con ejemplos. Tengo una 'tubería' de un tamaño determinado con señales enrutadas a través de ella en desplazamientos determinados. La tubería puede fragmentarse con señales en diferentes desplazamientos, lo que hace imposible ajustar una nueva señal. Quiero un algoritmo que me diga cómo organizar las señales para desfragmentar la tubería; ¡pero con un número mínimo de señales que realmente se mueven! Entonces, para el ejemplo ...

Digamos que tengo una tubería de tamaño 16. Tiene las siguientes señales con los tamaños y las compensaciones descritas.

offset 0: A (size of 4; fills slots 0-3)
offset 5: C (size of 2, fills slot 5-6)
offset 8: B (size of 4, fills 8-11)
offset 14: D (size 2, fills 14-15)

Pipe: AAAA_CC_BBBB__DD

En este caso, tengo 1 ranura abierta en los desplazamientos 4 y 7, y dos en el desplazamiento 12-13. Ahora digamos que quiero agregar una señal de tamaño 4 a esta tubería. No hay espacio continuo para ello ahora, pero sé que tengo suficiente espacio si lo desfragmento. La solución 'obvia' es agrupar todas las señales en la 'parte superior' de esta manera:

offset 0:  A (size 4, fills 0-3)
offset 4:  B (size 4, fills 4-7)
offset 8:  C (size 2, fills 8-9)
offset 10: D (size 2, fills 10-11)

Pipe: AAAABBBBCCDD____

Esto deja las ranuras 12-15 libres para mi nueva señal. Sin embargo, para hacer esto, reposicioné 3 señales (BD). Para cada señal que moví, tengo que enviar comandos al hardware y esperar una cantidad de tiempo no trivial.

Si fuera más inteligente podría darme cuenta de que hay otro enfoque. Podría reposicionar así:

offset 0:  A(size 4, fills 0-3)
offset 8:  B(size 4, fills 8-11)
offset 12: C(size 2, fills 12-13)
offset 14: D(size 2, fills 14-15).

Pipe: AAAA____BBBBCCDD

Ahora puedo ajustar mi nueva señal en desplazamientos 4-7; Y solo tuve que reposicionar una señal (B). Ahorrando así en llamadas de hardware.

Me pregunto si hay un buen algoritmo para detectar situaciones como esta; donde puedo 'encajar' una señal en una tubería con el mínimo número de señales movidas. El problema que me viene a la mente es una N! algoritmo; que básicamente equivale a "generar todas las distribuciones posibles, calcular cuántos movimientos resultó". Estoy buscando un acercamiento más rápido.

La aproximación no tiene que ser 100% perfecta, estoy buscando principalmente minimizar el caso promedio, siempre que el peor de los casos no sea demasiado horrendo. Sé que nunca tendré más de 255 señales en una tubería determinada; ¡así que puedo 'escaparme' con N! tan malo como suena También sé que el tamaño de cada señal es una potencia de 2, al igual que el tamaño de la tubería.

Además, ¿existen algoritmos brillantes para la colocación de señales que minimicen la fragmentación?


Pregunta contestada; vea abajo.

Quería ampliar un poco la respuesta para explicar mejor cómo ocurriría la desfragmentación para cualquiera que lea; amigo lo explica. Quería señalar un enfoque más simple para conceptualizar y explicar la parte de defraudación con más detalle ya que esa era la pregunta original.

Estoy explicando mi enfoque, que es un enfoque ligeramente diferente / más simple pero que efectivamente mantiene el concepto de "amigo". No estoy precalculando bloques o etiquetándolos, esto es demasiado esfuerzo para implementar y mantener. Para mí, el costo de la CPU de calcular a dónde irá una señal es bastante trivial en comparación con la colocación / eliminación real de señales; así que puedo permitirme perder una pequeña cantidad lineal de CPU al no precomputar para simplificar mi lógica. Entonces el proceso es:

para la inserción:

Todas las señales se mantienen en límites de señal iguales al tamaño de la señal, por lo que una señal comenzará en desplazamiento donde offest% senaliza = 0. Para el desplazamiento real, recorremos y calculamos los intervalos que mantienen esta frontera. Entonces, si mi señal es de tamaño 4 en una tubería de tamaño 16, miraré los intervalos 0-4, 5-7, 8-11, 12-15.

Para cada intervalo, verifique si todo el espacio en ese intervalo es libre. En el caso simple, tenemos un intervalo sin señales y simplemente colocamos la señal en ese intervalo. Importante, miramos los intervalos en orden y colocamos nuestra señal en el primer intervalo libre , esto asegura que rompamos el 'bloque' más pequeño posible (usando términos de amigos) cuando agregamos nuestra señal. Esto debería ser equivalente al enfoque de amigo descrito por im3l96; excepto sin precomputación de bloques.

Si ningún intervalo es completamente libre, tenemos que desfragmentar. Para esto, encontramos la señal con las ranuras más no utilizadas. Si los intervalos múltiples tienen el mismo número de ranuras sin usar, seleccione el primer intervalo. Luego encontramos la señal más grande en este intervalo y recurrimos recursivamente al mismo algoritmo de inserción para la señal más pequeña (excepto marcar el intervalo que seleccionamos para insertar nuestra primera señal como no disponible de alguna manera). Esto mueve la señal a otro lugar que encajará. Luego encontramos la siguiente señal más pequeña en nuestro intervalo seleccionado y hacemos lo mismo hasta que hayamos movido todas las señales. El peor caso de 2 ^ n-1 señales movidas; donde N es el número de tamaños de señal potenciales <= nuestra señal (suponiendo que las señales son múltiples de 2, entonces N = log2 (signalSize)).

Aquí hay un ejemplo. * representa un espacio marcado como no disponible cuando llamamos recursivamente a este método (es decir, el intervalo en el que el método de llamada quería colocar su señal, y por lo tanto no quiere que la llamada recursiva intente colocar una señal)

Aquí hay un caso simple y simple que se me ocurrió que aún demuestra una complejidad completa. Nota: la siguiente estructura sería difícil de crear, pero podría resultar de la aproximación de amigos si alguien lo intentara muy, muy duro.

FFFFFFFF__AA_B__EEEE_HGGCC_DII_J

Alguien pasa una señal Z de tamaño 8

seleccionamos offset 8:

defragInsert (z, tamaño 8)

effective structure: FFFFFFFF__AA_B__EEEE_HGGCC_DII_J

placing signal in interval: __AA_B__

defragInput (A, tamaño 2)

effective structure: FFFFFFFF********EEEE_HGGCC_DII_J
place signal in interval (offset 20)  _H

defragInput (H, size1)

effective structure: FFFFFFFF********EEEE**GGCC_DII_J
place signal between C & D

return defragInput (H, size1)

effective structure: FFFFFFFF********EEEE__GGCCHDII_J
place H at offset 20 now that it's open

return defragInput (A, tamaño 2)

estructura efectiva: FFFFFFFF_ _ _B__EEEEAAGGCCHDII_J mover B ahora ...

defragInput (B, size1)

estructura efectiva: FFFFFFFF * ** * EEEEAAGGCCHDII_J lugar B entre I y J

return defragInput (B, size1)

estructura efectiva: FFFFFFFF_ __ _EEEEAAGGCCHDIIBJ add B

return defragInsert (z, tamaño 8)

fianl structure: FFFFFFFFzzzzzzzzEEEEAAGGCCHDIIBJ
dsollen
fuente
Es difícil entender qué está haciendo este proceso. ¿Podría darnos un ejemplo de entrada (tal vez con una tubería con digamos 8 o 16 señales en lugar de 255) y un ejemplo de salida incorrecta y buena salida?
Neil
2
I 2nd Neil, es realmente difícil entender lo que específicamente estás preguntando. Pero parece que podría estar tratando de hacer algo similar a la asignación dinámica de memoria. En el mundo incrustado, para ayudar a evitar la fragmentación, la memoria dinámica se asigna previamente en grupos de diferentes tamaños. es decir. 10,000 bloques de unidades de 256 bytes. 1,000 bloques de unidades 1K, 200 bloques de unidades 1M, etc. Se anula la nueva. Luego, cuando se solicita memoria dinámica, se recupera del grupo que tiene los bloques de menor tamaño disponibles. Esto es fácil de administrar, rápido y evita la fragmentación. Podría funcionar para ti.
Dunk
Editado para agregar una ilustración visual de las tuberías. Acepta la edición si crees que ayuda.
Bobson
¿Cuándo / qué determina la adición / eliminación de señales?
Paddy3118
1
I 2nd @Dunk, el problema es similar a la asignación de memoria. Parte del problema en su ejemplo es que está asignando ranuras de una manera que lo hizo muy fragmentado (sin usar pools / chunk). Una solución simple que conozco es usar en.wikipedia.org/wiki/Buddy_memory_allocation . Usando amigo, en lugar de AAAA_CC_BBBB__DD obtendrá AAAACCDDBBBB____ (quedan 4 espacios vacíos). Sin reposicionamiento, se asigna de esa manera.
imel96

Respuestas:

4

El problema es similar a la asignación de memoria, por lo que mi solución propuesta es utilizar http://en.wikipedia.org/wiki/Buddy_memory_allocation . El uso del asignador de amigos minimiza la fragmentación en primer lugar al elegir el espacio libre contiguo más pequeño en la tubería para una señal.

Usando el ejemplo, así es como se asignará el espacio para la tubería de tamaño 8:

Initial state
Pipe: ----------------

A (size 4), smallest available is 16, so split that into 2x4 and 1x8
Pipe: AAAA ---- --------

C (size of 2), smallest available is 4, split that into 2x2 
Pipe: AAAA CC -- --------

B (size of 4), smallest available is 8, split that into 2x4
Pipe: AAAA CC -- BBBB ----

D (size 2), smallest available is 2
Pipe: AAAA CC DD BBBB ----

Liberar espacio es una cuestión de verificar el "amigo" del espacio que se libera; Si Buddy también es libre, entonces pueden fusionarse para formar un espacio contiguo más grande. Entonces, si el espacio se libera en el orden de las señales que entran, entonces se verá así:

Free A (buddy of space being freed is not free)
Pipe: ---- CC DD BBBB ----

Free C (buddy is not free)
Pipe: ---- -- DD BBBB ----

Free B (buddy is free, merge)
Pipe: ---- -- DD --------

Free D (buddy is free, merge)
Pipe: ----------------

También es bastante simple y rápido porque solo hay poca fragmentación, especialmente porque el tamaño de las señales es potencia de 2.

Si realmente es necesario, la desfragmentación / compactación también se puede hacer fácilmente, solo es cuestión de encontrar bloques asignados del mismo tamaño con amigos libres y fusionarlos (mover un bloque asignado creará un bloque libre más grande).

imel96
fuente
2

Bueno, antes de buscar una respuesta, primero debe definir claramente lo que desea minimizar, es decir, qué tipo de reposicionamiento de señal está permitido.

  • tomar una (y solo una) señal de la tubería y agregarla en otro lugar sin solapamientos (y repetir ese paso n veces)?

  • o para tomar n señales simultáneamente de su tubo y volver a insertarlas luego sin superposiciones (contadas como n movimientos)?

Obviamente, esta primera alternativa es mucho más restringida, ofreciéndole un espacio de búsqueda mucho más pequeño. Por ejemplo, para pasar de AAAA_CC_BBBB__DDa AAAA_CC___DDBBBB, se necesitarán al menos 4 movimientos si no se permiten reposicionamientos simultáneos, y 2 movimientos si se permiten.

Supongo que tiene el primer problema en mente (por favor confirme eso), así que aquí está mi sugerencia para esto:

  • haga de esto un problema de búsqueda de gráficos. Para una tubería de entrega, cada configuración de señal define un vértice de su gráfico, y los bordes entre esos vértices son los posibles reposicionamientos.

  • asigne un valor a cada vértice: la longitud de la secuencia de ranura continua más larga

Ahora debería ser fácil usar una técnica de búsqueda de gráficos estándar como la búsqueda de amplitud primero o la búsqueda A * para encontrar el número más pequeño de pasos hasta llegar a una configuración de tubería que tenga suficiente espacio para la nueva señal que desea agregar.

Doc Brown
fuente
Puedo mover tantas señales como quiera y agregar tantas señales como quiera, en una tubería determinada. Por lo tanto, siempre y cuando el resultado final no se superponga, cada movimiento no tiene que garantizar que no se superpongan
2013
@dsollen: esta no es una respuesta a mi pregunta, no pregunté si n es limitado. Le preguntaba si sus movimientos tienen que llevarse a cabo uno tras otro, sin solapamientos después de cada paso, o si puede mover dos señales simultáneamente. Por ejemplo, para cambiar las posiciones de dos señales, el primer caso necesita al menos 3 movimientos, mientras que el segundo solo necesita 2.
Doc Brown
@dsollen: mira mi edición
Doc Brown
0

Debe tomar una muestra representativa de sus datos reales y simular diferentes algoritmos de reempaque. Por el momento, no tengo suficiente información para simular el sistema, por lo que no pude comprobar cómo funcionaba ningún algoritmo.

Paddy3118
fuente
Sí, estoy de acuerdo, pero tampoco tengo datos sobre cómo se usará hasta que se libere y esté fuera de mis manos. Sin embargo, el algoritmo no necesita estar perfectamente optimizado para un caso de uso específico. Un algoritmo genérico que funciona bastante bien para cualquier orden de señales potencialmente aleatorio estaría bien. Tal concepto probablemente tendrá el peor de los casos de N ^ 2, y eso es factible cuando N es tan pequeño ... Sin embargo, puedo obtener el peor de los casos de N ^ 2 pero el caso promedio decente es mucho mejor.
dsollen
Hola @dsollen, en ese caso lo sacaría lo antes posible y les diría a los clientes que las optimizaciones son posibles tan pronto como recibas algún comentario. Si se tratara de una ordenación, es el equivalente a dar una ordenación por burbuja o una fusión, ya que no tiene suficiente información para recomendar Timsort.
Paddy3118
Gracias por la respuesta. Eso es esencialmente lo que hice, me llevó mucho tiempo encontrar algo bueno, así que hice lo más rápido para implementar un estúpido acercamiento a esta versión. Sin embargo, estoy bastante seguro de que iré con una versión perezosa de niño bastardo del enfoque de asignación de amigos sugerido por imel96 cuando haga la mejor versión. Sería difícil recopilar el uso, ya que muchos sitios tendrán diferentes casos de uso promedio. Y al final, un enfoque O (n) que minimiza la necesidad de ejecutarlo es tan rápido que cualquier optimización adicional no sería perceptible para los usuarios :)
dsollen
@dsollen: Buena suerte :-)
Paddy3118