2013-05-10 2 views
1

이 메서드를 사용하여 약 22 만 줄의 단일 텍스트 파일을 처리하고 있습니다. 하나를 처리하는 데 몇 분이 걸리지 만 그 중 많은 수가 있습니다. 이 프로세스를 더 빨리 수행 할 수있는 권장 사항이 있습니까? 파일을 해시 배열로 변환하는 프로세스 성능을 향상 시키려면 어떻게해야합니까?

def parse_list(file_path,import=false) 
# Parse the fixed-length fields 
    if File.exist?(file_path) 
    result=[] 
    File.readlines(file_path)[5..-1].each do |rs| 
     if rs.length > 140 
      r=rs.strip 
      unless r=='' 
      filing={ 
        'name' => r[0..50].strip, 
        'form' => r[51..70].strip, 
        'type' => r[71..80].strip, 
        'date' => r[81..90].strip, 
        'location' => r[91..-1].strip 
        }  
       result.push(filing) 
      end 
     end 
    end 
    return result 
    else 
    return false 
    end 
end 

는 업데이트 :

원래, 나는 넥스와 thetinman의 방법을 사용에서 엄청난 시간을 절약 그래서 난 일치 구문 분석 방법을 유지를 테스트했다가 생각했다.

내 원래 r[].strip 구문 분석 방법을 사용하지만, 넥스의 each_line 블록 방법과 thetinman의 foreach 방법과 :와 unpack :

Rehearsal --------------------------------------------- 
Nex   9.580000 0.120000 9.700000 ( 9.694327) 
Thetinman 11.470000 0.090000 11.560000 (11.567294) 
----------------------------------- total: 21.260000sec 

       user  system  total  real 
Nex  15.480000 0.120000 15.600000 (15.599319) 
Thetinman 18.150000 0.070000 18.220000 (18.217744) 

unpack.map(&:strip)r[].strip 대 :

Rehearsal --------------------------------------------- 
Nex   8.260000 0.130000 8.390000 ( 8.394067) 
Thetinman 9.740000 0.120000 9.860000 ( 9.862880) 
----------------------------------- total: 18.250000sec 

       user  system  total  real 
Nex  14.270000 0.140000 14.410000 (14.397286) 
Thetinman 19.030000 0.080000 19.110000 (19.118621) 

는 thetinman의 unpack.map 구문 분석 방법을 사용하여 다시 실행 map은 속도가 증가하지 않는 것 같지만 사용하는 흥미로운 방법입니다. 미래에.

다른 문제가 발견되었습니다. 상당한 시간을 절약하면 Nex와 thetinman의 방법을 수동으로 실행하려고했습니다. 이것은 내 원래 코드처럼 내 컴퓨터가 걸려있는 것을 발견 한 곳입니다. 그래서 나는 다시 테스트했지만, 원래의 코드로 계속 진행했습니다.

Rehearsal --------------------------------------------- 
Original 7.980000 0.140000 8.120000 ( 8.118340) 
Nex   9.460000 0.080000 9.540000 ( 9.546889) 
Thetinman 10.980000 0.070000 11.050000 (11.042459) 
----------------------------------- total: 28.710000sec 

       user  system  total  real 
Original 16.280000 0.140000 16.420000 (16.414070) 
Nex  15.370000 0.080000 15.450000 (15.454174) 
Thetinman 20.100000 0.090000 20.190000 (20.195533) 

내 코드, 넥스, 그리고 thetinman의 방법은 넥스가 가장 빠른 사용하여 벤치 마크 인으로, 비교 보인다. 그러나 Benchmark는 코드를 수동으로 테스트하기 위해 pry를 사용하면 모든 메소드가 상당히 길어질 수 있기 때문에 전체 결과를 알 수있는 것 같지 않습니다.

  1. 가 대규모 느리게 실행 코드를 만드는이 이상한 결과를 얻을 것이다 IRB/올립니다에서 이런 일을 실행하는 방법에 대한 구체적인 뭔가가 있나요 : 나는 몇 가지 남아있는 질문이

    ?

  2. original_method.count, nex_method.count 또는 thetinmans_method.count을 실행하면 모두 빠르게 돌아 오는 것 같습니다.
  3. 메모리 문제 및 확장 성으로 인해 thetinman 및 nex는 원래 방법을 사용하지 말 것을 권장합니다. 그러나 미래에는 벤치 마크 같은 것으로 메모리 사용을 테스트하는 방법이 있습니까?
  4. NEX에 대한

업데이트, activerecord-import 사용 :

@nex, 이것은 당신이 무슨 뜻입니까? 이것은 여전히 ​​나를 위해 천천히 실행하는 것,하지만 난 당신이 말할 때 무슨 뜻인지 잘 모르겠어요 :

가져 오기 그 블록 내부의 데이터를 한 세트.

어떻게 수정 하시겠습니까?당신이 볼 수 있듯이 activerecord-import 방법에서

def parse_line(line) 
    filing={ 
    'name' => line[0..50].strip, 
    'form' => line[51..70].strip, 
    'type' => line[71..80].strip, 
    'date' => line[81..90].strip, 
    'location' => line[91..-1].strip 
    }  
end 

def import_files 
result=[] 
parse_list_nix(file_path){|line| 
    filing=parse_line(line)  
    result.push(Filing.new(filing)) 

} 
Filing.import result #result is an array of new records that are all imported at once 
end 

결과, 실질적으로 느리다 :

Rehearsal ------------------------------------------ 
import 534.840000 1.860000 536.700000 (553.507644) 
------------------------------- total: 536.700000sec 

      user  system  total  real 
import 263.220000 1.320000 264.540000 (282.751891) 

