2009-07-24 34 views
7

Tôi có một số tập lệnh hiện đang đọc trong rất nhiều dữ liệu từ một số tệp .CSV. Để có hiệu quả, tôi sử dụng mô-đun Text::CSV_XS để đọc chúng và sau đó tạo một băm bằng cách sử dụng một trong các cột làm chỉ mục. Tuy nhiên, tôi có một số tệp và chúng khá lớn. Và mỗi tập lệnh cần đọc trong dữ liệu một lần nữa.Làm cách nào để truy cập dữ liệu trong nhiều tệp CSV lớn một cách nhanh chóng từ Perl?

Câu hỏi đặt ra là: Làm thế nào tôi có thể lưu trữ liên tục các băm Perl này để tất cả chúng có thể được đọc lại với tối thiểu là CPU?

Kết hợp tập lệnh không phải là một tùy chọn. Tôi muốn ...

tôi áp dụng quy tắc 2 của tối ưu hóa và được sử dụng profiling để thấy rằng đại đa số các CPU (khoảng 90%) là ở:

Text::CSV_XS::fields 
Text::CSV_XS::Parse 
Text::CSV_XS::parse 

Vì vậy, tôi đã thực hiện một kịch bản thử nghiệm đọc tất cả các tệp .CSV (Văn bản :: CSV_XS), đã bỏ chúng bằng cách sử dụng mô-đun Storable, sau đó quay lại và đọc lại bằng cách sử dụng mô-đun Storable. Tôi cấu hình này vì vậy tôi có thể nhìn thấy thời gian CPU:

$ c:/perl/bin/dprofpp.bat 
Total Elapsed Time = 1809.397 Seconds 
    User+System Time = 950.5560 Seconds 
Exclusive Times 
%Time ExclSec CumulS #Calls sec/call Csec/c Name 
25.6 243.6 243.66 126 1.9338 1.9338 Storable::pretrieve 
20.5 194.9 194.92 893448 0.0002 0.0002 Text::CSV_XS::fields 
9.49 90.19 90.198 893448 0.0001 0.0001 Text::CSV_XS::Parse 
7.48 71.07 71.072 126 0.5641 0.5641 Storable::pstore 
4.45 42.32 132.52 893448 0.0000 0.0001 Text::CSV_XS::parse 
(the rest was in terms of 0.07% or less and can be ignored) 

Vì vậy, sử dụng storable chi phí khoảng 25,6% để tải lại so với chữ :: CSV_XS vào khoảng 35%. Không có nhiều khoản tiết kiệm ...

Có ai có đề xuất về cách tôi có thể đọc trong các dữ liệu này hiệu quả hơn không?

Cảm ơn sự giúp đỡ của bạn.

Trả lời

9

Phân tích cú pháp dữ liệu một lần và đặt nó vào một số SQLite db. Truy vấn bằng cách sử dụng DBI.

+0

Yours và friedo's có được những ngón tay cái từ tôi. – Axeman

+0

@Axeman Cảm ơn bạn. –

+0

Đó là cách để đi nếu bạn không cần quyền truy cập ghi. –

2

Không thể kéo toàn bộ danh sách vào bộ nhớ mỗi khi bạn chạy tập lệnh. Sử dụng cơ sở dữ liệu trên đĩa sẽ cho phép bạn thực hiện việc này. Nếu vì một lý do nào đó, bạn phải chạm vào từng mục nhập trong tệp CSV mỗi khi bạn chạy, tôi có thể khuyên bạn nên lưu trữ nó trên một đĩa RAM thay vì đĩa vật lý. Nó rõ ràng là phù hợp trong bộ nhớ, tôi không nghĩ rằng bạn sẽ nhận được cải thiện nhiều bằng cách thay đổi định dạng trên đĩa bạn lưu trữ nó. Cách duy nhất để thực sự tăng tốc nó là lưu trữ nó trên một phương tiện nhanh hơn.

11

Cách dễ nhất để đặt một băm rất lớn trên đĩa, IMHO, là với BerkeleyDB. Nó nhanh chóng, được kiểm tra thời gian và chắc chắn, và mô-đun CPAN cung cấp một API gắn kết. Điều đó có nghĩa là bạn có thể tiếp tục sử dụng băm của bạn như thể nó là một cấu trúc dữ liệu trong bộ nhớ, nhưng nó sẽ tự động đọc và ghi thông qua BerkeleyDB vào đĩa.

