Tập tin nhanh python io

Tôi đang viết một tập lệnh python nhỏ để theo dõi các sự kiện và thông báo khác nhau. Nó sử dụng một tệp phẳng làm chỉ mục, mỗi bản ghi có cùng kích thước và chứa thông tin chi tiết về từng thông báo

Tệp này có thể lớn, theo thứ tự vài trăm megabyte. Mã python là tầm thường, với điều kiện là mỗi bản ghi có cùng kích thước, nhưng cách nhanh nhất để truy cập và sử dụng tệp chỉ mục đó là gì?

Với python [hoặc bất kỳ ngôn ngữ lập trình nào, với giá trị nào], tôi có nhiều cách để đọc một tệp

  • Tôi chỉ có thể dựa vào
    $ time python /tmp/snippets/snippet00.py ./test-index.bin
    93368320
    real    0m54.960s
    user    0m53.715s
    sys     0m0.620s
    
    7 và
    $ time python /tmp/snippets/snippet00.py ./test-index.bin
    93368320
    real    0m54.960s
    user    0m53.715s
    sys     0m0.620s
    
    8 trong python có bộ đệm hoàn toàn tốt và chỉ
    $ time python /tmp/snippets/snippet00.py ./test-index.bin
    93368320
    real    0m54.960s
    user    0m53.715s
    sys     0m0.620s
    
    7 [hoặc
    $ time python /tmp/snippets/snippet00.py ./test-index.bin
    93368320
    real    0m54.960s
    user    0m53.715s
    sys     0m0.620s
    
    8] một bản ghi tại một thời điểm
  • Tôi có thể đọc nó trong một lần và sau đó thao tác trong bộ nhớ [ví dụ: một
    $ time python /tmp/snippets/snippet00.py ./test-index.bin
    93368320
    real    0m54.960s
    user    0m53.715s
    sys     0m0.620s
    
    7 duy nhất trong một chuỗi, tiếp theo là sử dụng độ lệch trong chuỗi]
  • Tôi có thể thực hiện lưu vào bộ đệm của riêng mình, đọc một đoạn lớn mỗi lần và sau đó thao tác trên từng đoạn dưới dạng một tập hợp bản ghi [ví dụ: nhiều
    $ time python /tmp/snippets/snippet00.py ./test-index.bin
    93368320
    real    0m54.960s
    user    0m53.715s
    sys     0m0.620s
    
    7 của một số bội số của kích thước bản ghi, tiếp theo là sử dụng độ lệch trong mỗi đoạn]
  • Tôi có thể sử dụng các thư viện đẹp hơn cho phép tôi
    $ time cat ./test-index.bin &> /dev/null
    real    0m0.531s
    user    0m0.016s
    sys     0m0.512s
    
    3 hoặc sử dụng một số cách tiếp cận điên rồ khác

và thậm chí nhiều cách hơn để phân tích nó. Hiện tại, tôi chỉ muốn tập trung vào phần đọc, vì trong trường hợp của tôi, việc phân tích cú pháp là một

$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
4 hoặc
$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
5 đơn giản

Nhiệm vụ

từ chối trách nhiệm. hầu hết các ví dụ dưới đây được đơn giản hóa quá mức và không đặc biệt đẹp. Đôi khi tôi nên sử dụng cấu trúc

$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
6, kiểm tra
$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
7 hoặc ưa thích các hoạt động của
$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
8 hơn hoạt động của
$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
9. Tôi đã cố gắng tránh mọi phiền nhiễu bằng cách giữ cho các ví dụ càng giống nhau càng tốt. Xin vui lòng chịu đựng tôi, và giữ cảm xúc trăn trở của bạn, nếu bạn có thể. ], nhưng vui lòng cung cấp đề xuất nếu bạn có bất kỳ

Câu hỏi đầu tiên tôi muốn trả lời là "việc giải quyết vấn đề này có đáng để dành thời gian cho nó không? Tại sao tôi không thể viết mã đơn giản nhất, sử dụng

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
7, và hoàn thành nó?"

