Grep -E, Sed -E - hiệu suất thấp khi '[x] {1,9999}' được sử dụng, nhưng tại sao?


9

Khi grephoặc sedđược sử dụng với tùy chọn --extended-regexpvà mẫu {1,9999}là một phần của biểu thức chính được sử dụng, hiệu suất của các lệnh này trở nên thấp. Để rõ ràng hơn, dưới đây được áp dụng một vài thử nghiệm. [1] [2]

  • Hiệu suất tương đối của grep -E, egrepsed -Egần như bằng nhau, do đó, chỉ có thử nghiệm được thực hiện với grep -Eđược cung cấp.

Kiểm tra 1

$ time grep -E '[0-9]{1,99}' < /dev/null

real    0m0.002s

Kiểm tra 2

$ time grep -E '[0-9]{1,9999}' < /dev/null

> real    0m0.494s

Bài kiểm tra 3

$ time grep -E '[0123456789] {1,9999}' </ dev / null

> 21m43.947 thực

Kiểm tra 4

$ time grep -E '[0123456789]+' < /dev/null
$ time grep -E '[0123456789]*' < /dev/null
$ time grep -E '[0123456789]{1,}' < /dev/null
$ time grep -P '[0123456789]{1,9999}' < /dev/null

real    0m0.002s       

Lý do của sự khác biệt đáng kể này của hiệu suất là gì?


3
Đó là một quan sát thú vị - tôi đoán là bạn cần đào sâu vào bên trong của grep để tìm ra chính xác cách nó xây dựng cây phân tích (cũng sẽ rất thú vị khi so sánh [0-9]+)
Steeldo

3
Đầu vào không quan trọng. Như @steel ấn cho thấy, sự chậm lại đi trước phù hợp. Một bài kiểm tra đơn giản hơn time grep -E '[0-9]{1,99}' </dev/nullso với time grep -E '[0-9]{1,9999}' </dev/null. Ngay cả khi không có đầu vào , lệnh thứ hai vẫn chậm (vào ngày 16.04). Như mong đợi, bỏ qua -Evà thoát ra {}hành xử giống nhau và thay thế -Ebằng -Pkhông chậm (PCRE là một công cụ khác). Hầu hết thú vị là bao nhiêu nhanh hơn [0-9] là hơn ., xvà thậm chí [0123456789]. Với bất kỳ trong số đó và {1,9999}, greptiêu thụ một lượng lớn RAM; Tôi không dám để nó chạy quá ~ 10 phút.
Eliah Kagan

3
@ αғsнιη Không, { }được ' 'trích dẫn ; Vỏ chuyển chúng không thay đổi đến grep. Dù sao, {1,9999}sẽ là một mở rộng cú đúp rất nhanh và đơn giản . Vỏ sẽ mở rộng nó ra 1 9999.
Eliah Kagan

4
@ αғsнιη Tôi không biết ý của bạn là gì, nhưng điều này chắc chắn không liên quan gì đến cái vỏ. Trong một lệnh chạy dài, tôi đã sử dụng pstopđể xác minh grepđã thông qua các đối số dự kiến ​​và nó, không bash, tiêu thụ nhiều RAM và CPU. Tôi mong đợi grepsedcả hai đều sử dụng các hàm regex POSIX được triển khai trong libc để khớp BRE / ERE; Tôi thực sự không nên nói về grepthiết kế, ngoại trừ khi các grepnhà phát triển chọn sử dụng thư viện đó.
Eliah Kagan

3
Tôi khuyên bạn nên thay thế các thử nghiệm bằng time grep ... < /dev/null, để mọi người không nhầm lẫn vấn đề thực tế với dữ liệu được cung cấp cho grepvà những thứ không liên quan khác.
muru

Câu trả lời:


10

Lưu ý rằng đó không phải là kết hợp cần có thời gian, mà là việc xây dựng RE. Bạn sẽ thấy rằng nó cũng sử dụng khá nhiều RAM:

$ valgrind grep -Eo '[0-9]{1,9999}' < /dev/null
==6518== HEAP SUMMARY:
==6518==     in use at exit: 1,603,530,656 bytes in 60,013 blocks
==6518==   total heap usage: 123,613 allocs, 63,600 frees, 1,612,381,621 bytes allocated
$ valgrind grep -Eo '[0-9]{1,99}' < /dev/null
==6578==     in use at exit: 242,028 bytes in 613 blocks
==6578==   total heap usage: 1,459 allocs, 846 frees, 362,387 bytes allocated
$ valgrind grep -Eo '[0-9]{1,999}' < /dev/null
==6594== HEAP SUMMARY:
==6594==     in use at exit: 16,429,496 bytes in 6,013 blocks
==6594==   total heap usage: 12,586 allocs, 6,573 frees, 17,378,572 bytes allocated

Số lượng allocs dường như tỷ lệ thuận với số lần lặp, nhưng bộ nhớ được phân bổ dường như tăng theo cấp số nhân.

Đó là cách mà regexps GNU được thực hiện. Nếu bạn biên dịch GNU grepvới CPPFLAGS=-DDEBUG ./configure && makevà chạy các lệnh đó, bạn sẽ thấy hiệu ứng theo cấp số nhân trong hành động. Đi sâu hơn điều đó có nghĩa là trải qua rất nhiều lý thuyết về DFA và đi sâu vào triển khai regrec gnulib.

Tại đây, bạn có thể sử dụng PCRE thay vào đó dường như không có cùng một vấn đề: grep -Po '[0-9]{1,65535}'(tối đa, mặc dù bạn luôn có thể làm những việc như [0-9](?:[0-9]{0,10000}){100}trong 1 đến 1.000.001 lần lặp lại) không tốn nhiều thời gian cũng như bộ nhớ hơn grep -Po '[0-9]{1,2}'.


Có cách nào để làm việc này không?
Sergiy Kolodyazhnyy

3
@SergiyKolodyazhnyy, bạn có thể sử dụng grep -Po '[0-9]{1,9999}mà dường như không có vấn đề gì.
Stéphane Chazelas

1
Nó không phải chỉ trong sed -Ehay grep -E, nhưng trong awkcũng có hiệu suất thấp này (khoảng lệnh awk cuối cùng). có lẽ awkcũng không thể sử dụng PCRE?
αsнι
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.