1

Nếu bạn chỉ cần truy cập một phần dữ liệu trong mỗi tập lệnh, thay vì TẤT CẢ, DBM::Deep có lẽ là cách tốt nhất cho bạn.

Đĩa/IO có thể là nút cổ chai lớn nhất của bạn bất kể bạn làm gì. Có lẽ bạn có thể sử dụng một nhà cung cấp dữ liệu giữ tất cả dữ liệu có sẵn trong bộ nhớ đệm đã được định sẵn - sử dụng một cái gì đó như Sys::Mmap::Simple Tôi chưa bao giờ cần làm điều này, vì vậy tôi không có nhiều thứ khác để cung cấp.

+0

Giải thích * tại sao * DBM :: Deep sẽ tốt hơn cho việc truy cập một phần dữ liệu, vui lòng? – ysth

+1

DBM :: Deep là một mô-đun đẹp: Hãy suy nghĩ về nó theo nghĩa đen lưu trữ cấu trúc dữ liệu Perl trên đĩa mà không cần phải deserialize * toàn bộ * DB như với Storable. Điều đó đang được nói, nó rất, rất chậm nếu bạn cần bất kỳ phần đáng kể nào của dữ liệu. Nó đặt thuận tiện hơn hiệu suất. – tsee

3

Vâng, tôi đã thực hiện đề xuất của Sinan Ünür (cảm ơn!) Và thực hiện một cơ sở dữ liệu SQLite và chạy lại chương trình thử nghiệm của tôi để so sánh nhận dữ liệu thông qua các tập tin CSV so với nhận được dữ liệu ra khỏi cơ sở dữ liệu SQLite:

$ c:/perl/bin/dprofpp.bat 
Total Elapsed Time = 1705.947 Seconds 
    User+System Time = 1084.296 Seconds 
Exclusive Times 
%Time ExclSec CumulS #Calls sec/call Csec/c Name 
19.5 212.2 212.26 893448 0.0002 0.0002 Text::CSV_XS::fields 
15.7 170.7 224.45 126 1.3549 1.7814 DBD::_::st::fetchall_hashref 
9.14 99.15 99.157 893448 0.0001 0.0001 Text::CSV_XS::Parse 
6.03 65.34 164.49 893448 0.0001 0.0002 Text::CSV_XS::parse 
4.93 53.41 53.412 893574 0.0001 0.0001 DBI::st::fetch 
    [ *removed the items of less than 0.01 percent* ] 

Tổng cho CSV_XS là 34,67% so với đến 20,63% cho SQLite, phần nào tốt hơn so với giải pháp Storable mà tôi đã thử trước đây. Tuy nhiên, đây không phải là một sự so sánh công bằng vì với giải pháp CSV_XS tôi phải tải toàn bộ tệp CSV nhưng với giao diện SQLite, tôi chỉ có thể tải các phần tôi muốn. Vì vậy, trong thực tế, tôi mong đợi nhiều cải tiến hơn so với các thử nghiệm đơn giản này.

Tôi chưa thử sử dụng BerkeleyDB (xin lỗi, friedo) thay vì SQLite, chủ yếu là vì tôi không thấy đề xuất đó cho đến khi tôi tham gia tốt vào việc thử dùng SQLite. Thiết lập thử nghiệm là một nhiệm vụ không tầm thường vì tôi hầu như không bao giờ có dịp sử dụng cơ sở dữ liệu SQL.

Tuy nhiên, giải pháp rõ ràng là tải tất cả dữ liệu vào cơ sở dữ liệu và truy cập thông qua mô-đun DBI. Cảm ơn sự giúp đỡ của mọi người. Tất cả các câu trả lời đều được đánh giá cao.

+0

@Harold Cảm ơn bạn đã chấp nhận câu trả lời của tôi, nhưng, quan trọng hơn, cảm ơn bạn rất nhiều vì một bản tóm tắt tốt đẹp với các con số thực tế. –

Các vấn đề liên quan