2010-09-07 12 views
13

Necesito escribir un programa de clasificación en C y sería bueno si el archivo pudiera ordenarse en su lugar para ahorrar espacio en el disco. Los datos son valiosos, por lo tanto, debo asegurarme de que si el proceso se interrumpe (ctrl-c), el archivo no se corrompe. Puedo garantizar que el cable de alimentación de la máquina no será arrancado.Algoritmo de ordenamiento en sitio interrumpible

Detalles adicionales: archivo es de ~ 40 GB, los registros son de 128 bits, la máquina es de 64 bits, OS es POSIX

¿Alguna pista sobre el cumplimiento de esta, o notas en general?

Gracias!

Para aclarar: Espero que el usuario querrá ctrl-c el proceso. En este caso, quiero salir con gracia y asegurarme de que la información esté segura. Entonces, esta pregunta se trata sobre el manejo de interrupciones y la elección de un algoritmo de ordenamiento que puede concluir rápidamente si así lo solicita.

Seguimiento (2 años después): Solo para la posteridad, he instalado el controlador SIGINT y funcionó muy bien. Esto no me protege contra fallas en el suministro eléctrico, pero ese es un riesgo que puedo manejar. Código en https://code.google.com/p/pawnsbfs/source/browse/trunk/hsort.c y https://code.google.com/p/pawnsbfs/source/browse/trunk/qsort.c

+0

Creo que tienes que preguntar ¿Qué es más importante? Espacio, o si el proceso debe ser interrumpido.Si necesita asegurarse de que el archivo no está dañado, deberá hacer un seguimiento de su progreso y estados previos de alguna manera, esto ocupará más espacio que el archivo. –

+0

Para estar seguro. Haga una copia y clasifique la copia. No puedo imaginar que el sistema de archivos tenga problemas con otro 40GB –

+0

@Martin: su imaginación no funciona como lo hace la mía. En mi disco principal actualmente tengo 36.4GB gratis. –

Respuesta

5

Instale un controlador para SIGINT que solo establece el indicador "el proceso debe salir pronto".

En su género, verifique la bandera después de cada intercambio de dos registros (o después de cada N swaps). Si la bandera está configurada, rescatar.

+0

Esto me parece la mejor solución. Algunas de las otras recomendaciones me dan la impresión de que Ctrl-C debe ignorarse si no se presiona en el momento justo, lo que a mí me parece una mala experiencia de usuario. –

+0

Parece el ganador. Parece aplicarse a quicksort y heapsort. ¡Gracias! –

+0

No guardará sus datos de kill -9 o una falla de energía. –

3

Utilice la ordenación de pila y evite interrupciones (por ejemplo, señales de bloque) durante cada operación de intercambio.

+0

Sí. Simple y fácil. También realice cambios en la memoria y luego escríbalos/vacíelos todos a la vez (señales de bloqueo para esta parte). –

+0

Heapsort requiere golpear todo el archivo, lo que puede no ser bueno para cualquier cosa que no se ajuste a la memoria física. –

+0

Por otro lado, si el archivo es gigante como en su caso, Big-O y no el factor constante de io cache falla va a dominar. El ordenamiento en montones es el único tipo en el lugar con una O grande mejor que cuadrática. –

0

Realice una copia de seguridad de todo lo que planea cambiar. Pon una bandera que marca un tipo exitoso. Si todo está bien, conserve el resultado; de lo contrario, restaure la copia de seguridad.

+0

-1 no cumple con el criterio in situ de la pregunta, que establece claramente que hay un enorme conjunto de datos y quizás no hay espacio para respaldarlo. –

+0

R: No necesita guardar todo el archivo. Solo guarde las piezas pequeñas que se usan en orden. Nunca dije que todo el archivo debe estar respaldado. –

12

Jerry tiene razón, si solo le preocupa Ctrl-C, puede ignorar SIGINT por períodos cada vez. Si quiere ser una prueba contra la muerte del proceso en general, necesita algún tipo de diario mínimo. Para intercambiar dos elementos:

1) Agregue un registro a una estructura de control al final del archivo o en un archivo separado, indicando los dos elementos del archivo que va a intercambiar, A y B.

2) Copie A en el espacio reutilizable, registre que ya lo hizo, enjuague.

3) Copia de B en A, entonces grabar en el espacio de desecho que se ha hecho, al ras

4) Copiar en el espacio de desecho sobre B.

5) Retire el registro.

Este es O (1) espacio adicional para todos los propósitos prácticos, por lo que todavía cuenta como en el lugar en la mayoría de las definiciones. En teoría, registrar un índice es O (log n) si n puede ser arbitrariamente grande: en realidad es un log n muy pequeño, y el hardware razonable/tiempo de ejecución lo limita a 64.

En todos los casos cuando digo " flush ", me refiero a comprometer los cambios" lo suficiente ". A veces, su operación de descarga básica solo vacía búferes dentro del proceso, pero en realidad no sincroniza el medio físico, ya que no vacía los búferes a través de los niveles de hardware/dispositivo de OS/dispositivo. Eso es suficiente cuando lo único que te preocupa es la muerte por proceso, pero si te preocupan los medios de transporte abruptos, entonces tienes que pasar por alto el controlador. Si estaba preocupado por un corte de energía, tendría que sincronizar el hardware, pero no es así. Con un UPS o si cree que los cortes de energía son tan raros que no le importa perder datos, está bien.

