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.512s4 hoặc
$ time cat ./test-index.bin &> /dev/null real 0m0.531s user 0m0.016s sys 0m0.512s5 đơ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.512s6, kiểm tra
$ time cat ./test-index.bin &> /dev/null real 0m0.531s user 0m0.016s sys 0m0.512s7 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.512s8 hơn hoạt động của
$ time cat ./test-index.bin &> /dev/null real 0m0.531s user 0m0.016s sys 0m0.512s9. 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.620s7, 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.620s4
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.620s5
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.620s62
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.620s63
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.620s17, đ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.620s64
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.620s65
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.620s66
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.620s67
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.512s3. Như đã chỉ ra trên luồng reddit,
$ time cat ./test-index.bin &> /dev/null real 0m0.531s user 0m0.016s sys 0m0.512s3 và
$ time python /tmp/snippets/snippet00.py ./test-index.bin 93368320 real 0m54.960s user 0m53.715s sys 0m0.620s30 đượ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.620s31 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.512s3 thay vì
$ time python /tmp/snippets/snippet00.py ./test-index.bin 93368320 real 0m54.960s user 0m53.715s sys 0m0.620s33 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.620s34, 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.620s79, 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.512s3 và
$ time python /tmp/snippets/snippet00.py ./test-index.bin 93368320 real 0m54.960s user 0m53.715s sys 0m0.620s30. Đ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.620s38 theo sau là một
$ time python /tmp/snippets/snippet00.py ./test-index.bin 93368320 real 0m54.960s user 0m53.715s sys 0m0.620s39 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.620s68
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.620s69
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.620s0
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.620s1
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?