2013-05-09 3 views
0

많은 파일이 10 억 개가 있습니다. 스크립트는 모든 행을 반복하여 다른 데이터 세트와 비교합니다.루비 파일 읽기 병렬 처리

현재이 스레드는 1 스레드/1 코어에서 실행 중이므로 여러 파일을 동시에 처리 할 수 ​​있는지 궁금합니다.

내 마음에 지금까지 온 유일한 해결책은 sed 유닉스 명령입니다. sed를 사용하면 파일의 "조각"을 읽을 수 있습니다 (x 줄부터 y 줄까지). 그래서 몇 개의 포크가 해당하는 sed의 출력을 처리 할 수 ​​있습니다. 그러나 문제는 Ruby가 전체 sed 출력을 RAM에 먼저로드한다는 것입니다.

sed보다 나은 해결책이 있습니까? 아니면 sed 출력을 Ruby로 "스트리밍"할 수있는 방법이 있습니까?

+3

'split' 명령을 사용하여 파일을 더 작은 파일로 분할 한 다음 작은 파일을 병렬로 처리합니다. – saihgala

+0

파일에서 일부 문자열을 일치시켜야하는 경우 분할하는 방법에 대해주의해야합니다. 절반에서 일치하는 내용을 분리하여 놓칠 수 있기 때문입니다. – fotanus

답변

1

fork 또는 threads으로이 작업을 수행 할 수 있습니다. 두 경우 모두 관리 대상을 작성하고 필요한 하위 프로세스 수와 처리 할 파일 행 수를 결정해야합니다.

코드의 첫 번째 부분에서는 파일을 스캔하여 파일에 포함 된 줄 수를 결정해야합니다. 당신은 당신이 * nix에서 스크립트 또는 Mac OS에 있다면 다음 명령을 사용하여 그렇게 할 수 있습니다 :

lc = `wc -l path/to/file`.to_i 

하거나 파일을 열고 당신이 라인을 읽을 때 카운터를 증가하여

. 루비는이 일에 매우 빠르지 만 "60 억"라인을 포함하는 파일에 wc는 더 나은 선택이 될 수 있습니다

: 하위 프로세스의 숫자로 관리 할 것을

lc = 0 
File.foreach('path/to/file') { lc += 1 } 

나누기

NUM_OF_PROCESSES = 5 
lines_per_process = lc/NUM_OF_PROCESSES 
는 그런 곳 처리를 시작하는 방법을 말하고, 당신의 프로세스를 시작하고, 얼마나 많은 라인 : 검증되지 않은,하지만 내가 시작 했죠 어디

require 'threads' 
children = [] 
1.step(lc, lines_per_process) do |start_line| 
    children << Thread.new do 
    cur_line = 0 
    File.foreach('path/to/file') do |li| 
     cur_line += 1 
     next unless (cur_line === start_line .. (start_line + lines_per_process) 
     # ... do something with the lines read 
    end 
    end 
end 

# wait for them to finish 
children.each { |c| c.join } 

합니다.

+0

감사합니다! 이것은 매우 유용합니다 - 아주 간단합니다. 제안으로서, 나는 lines_per_process division을 .to_f.ceil 할 것이다. –

+0

스레드가 처리해야하는 줄 수를 계산하는 방법은 여러 가지가 있습니다. 그 기계에 대한 경험을 바탕으로'n' lines/second/thread를 할 수 있다고 말한 임의의 수를 설정할 수도 있습니다. 파일의 맨 위에서 시작해서 줄을 긋고, 매 n 개의 스레드를 시작하고 스레드에게' fpos' 값을 사용하여 그 지점까지 '탐색'할 수 있습니다. 그것은 많은 선과 죽은 것을 읽습니다. 다른 방법으로는 섬유를 사용하거나 포크를 사용하고 OS가 Ruby 대신 작업을 관리하도록 할 수 있습니다. Ruby의 현재 스레드 모델의 특성 때문에 처리량을 늘려야합니다. –

2

실제로 도움을 요청하는 내용.

먼저 파일의 n 번째 줄로 점프하려면 먼저 파일의 이전 부분을 읽고 줄 바꿈 횟수를 계산해야합니다. 예를 들면 다음과 같습니다 sed 명령하지 인스턴트 얼마나

$ ruby -e '(1..10000000).each { |i| puts "This is line number #{i}"}' > large_file.txt 
$ du -h large_file.txt 
266M large_file.txt 
$ purge # mac os x command - clears any in memory disk caches in use 
$ time sed -n -e "5000000p; 5000000q" large_file.txt 
This is line number 5000000 
sed -n -e "5000000p; 5000000q" large_file.txt 0.52s user 0.13s system 28% cpu 2.305 total 
$ time sed -n -e "5000000p; 5000000q" large_file.txt 
This is line number 5000000 
sed -n -e "5000000p; 5000000q" large_file.txt 0.49s user 0.05s system 99% cpu 0.542 total 

주, 그것은 5 백만 라인이 어디에 있는지 알아 내기 위해 파일의 처음 부분을 읽어했다. 그래서 두 번째 실행은 내게 훨씬 빠릅니다. 컴퓨터가 파일을 RAM에 캐시했습니다. 당신은 항상 다음 줄을 읽기 위해 다른 파일의 일부 또는 파일 사이에 점프하는 경우 (수동으로 파일을 분할하여)이 해내 할 경우에도

, 당신은 가난한 IO 성능을 얻을 것이다. 더 나은 것 무엇


대신 별도의 스레드 (또는 프로세스)의 모든 n 번째 라인을 처리하는 것입니다. 이렇게하면 여러 개의 CPU 코어를 사용할 수 있지만 여전히 우수한 IO 성능을 발휘합니다. 이것은 parallel 라이브러리를 통해 쉽게 수행 할 수 있습니다.(4 개 공정을 사용)

$ ruby -e '(1..10000000).each { |i| puts "This is line number #{i}"}' > large_file.txt # use a smaller file to speed up the tests 
$ time ruby -r parallel -e "Parallel.each(File.open('large_file.txt').each_line, in_processes: 4) { |line| puts line if (line * 10000) =~ /9999/ }" 
This is line number 9999 
This is line number 19999 
This is line number 29999 
This is line number 39999 
This is line number 49999 
This is line number 59999 
This is line number 69999 
This is line number 79999 
This is line number 89999 
This is line number 99990 
This is line number 99991 
This is line number 99992 
This is line number 99993 
This is line number 99994 
This is line number 99995 
This is line number 99996 
This is line number 99997 
This is line number 99999 
This is line number 99998 
ruby -r parallel -e 55.84s user 10.73s system 400% cpu 16.613 total 

$ time ruby -r parallel -e "Parallel.each(File.open('large_file.txt').each_line, in_processes: 1) { |line| puts line if (line * 10000) =~ /9999/ }" 
This is line number 9999 
This is line number 19999 
This is line number 29999 
This is line number 39999 
This is line number 49999 
This is line number 59999 
This is line number 69999 
This is line number 79999 
This is line number 89999 
This is line number 99990 
This is line number 99991 
This is line number 99992 
This is line number 99993 
This is line number 99994 
This is line number 99995 
This is line number 99996 
This is line number 99997 
This is line number 99998 
This is line number 99999 
ruby -r parallel -e 47.04s user 7.46s system 97% cpu 55.738 total 

번째 버전 빠른 일본어, 약 4 배의 시간이 29.81 % 완료 :

사용 예는 (제 컴퓨터는 4 개의 코어를 가지고).

+0

좋은 제안, 나는이 보석에 대해 몰랐다. –