Meus dados:
- É um arquivo de 71 MB com 1,5 milhões de linhas.
- Tem 6 campos
- Todos os seis campos se combinam para formar uma chave exclusiva - e é isso que eu preciso classificar.
Instrução de classificação:
sort -t ',' -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 -k6,6 -o output.csv input.csv
O problema:
- Se eu ordenar sem chaves, demora 30 segundos.
- Se eu ordenar com chaves, demora 660 segundos.
- Preciso classificar com chaves para manter isso genérico e útil para outros arquivos que também têm campos sem chave. O tempo de 30 segundos é bom, mas o 660 é um assassino.
Mais detalhes usando o tempo unix:
- classifique input.csv -o output.csv = 28 segundos
- classifique -t ',' -k1 input.csv -o output.csv = 28 segundos
- classifique -t ',' -k1,1 input.csv -o output.csv = 64 segundos
- classifique -t ',' -k1,1 -k2,2 input.csv -o output.csv = 194 segundos
- classifique -t ',' -k1,1 -k2,2 -k3,3 input.csv -o output.csv = 328 segundos
- classifique -t ',' -k1,1 -k2,2 -k3,3 -k4,4 input.csv -o output.csv = 483 segundos
- classifique -t ',' -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 input.csv -o output.csv = 561 segundos
- classifique -t ',' -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 -k6,6 input.csv -o output.csv = 660 segundos
Eu poderia, teoricamente, mover o diretório temporário para SSD e / ou dividir o arquivo em 4 partes, classificá-las separadamente (em paralelo) e depois mesclar os resultados, etc. Mas espero algo mais simples, pois parece que o tipo é apenas escolhendo um algoritmo ruim.
Alguma sugestão?
Testando melhorias usando o tamanho do buffer:
- Com 2 teclas, obtive uma melhoria de 5% com 8, 20, 24 MB e melhor desempenho de 8% de melhoria com 16 MB, mas 6% pior com 128 MB
- Com 6 teclas, obtive uma melhoria de 5% com 8, 20, 24 MB e melhor desempenho de 9% de melhoria com 16 MB.
Teste de melhorias usando a ordem do dicionário (apenas 1 execute cada):
- classifique -d --buffer-size = 8M -t ',' -k1,1 -k2,2 input.csv -o output.csv = 235 segundos (21% pior)
- classifique -d --buffer-size = 8M -t ',' -k1,1 -k2,2 input.csv -o ouput.csv = 232 segundos (21% pior)
- conclusão: faz sentido que isso atrase o processo, não seja útil
Teste com sistema de arquivos diferente no SSD - não consigo fazer isso neste servidor agora.
Teste com código para consolidar chaves adjacentes:
def consolidate_keys(key_fields, key_types):
""" Inputs:
- key_fields - a list of numbers in quotes: ['1','2','3']
- key_types - a list of types of the key_fields: ['integer','string','integer']
Outputs:
- key_fields - a consolidated list: ['1,2','3']
- key_types - a list of types of the consolidated list: ['string','integer']
"""
assert(len(key_fields) == len(key_types))
def get_min(val):
vals = val.split(',')
assert(len(vals) <= 2)
return vals[0]
def get_max(val):
vals = val.split(',')
assert(len(vals) <= 2)
return vals[len(vals)-1]
i = 0
while True:
try:
if ( (int(get_max(key_fields[i])) + 1) == int(key_fields[i+1])
and key_types[i] == key_types[i+1]):
key_fields[i] = '%s,%s' % (get_min(key_fields[i]), key_fields[i+1])
key_types[i] = key_types[i]
key_fields.pop(i+1)
key_types.pop(i+1)
continue
i = i+1
except IndexError:
break # last entry
return key_fields, key_types
Embora esse código seja apenas uma solução alternativa, ele só se aplicará a casos em que eu tenha um conjunto contíguo de chaves. Isso acelera o código em 95% no pior cenário possível.