Al inicio, verifique el espacio libre para cualquier registro de "intercambio en curso".Si encuentra uno, determine cuánto ha avanzado y complete el intercambio desde allí para que los datos vuelvan a estar en buen estado. Luego comience su clasificación de nuevo.

Obviamente, hay un problema de rendimiento aquí, ya que está haciendo el doble de escritura de registros que antes, y las descargas/sincronizaciones pueden ser sorprendentemente caras. En la práctica, su ordenación en el lugar puede tener algunas operaciones compuestas de elementos en movimiento, que implican muchos intercambios, pero que puede optimizar para evitar que cada elemento llegue al espacio cero. Simplemente debe asegurarse de que antes de sobrescribir cualquier dato, tenga una copia segura en algún lugar y un registro de dónde debe ir esa copia para que su archivo vuelva a un estado donde contenga exactamente una copia de cada elemento.

Jerry también tiene razón en que la clasificación real en el lugar es demasiado difícil y lenta para la mayoría de los propósitos prácticos. Si puede ahorrar una fracción lineal del tamaño del archivo original como espacio cero, lo tendrá mucho mejor con un tipo de fusión.

De acuerdo con su aclaración, no necesitaría ninguna operación de lavado incluso con un ordenamiento in situ. Necesita espacio cero en la memoria que funciona de la misma manera, y que su manejador SIGINT puede acceder para obtener los datos seguros antes de salir, en lugar de restaurar al iniciar después de una salida anormal, y necesita acceder a esa memoria de forma segura para señales (lo que técnicamente significa usar un sig_atomic_t para marcar qué cambios se han realizado). Aun así, probablemente estés mejor con un mergesort que con un tipo real.

+0

Pensaría en usar un archivo mapeado de memoria para el espacio cero. Debería darte lo mejor de ambos mundos. 1) Baja sobrecarga para los accesos porque está almacenada en la memoria caché y no hay llamadas API 2) Si el proceso se apaga, el SO aún debe vaciar la memoria modificada en el disco, independientemente de cómo muera el proceso. – torak

+0

@torak: Buenas noticias, no sabía que POSIX proporcionara esa garantía para mmap. Todavía me preocupa que los accesos al archivo puedan reordenarse, lo que hace que no sea confiable para la recuperación. Entonces todos los punteros deberían ser 'volátiles' o algo así. –

+0

Debería usar 'msync()' con 'MS_SYNC' en cada punto de enjuague. 'msync()' también debería implicar una barrera de compilación, por lo que 'volátil' no debería ser necesario. – caf

5

La parte para proteger contra ctrl-c es bastante fácil: signal(SIGINT, SIG_IGN);.

Por lo que se refiere a la clasificación, una ordenación por fusión generalmente funciona bien para la clasificación externa. La idea básica es leer tantos registros en la memoria como sea posible, ordenarlos y luego volver a escribirlos en el disco. Con mucho, la manera más fácil de manejar esto es escribir cada ejecución en un archivo separado en el disco. Luego los combina de nuevo: lea el primer registro de cada ejecución en la memoria y escriba el más pequeño de ellos en el archivo original; lea otro registro de la ejecución que suministró ese registro, y repita hasta que termine. La última fase es la única vez que está modificando el archivo original, por lo que es el único momento en que realmente necesita asegurarse contra interrupciones y cosas por el estilo.

Otra posibilidad es utilizar una ordenación por selección. Lo malo es que la clasificación en sí es bastante lenta. Lo bueno es que es bastante fácil escribirlo para sobrevivir casi cualquier cosa, sin usar mucho espacio extra. La idea general es bastante simple: encuentre el registro más pequeño en el archivo e instálelo en el primer lugar. A continuación, busque el registro más pequeño de lo que queda, e instálelo en el segundo punto, y así sucesivamente hasta que termine. El buen punto de esto es que el diario es trivial: antes de hacer un intercambio, registra los valores de los dos registros que va a intercambiar. Dado que el orden se extiende desde el primer registro hasta el último, la única otra cosa que necesita hacer un seguimiento es cuántos registros ya están ordenados en un momento determinado.

+1

+1 - no se ajusta a la demanda en el lugar, pero es en mi humilde opinión el enfoque más seguro. No modifica el archivo maestro mientras ordena los fragmentos, y si el proceso de fusión falla, tiene los archivos de fragmentos ordenados como respaldo. – snemarch

+1

En lugar de ignorar 'SIGINT', debe ** bloquear ** (y todas las demás señales) durante las operaciones de intercambio, pero periódicamente desbloquearlo para que las señales pendientes que llegaron bloqueadas puedan procesarse. –

-1

Asumiendo un sistema operativo de 64 bits (dijiste que es una máquina de 64 bits pero aún podía funcionar con sistema operativo de 32 bits), puedes usar mmap para asignar el archivo a una matriz y luego usar qsort en la matriz.

Agregue un controlador para SIGINT para llamar a msync y munmap para permitir que la aplicación responda a Ctrl-C sin perder datos.

+1

-1 para una respuesta incorrecta. Si una señal interrumpe 'qsort', los datos están en un estado indeterminado hasta que' qsort' termine. No hay forma de usar el 'qsort' del sistema para satisfacer las necesidades del OP. –

Cuestiones relacionadas