2012-06-13 11 views
5

Estoy tratando de extraer todos los caracteres únicos de mi archivo de diccionario francés utf-8 usando este código de Ruby. El diccionario es 3.7 MB. Por alguna razón, mi computadora decente tarda aproximadamente media hora en ejecutarse. ¿Algunas ideas?Agregar cadenas cortas a un conjunto en Ruby es lento

c = Set.new 
f = open "dict" 
s = f.read 
f.close 

for i in 0..s.length-1 
    c << s[i] 
end 
+0

Al finalizar, solo había 69 caracteres en el conjunto. No veo por qué debería llevar tanto tiempo ejecutarlo. –

Respuesta

5

La lectura en el archivo completo de una sola vez antes de realizar cualquier cálculo sobre el mismo impide IO de ser intercalada con el cálculo. Además, aumenta la presión de la memoria (potencialmente importante si se está ejecutando cerca del límite de la memoria) y reduce drásticamente cache coherency.

me escribió el siguiente pequeño script que se ejecuta en segundo .3 en mi archivo /usr/share/dict/words - menos de un megabyte, pero aún lo suficientemente grande como para ser un poco interesante:

$ cat /tmp/set.rb 
#!/usr/bin/ruby 

require 'set' 

c = Set.new 
f = open "/usr/share/dict/words" 

f.each_char do |char| 
    c << char 
end 

p c 
$ time /tmp/set.rb 
#<Set: {"A", "\n", "'", "s", "B", "M", "C", "T", "H", "I", "D", "S", "O", "L", "P", "W", "Z", "a", "c", "h", "e", "n", "l", "i", "y", "r", "o", "b", "d", "t", "u", "j", "g", "m", "p", "v", "x", "f", "k", "z", "w", "q", "ó", "ü", "á", "ö", "ñ", "E", "F", "R", "U", "N", "G", "K", "é", "ä", "Q", "è", "V", "J", "X", "ç", "ô", "í", "Y", "â", "û", "ê", "å", "Å"}> 

real 0m0.341s 
user 0m0.340s 
sys 0m0.000s 

Su programa era todavía ejecutar una minutos a más tarde, y me di por vencido.

La principal diferencia es que el mío usa los iteradores incorporados para leer en un búfer una cantidad menor del archivo (probablemente 4k-16k) y me da un carácter específico en cada iteración. Esto reutilizará las mismas pequeñas cantidades de memoria una y otra vez y permitirá que las líneas de caché relativamente pequeñas de la CPU almacenen la totalidad de los datos.

Editar

Con un caso de prueba pequeña que era capaz de aislar la diferencia de velocidad sobre todo a la each_char vs cadena de sub-secuencias de comandos. Jörg points out that string subscripting is an O(N) operation - porque las cadenas UTF-8 no se pueden indexar simplemente por multiplicación como cabría esperar, encontrar el carácter Nth significa comenzar desde el principio. Por lo tanto, su enfoque es O (N^2) y el mío fue solo O (N) y que va mucho más allá al explicar la diferencia de rendimiento. Finalmente estoy contento de que descubrimos la causa central.

+0

¡Santa vaca! No puedo creer la diferencia que hizo! He estudiado algunas cosas sobre la estructura de datos y la eficiencia de los algoritmos, pero no tenía idea de que considerar la memoria caché podría proporcionar una mejora semejante. Me alegro de que no haya sido porque los Sets son lentos. Tendré que estudiar esto y pensar en ello. ¡¡¡Gracias!!! –

+0

Ciertamente no esperaba que tu versión funcionara más despacio que antes. Honestamente, hubiera supuesto un factor de diferencia de 10 como máximo. Esto fue una completa sorpresa para mí, y no puedo evitar pensar que debe haber algo más. (¿Tal vez se deba en gran parte a los subíndices de la matriz frente al repetidor? Se necesitan más pruebas). De todos modos, este fue un hallazgo divertido. ¡Gracias! – sarnold

+2

En realidad, parece deberse principalmente a iteración frente a indexación. No estoy seguro de por qué eso importaría, pero ahí vamos. :) – sarnold

Cuestiones relacionadas