Câu trả lời của Costas có lẽ là vấn đề chính xác nhất được đưa ra bởi vì bạn có một trường có tỷ lệ trùng khớp 100%.
Nhưng nếu vấn đề của bạn thực sự xảy ra với hàng triệu regexps trong hàng tỷ dòng, thì GNU Parallel có một mô tả về cách thực hiện điều đó: https://www.gnu.org/software/abul/man.html#EXAMPLE:-Grepping -n-lines-for-m-normal-biểu thức
Giải pháp đơn giản nhất để grep một tệp lớn cho nhiều regexps là:
grep -f regexps.txt bigfile
Hoặc nếu biểu thức chính là các chuỗi cố định:
grep -F -f regexps.txt bigfile
Có 3 yếu tố giới hạn: CPU, RAM và I / O đĩa.
RAM rất dễ đo: Nếu quá trình grep chiếm phần lớn bộ nhớ trống của bạn (ví dụ: khi chạy trên cùng), thì RAM là một yếu tố hạn chế.
CPU cũng dễ dàng đo lường: Nếu grep chiếm> 90% CPU ở trên cùng, thì CPU là một yếu tố hạn chế và song song hóa sẽ tăng tốc độ này.
Khó có thể xem liệu I / O của đĩa là yếu tố giới hạn hay không, và tùy thuộc vào hệ thống đĩa, nó có thể nhanh hơn hoặc chậm hơn để song song hóa. Cách duy nhất để biết chắc chắn là kiểm tra và đo lường.
Yếu tố giới hạn: RAM
Bigfile grep -f regexs.txt bình thường hoạt động bất kể kích thước của bigfile, nhưng nếu regexps.txt quá lớn, nó không thể vừa với bộ nhớ, thì bạn cần phải tách nó ra.
grep -F chiếm khoảng 100 byte RAM và grep mất khoảng 500 byte RAM cho mỗi 1 byte regrec. Vì vậy, nếu regexps.txt là 1% RAM của bạn, thì nó có thể quá lớn.
Nếu bạn có thể chuyển đổi biểu thức chính quy của mình thành các chuỗi cố định, hãy làm điều đó. Ví dụ: nếu các dòng bạn đang tìm kiếm trong bigfile tất cả trông giống như:
ID1 foo bar baz Identifier1 quux
fubar ID2 foo bar baz Identifier2
sau đó regexps.txt của bạn có thể được chuyển đổi từ:
ID1.*Identifier1
ID2.*Identifier2
vào:
ID1 foo bar baz Identifier1
ID2 foo bar baz Identifier2
Bằng cách này, bạn có thể sử dụng grep -F, chiếm ít hơn 80% bộ nhớ và nhanh hơn nhiều.
Nếu nó vẫn không vừa trong bộ nhớ, bạn có thể làm điều này:
parallel --pipepart -a regexps.txt --block 1M grep -F -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
1M nên là bộ nhớ trống của bạn chia cho số lõi và chia cho 200 cho grep -F và 1000 cho grep bình thường. Trên GNU / Linux, bạn có thể làm:
free=$(awk '/^((Swap)?Cached|MemFree|Buffers):/ { sum += $2 }
END { print sum }' /proc/meminfo)
percpu=$((free / 200 / $(parallel --number-of-cores)))k
parallel --pipepart -a regexps.txt --block $percpu --compress grep -F -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
Nếu bạn có thể sống với các dòng trùng lặp và thứ tự sai, thì nhanh hơn để làm:
parallel --pipepart -a regexps.txt --block $percpu --compress grep -F -f - bigfile
Yếu tố giới hạn: CPU
Nếu CPU là song song hệ số giới hạn thì nên thực hiện trên biểu thức chính quy:
cat regexp.txt | parallel --pipe -L1000 --round-robin --compress grep -f - -n bigfile |
sort -un | perl -pe 's/^\d+://'
Lệnh sẽ khởi động một grep trên mỗi CPU và đọc bigfile một lần cho mỗi CPU, nhưng vì điều đó được thực hiện song song, tất cả các lần đọc ngoại trừ lần đầu tiên sẽ được lưu trong bộ nhớ cache. Tùy thuộc vào kích thước của regrec.txt, có thể sử dụng nhanh hơn - chặn 10m thay vì -L1000.
Một số hệ thống lưu trữ hoạt động tốt hơn khi đọc song song nhiều khối. Điều này đúng với một số hệ thống RAID và một số hệ thống tệp mạng. Để song song việc đọc bigfile:
parallel --pipepart --block 100M -a bigfile -k --compress grep -f regexp.txt
Điều này sẽ chia bigfile thành các khối 100MB và chạy grep trên mỗi khối này. Để song song cả việc đọc bigfile và regapi.txt, hãy kết hợp cả hai bằng cách sử dụng --fifo:
parallel --pipepart --block 100M -a bigfile --fifo cat regexp.txt \
\| parallel --pipe -L1000 --round-robin grep -f - {}
Nếu một dòng khớp với nhiều biểu thức chính, dòng có thể được nhân đôi.
Vấn đề lớn hơn
Nếu vấn đề quá lớn để giải quyết bằng cách này, có lẽ bạn đã sẵn sàng cho Lucene.