이 느린 가져 오기 프로세스가 정상 보이는가?

나에게 너무 느린 것 같습니다. 이 속도를내는 방법을 알아 내려고 노력하고 있지만 아이디어가 없습니다.

+0

. 테스트에서 파일 읽기 방법을 제외하고 모든 것을 제거하고 데이터와 함께 할 때까지 최대한 빨리 조정할 때까지 조정하십시오. 확장 성이 없으므로 RAM에 모든 것을 가져오고 싶지는 않습니다. 그런 다음 분할을 추가하고 가져 와서 테스트를 반복하십시오. Benchmark와 함께 pry 또는 IRB 나 디버거를 사용할 수없고 어떠한 종류의 실제 결과도 기대할 수 있습니다. 스크립트가 제공하는 편의성의 부작용으로 스크립트가 실행되는 방식에 영향을 미치고 느린 실행 코드로 변환됩니다. –

+1

로드하는 파일의 수는 얼마인지는 알려지지 않았지만, 하루에 한 번씩 220K 줄의 파일을로드하는 데 5-8 분이 걸리지는 않습니다. 느린 경우에는 여러 스레드를 사용하여 여러 파일을로드하거나 프로세스를 포크하여이를 수행하도록하거나 데이터베이스가로드를 직접 수행하도록 할 수 있습니다. 고정 너비 필드를 사용하기 때문에 전체로드가로드됩니다. 아마 컴파일 된 응용 프로그램 관련 코드이기 때문에 더 빠릅니다. –

+0

@thetinman, 가져올 파일 당 약 6-9 분입니다 (위의 벤치 마크 참조). 약 20 개의 파일을 가져와야합니다. 좋은 제안이 있습니다. 나는 데이터베이스에 직접 가져 오기를 제안합니다. 나는 그것이 어떻게 행해지는지 연구 할 것이다. 나는 스레딩이나 프로세스를 포크하는 것을 배웠지 않았지만 그것도 살펴볼 것입니다. 좋은 링크가 있다면 제 방식으로 보내주십시오. 다시 감사합니다! – user2012677

답변

2

샘플 데이터 없이는이를 확인하기 어렵지만, 원래의 코드를 기반으로, 나는 아마 이런 걸 쓸 것 :

require 'english' 

# Parse the fixed-length fields 
def parse_list(file_path,import=false) 

    return false unless File.exist?(file_path) 

    result=[] 
    File.foreach(file_path) do |rs| 
    next unless $INPUT_LINE_NUMBER > 5 
    next unless rs.length > 140 

    r = rs.strip 
    if r > '' 
     name, form, type, date, location = r.unpack('A51 A20 A10 A10 A*').map(&:strip) 
     result << { 
     'name'  => name, 
     'form'  => form, 
     'type'  => type, 
     'date'  => date, 
     'location' => location 
     } 
    end 
    end 

    result 
end 

220,000 라인 I에서 온 큰 파일이 아닙니다합니다. 우리는 오전 중반까지 로그 파일을 3 배나 얻습니다. 따라서 파일 입출력을 사용하는 모든 파일 I/O를 사용합니다. Ruby의 IO 클래스에는 행 단위 I/O와 배열을 반환하는 숫자의 두 가지 메서드가 있습니다. 그들이 확장 가능하기 때문에 당신은 전자를 원한다. 읽혀지는 파일이 Ruby의 메모리에 안락하게 맞을 것이라고 보장 할 수 없다면 나중에는 피하십시오.

+0

$ INPUT_LINE_NUMBER> 5가 아닌 한 .foreach (file_path) [5 .. – user2012677

+0

원래 코드의 흔적입니다. 그것을 꺼내. 감사. –

+0

느린 가져 오기 프로세스가 정상적으로 보입니까? 또는 개선을위한 추가 권장 사항을 작성 하시겠습니까? – user2012677

2

문제는 당신이 당신의 기억을 채우고 있다는 것입니다. 그 결과로 무엇을 할 것입니까? 그것은 전체적으로 당신의 기억 속에 머물러야합니까, 아니면 그것을 한 줄씩 한 줄씩 처리하는 것일 수 있습니까?

또한 여기에서 readline을 사용하지 마십시오. 그것이 열거를 사용하기 때문에이 대신 같은 것을 할 : 당신은 우리에게에서 작업 할 수있는 샘플 데이터를 제공하지 않은, 그래서 우리가 테스트 할 수있는 기반을 없어

def parse_list(file_path, import=false) 
    i = 0 
    File.open(file_path,'r').each_line do |line| 
    line.strip! 
    next if (i+=1) < 5 || line.length < 141 
    filing = { 'name' => r[0..50].strip, 
       'form' => r[51..70].strip, 
       'type' => r[71..80].strip, 
       'date' => r[81..90].strip, 
       'location' => r[91..-1].strip } 
    yield(filling) if block_given? 
    end 
end 

# and calling it like this: 
parse_list('/tmp/foobar'){ |filling| 
    Filing.new(filing).import 
} 
+0

activerecord-import (https://github.com/zdennis/activerecord-import)를 사용하여 레코드를 내 Postgres 데이터베이스로 가져올 계획입니다. – user2012677

+0

내가 제공 한 블록 버전을 사용하고 해당 블록 내부에 한 세트의 데이터를 가져올 수 있습니다. 이렇게하면 전체 배열을 메모리에 보관할 필요가 없으며 알고리즘이 훨씬 빠르게 실행됩니다. 물론 이것은 몇백 개의 레코드로는 눈에 띄지 않지만 200k 레코드 이상이면 분명히 가치가 있습니다. – nex

+0

위 참조 편집. – user2012677

관련 문제