Tôi tự hỏi liệu có thể làm bất cứ điều gì hiệu quả hơn là giải nén từ khi bắt đầu tập tin cho đến khi. Có vẻ như câu trả lời là không. Tuy nhiên, trên một số CPU (Skylake) zcat | tail
không tăng tốc CPU lên tới tốc độ xung nhịp hoàn toàn. Xem bên dưới. Một bộ giải mã tùy chỉnh có thể tránh được vấn đề đó và lưu các cuộc gọi hệ thống ghi đường ống, và có thể nhanh hơn ~ 10%. (Hoặc nhanh hơn ~ 60% trên Skylake nếu bạn không điều chỉnh cài đặt quản lý năng lượng).
Điều tốt nhất bạn có thể làm với một zlib tùy chỉnh với skipbytes
chức năng là phân tích các ký hiệu trong một khối nén để đi đến cuối cùng mà không thực hiện công việc thực sự tái tạo lại khối giải nén. Điều này có thể nhanh hơn đáng kể (có thể ít nhất là gấp 2 lần) so với việc gọi hàm giải mã thông thường của zlib để ghi đè lên cùng bộ đệm và di chuyển về phía trước trong tệp. Nhưng tôi không biết có ai viết một chức năng như vậy không. (Và tôi nghĩ rằng điều này không thực sự hoạt động trừ khi tệp được viết đặc biệt để cho phép bộ giải mã khởi động lại ở một khối nhất định).
Tôi đã hy vọng có một cách để bỏ qua các khối Deflate mà không giải mã chúng, bởi vì điều đó sẽ nhanh hơn nhiều . Cây Huffman được gửi ở đầu mỗi khối, vì vậy bạn có thể giải mã từ đầu của bất kỳ khối nào (tôi nghĩ). Ồ, tôi nghĩ rằng trạng thái bộ giải mã nhiều hơn cây Huffman, nó cũng là 32kiB dữ liệu được giải mã trước đó và điều này không được đặt lại / quên theo ranh giới khối theo mặc định. Các byte giống nhau có thể tiếp tục được tham chiếu nhiều lần, do đó, chỉ có thể xuất hiện một lần theo nghĩa đen trong một tệp nén khổng lồ. (ví dụ: trong tệp nhật ký, tên máy chủ có thể vẫn "nóng" trong từ điển nén toàn bộ thời gian và mọi phiên bản của nó đều tham chiếu đến tệp trước đó, không phải tên đầu tiên).
Các zlib
nhãn hiệu nói rằng bạn phải sử dụng Z_FULL_FLUSH
khi gọi deflate
nếu bạn muốn dòng nén được seekable đến thời điểm đó. Nó "đặt lại trạng thái nén", vì vậy tôi nghĩ rằng không có điều đó, các tham chiếu ngược có thể đi vào (các) khối trước đó. Vì vậy, trừ khi tệp zip của bạn được viết với các khối hoàn toàn không thường xuyên (như mọi 1G hoặc thứ gì đó sẽ có tác động không đáng kể đến việc nén), tôi nghĩ bạn sẽ phải thực hiện nhiều công việc giải mã đến điểm bạn muốn hơn ban đầu Suy nghĩ. Tôi đoán có lẽ bạn không thể bắt đầu khi bắt đầu bất kỳ khối nào.
Phần còn lại của điều này đã được viết trong khi tôi nghĩ rằng có thể chỉ cần tìm phần bắt đầu của khối chứa byte đầu tiên bạn muốn và giải mã từ đó.
Nhưng thật không may, sự khởi đầu của một khối Deflate không cho biết thời gian của nó là bao lâu . Dữ liệu không nén được có thể được mã hóa bằng loại khối không nén có kích thước 16 bit tính theo byte ở phía trước, nhưng các khối được nén không: RFC 1951 mô tả định dạng khá dễ đọc . Các khối có mã hóa Huffman động có cây ở phía trước khối (vì vậy bộ giải nén không phải tìm trong luồng), vì vậy máy nén phải giữ toàn bộ khối (đã nén) trong bộ nhớ trước khi ghi.
Khoảng cách tham chiếu ngược tối đa chỉ là 32kiB, do đó, máy nén không cần giữ nhiều dữ liệu không nén trong bộ nhớ, nhưng điều đó không giới hạn kích thước khối. Các khối có thể dài nhiều megabyte. (Điều này đủ lớn để đĩa tìm kiếm giá trị ngay cả trên ổ đĩa từ, so với đọc tuần tự vào bộ nhớ và chỉ bỏ qua dữ liệu trong RAM, nếu có thể tìm thấy phần cuối của khối hiện tại mà không cần phân tích cú pháp qua nó).
zlib tạo các khối càng lâu càng tốt:
Theo Marc Adler , zlib chỉ bắt đầu một khối mới khi bộ đệm biểu tượng lấp đầy, với cài đặt mặc định là 16.383 ký hiệu (bằng chữ hoặc khớp)
Tôi đã nén đầu ra của seq
(rất dư thừa và do đó có thể không phải là một thử nghiệm tuyệt vời), nhưng pv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
trên đó chỉ chạy với ~ 62 MiB / s dữ liệu nén trên Skylake i7-6700k ở tốc độ 3.9 GHz, với RAM DDR4-2666. Đó là dữ liệu được giải nén là 246MiB / s, là thay đổi lớn so với memcpy
tốc độ ~ 12 GiB / s đối với kích thước khối quá lớn để phù hợp với bộ đệm.
(Được energy_performance_preference
đặt thành mặc định balance_power
thay vì balance_performance
, bộ điều khiển CPU bên trong của Skylake quyết định chỉ chạy ở tốc độ 2,7 GHz, ~ 43 MiB / giây dữ liệu nén. Tôi sử dụng sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
để điều chỉnh nó. Có lẽ các cuộc gọi hệ thống thường xuyên như vậy không giống như giới hạn CPU thực làm việc cho đơn vị quản lý điện năng.)
TL: DR: zcat | tail -c
là CPU bị ràng buộc ngay cả trên CPU nhanh, trừ khi bạn có đĩa rất chậm. gzip đã sử dụng 100% CPU mà nó chạy (và chạy 1,81 lệnh trên mỗi đồng hồ, theo perf
) và tail
sử dụng 0,162 CPU mà nó chạy trên (0,58 IPC). Hệ thống chủ yếu là không hoạt động.
Tôi đang sử dụng Linux 4.14.11-1-ARCH, có KPTI được bật theo mặc định để hoạt động xung quanh Meltdown, vì vậy tất cả các write
cuộc gọi hệ thống đó gzip
đều đắt hơn so với trước đây: /
Việc tìm kiếm tích hợp sẵn unzip
hoặc zcat
(nhưng vẫn sử dụng zlib
chức năng giải mã thông thường ) sẽ lưu tất cả các ghi đường ống đó và sẽ khiến CPU Skylake chạy ở tốc độ xung nhịp hoàn toàn. (Việc ép xung này đối với một số loại tải là duy nhất đối với Intel Skylake và sau này, đã giảm tải việc ra quyết định tần số CPU từ HĐH, vì chúng có nhiều dữ liệu hơn về những gì CPU đang làm và có thể tăng / giảm nhanh hơn. Đây là thông thường tốt, nhưng ở đây dẫn đến Skylake không tăng tốc tối đa với một thiết lập thống đốc bảo thủ hơn).
Không có cuộc gọi hệ thống, chỉ cần viết lại bộ đệm phù hợp với bộ đệm L2 cho đến khi bạn đạt đến vị trí byte bắt đầu mà bạn muốn, có thể sẽ tạo ra một vài% khác biệt ít nhất. Có thể thậm chí 10%, nhưng tôi chỉ chiếm số ở đây. Tôi chưa zlib
tìm hiểu chi tiết nào để xem mức độ lớn của bộ nhớ cache và mức độ xả TLB (và do đó là xóa bộ nhớ cache) trên mỗi cuộc gọi hệ thống bị tổn thương khi bật KPTI.
Có một vài dự án phần mềm thêm chỉ mục tìm kiếm vào định dạng tệp gzip . Điều này không giúp ích gì cho bạn nếu bạn không thể khiến bất cứ ai tạo các tệp nén có thể tìm kiếm cho bạn, nhưng những người đọc khác trong tương lai có thể có lợi.
Có lẽ không ai trong số các dự án này có một chức năng giải mã mà biết làm thế nào để bỏ qua một dòng Deflate mà không có một chỉ số, bởi vì họ chỉ được thiết kế để làm việc khi một chỉ số là có sẵn.