Vì vậy, đó là nơi tôi bắt đầu từ

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
4

Bộ đếm, in i, chỉ để xác minh rằng tất cả các bản ghi đã được đọc chính xác

Tệp tôi đã sử dụng cho điểm chuẩn là khoảng 1. Kích thước 4Gb

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
5

Hệ thống của tôi có khoảng 4 Gb ram, chỉ /dev/null real 0m0.531s user 0m0.016s sys 0m0.512s 9, vì vậy hãy thử nó

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
62

Thử nghiệm đầu tiên không thực sự suôn sẻ, điều này còn tệ hơn bất kỳ điều gì tôi từng thấy trước đây

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
63

Bằng cách nhìn vào strace, có vẻ như nó đang thực hiện rất nhiều

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
17, điều này có thể có nghĩa là đọc vào bộ đệm, sau đó sao chép vào mảng phụ 16 byte

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
64

Cái nào có vẻ chậm hơn đọc thành một chuỗi đơn giản?

Hãy thử với một mảng phụ lớn hơn, chẳng hạn như 1048576 và xem điều gì sẽ xảy ra thay vào đó

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
65

thời gian chạy là bây giờ

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
66

4 giây, tương đương với những gì tôi có trước đây với số lần đọc lớn

Nếu tôi nhìn với sự bối rối, chủ yếu là vì tò mò, thì đây là những gì sẽ xảy ra

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
67

có vẻ như nó đang đọc trực tiếp vào mảng phụ

Điều đáng ngạc nhiên là

$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
3. Như đã chỉ ra trên luồng reddit,
$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
3 và
$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
30 được sử dụng để cấp phát bộ nhớ [các cờ biểu thị bộ nhớ ẩn danh và bộ mô tả tệp là -1]. Điều này có thể đơn giản liên quan đến lệnh gọi
$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
31 trong linux glibc, theo trang hướng dẫn sẽ sử dụng
$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
3 thay vì
$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
33 cho bất kỳ phân bổ nào lớn hơn
$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
34, theo mặc định được đặt thành 128 k

Điều tôi thấy nghi ngờ ở đây là nếu bạn nhìn vào

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
79, bạn sẽ thấy một mô hình liên tục của
$ time cat ./test-index.bin &> /dev/null
real    0m0.531s
user    0m0.016s
sys     0m0.512s
3 và
$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
30. Điều này có thể có nghĩa là một
$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
38 theo sau là một
$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
39 ngay sau đó, nhưng tôi đã mong đợi một bộ cấp phát bộ nhớ tốt sẽ phân bổ "bộ đệm" trong quy trình một thời gian trước khi trả lại bộ nhớ cho hệ điều hành, mặc dù tôi đã xem xét một thời gian rồi

Vì vậy, không thực sự nhìn vào mã python, đây là lý thuyết của tôi về nội bộ

  • Nếu "bytearray > buffer", hãy đọc trực tiếp vào bytearray
  • Mặt khác, đọc vào bộ đệm có kích thước được định cấu hình và sao chép dữ liệu được yêu cầu vào bytearray

Hãy thử với một mảng 8192 byte, gấp đôi kích thước bộ đệm

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
68

Từ strace, đây là lần đọc 8k, có lẽ là trực tiếp vào mảng phụ, với thời gian chạy

Hãy thử lại một lần nữa, với tính năng đệm bị vô hiệu hóa [lưu ý 0 trong io. mở]

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
69

Với strace, bây giờ tôi có thể thấy rõ ràng rằng python đang đọc trực tiếp 16 byte cùng một lúc

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
0

Tuy nhiên, điều này chậm hơn nhiều so với tôi mong đợi

$ time python /tmp/snippets/snippet00.py ./test-index.bin
93368320
real    0m54.960s
user    0m53.715s
sys     0m0.620s
1

Lưu ý thời gian sys ở 54 giây, có thể do các cuộc gọi đọc lặp đi lặp lại. Thời gian 1 phút 10 giây của người dùng có lẽ là số lần lặp cao?

Chủ Đề