Công cụ `uniq` nhanh nhất trong linux


8

Tôi có tệp văn bản lớn (1,5 G),

Tôi muốn biết công cụ nhanh nhất và đáng tin cậy hơn trong Linux là gì.

Tôi thường sử dụng:

awk '!x[$0]++' file.txt

Nhưng khi tôi sử dụng htoplệnh tôi thấy việc sử dụng bộ nhớ của tôi đang tăng lên.

Tôi muốn biết đâu là cái nhanh nhất và đáng tin cậy hơn cho các tệp lớn.

uniq?
sort?
sed?
awk?

Tại sao?


Bạn đã thử chạy chúng, có thể với time?
choroba

thời gian là quan trọng và cũng là sử dụng bộ nhớ và độ tin cậy (ý tôi là cái nào thực hiện công việc của mình một cách chính xác)
MLSC

Chưa ... Nhưng tôi đã thực hiện một số thử nghiệm trước đây ... và hỏi một nơi nào đó, một số người nói với tôi rằng awk là tốt nhất..nhưng trong htop ... Tôi thấy việc sử dụng bộ nhớ đang tăng lên
MLSC

3
@MortezaLSC: Đó là một sự đánh đổi. Chương trình càng nhanh thì càng sử dụng nhiều bộ nhớ.
cuonglm

Câu trả lời:


16

Hãy xem xét cách mỗi giải pháp hoạt động.

  • uniqĐiều này đòi hỏi các tập tin đã được sắp xếp. Nếu không, bạn phải chuyển nó qua sortđầu tiên, điều đó có nghĩa là sortphải đọc toàn bộ tệp vào bộ nhớ, sắp xếp lại nó ( O(n log n)), sau đó ghi nó vào ống. Công việc của uniqrất rẻ, vì nó chỉ phải so sánh các dòng liền kề của đầu vào.

  • sort -uĐiều này kết hợp công việc của sort | uniq. Điều này phải thu thập tất cả các đầu vào duy nhất vào bộ nhớ giống như awktập lệnh, nhưng sau đó cũng lãng phí thời gian sắp xếp chúng trước khi tạo đầu ra. Đây là O(n log n), mặc dù trong trường hợp nnày là số lượng các mục duy nhất, không phải tất cả các đầu vào. Vì vậy, nó tốt hơn so với đường ống.

  • sedTôi không chắc tại sao bạn liệt kê điều này, vì tôi không thể nghĩ ra một cách hay để làm điều này với sedtất cả. Có thể nếu trước tiên bạn sắp xếp nó và chuyển thành một sedkịch bản, có một cách để so sánh các dòng liền kề. Vì vậy, sedsẽ chỉ là làm những gì uniqlàm, và uniqcó thể làm điều đó về hiệu quả nhất có thể.

  • awkĐây có thể là tốt nhất bởi vì nó chỉ thực hiện số lượng công việc tối thiểu cần thiết. Khi nó đọc từng dòng, nó thực hiện tra cứu hàm băm hiệu quả để xem dòng đó đã có trong bộ nhớ chưa và chỉ lưu các dòng duy nhất dưới dạng khóa băm và bộ đếm làm giá trị. (Nếu dòng không xuất hiện trước đó, điều kiện sẽ là đúng, do đó, dòng sẽ được in. Nếu không, nó sẽ không.) Điều này sử dụng O(n)thời gian và O(uniq n)bộ nhớ.

Mọi phương pháp sẽ sử dụng một lượng bộ nhớ đáng kể, để sắp xếp đầu vào hoặc theo dõi những đầu vào nào đã thấy để chúng có thể loại bỏ trùng lặp.


1
+1 Giải thích liên quan awkcũng giải thích lý do tại sao nó sử dụng số lượng bộ nhớ ngày càng tăng. Bất cứ điều gì sắp xếp cũng sẽ làm điều này, chỉ 1) nó có thể sẽ sử dụng tất cả cùng một lúc, 2) nó có thể sử dụng nhiều hơn một chút, tùy thuộc vào số lượng khóa duy nhất so với khóa trùng lặp.
goldilocks

@Barmar xin lỗi, nhưng khi tôi có một tệp lớn (16 G) với dung lượng bộ nhớ 8G, vậy điều gì sẽ xảy ra với bộ nhớ của tôi?
MLSC

8
@goldilocks, viện sortđến các tệp tạm thời (một cách thông minh) để tránh lấp đầy bộ nhớ. Sử dụng bộ nhớ của nó bị ràng buộc. Ranh giới có thể tùy chỉnh với một số triển khai sắp xếp. Hiệu quả hơn là cho phép hệ thống hoán đổi bộ nhớ ngẫu nhiên vào đĩa (điều này cũng ảnh hưởng đến cả các ứng dụng trên hệ thống).
Stéphane Chazelas

Đúng. Vì vậy, nếu bạn gặp phải trường hợp awkhết bộ nhớ, sortcó thể là giải pháp duy nhất vì nó đã được thiết kế để giải quyết vấn đề này. Mặt khác, tất cả những gì đọc và ghi đĩa sẽ làm chậm nó, vì vậy có lẽ sẽ mất nhiều thời gian để hoàn thành. Nếu bạn đang xử lý một lượng lớn dữ liệu như vậy, có lẽ bạn nên sử dụng DBMS thay vì các tệp văn bản.
Barmar

@Barmar Làm thế nào bạn suy luận rằng thời gian sắp xếp lại tăng lên như O(n log n)thế nào? Hay chỉ là bạn biết nó từ nơi khác?
jimmij


0

Tôi chỉ muốn chỉ ra rằng gnu uniqdường như rất chậm, ngay cả trong một danh sách được sắp xếp.

Tôi vừa thử nhận danh sách các tiền tố thư mục từ danh sách tên tệp được sắp xếp:

$ pv all_files | cut -d '/' -f 1,2,3,4 | uniq > all_prefixes

36.7GiB 0:07:41 [81.4MiB/s]

$ pv all_files | cut -d '/' -f 1,2,3,4 | sort -u > all_prefixes2

36.7GiB 0:03:14 [ 193MiB/s]

$ pv all_files  | cut -d '/' -f 1,2,3,4 | awk '!x[$0]++' > all_prefixes3                                        
36.7GiB 0:02:18 [ 270MiB/s] 

sort -u có vẻ nhanh gấp đôi so với uniq, và điều này là với việc đọc sắp xếp từ stdin và viết đến stdout, vì vậy tôi không thấy nó thực hiện bất kỳ sự song song nào. Tôi không biết tại sao uniq nên chậm hơn rất nhiều sau đó sắp xếp, vì nó không phải sắp xếp danh sách ...

Outpuf của lệnh này là rất nhỏ (có rất nhiều bản sao), chỉ 264kb và sắp xếp chấm dứt ngay lập tức sau khi pv được thực hiện.

Tốc độ tương tự vẫn duy trì nếu bạn xoay quanh thứ tự các lệnh, luồng của tôi bị giới hạn bởi thời gian cpu ở đây, không phải truy cập đĩa và bộ nhớ cache (tôi chỉ có 8GB RAM và trao đổi của tôi không được sử dụng)

Tôi đang chạy cái này trên máy fedora 31 với loại gnu coreutils và uniq và gnu awk; ngôn ngữ được đặt thành en_US.UTF-8

CẬP NHẬT , vì điều này gây tò mò cho tôi khá nhiều, tôi đã thực hiện thêm một số thử nghiệm, hãy để phần cắt ra khỏi đường đi và đảm bảo tệp được sắp xếp độc đáo

cat all_files | cut -d '/' -f 1,2,3,4 | sort -T . > test

Điều này mất 8.4 phút. kiểm tra bây giờ là 7,9GB lớn

Hãy chạy các công cụ này trên tệp thay vì trong một đường ống, điều này sẽ cho phép các công cụ này thực hiện một số tối ưu hóa hơn, như sắp xếp sẽ đa luồng. và cũng từ một ssd nhanh hơn.

Bạn có thể không nhận thấy rằng sắp xếp cũng chiếm rất nhiều bộ nhớ, vì nó thực hiện các thủ thuật thông minh với các tệp tạm thời trong / tmp có thể là tmpfs và sẽ nằm trong ram của bạn (Hãy thử sắp xếp một tệp lớn hơn sau đó / tmp, bạn sẽ chạy vào không gian các vấn đề, đó là lý do tại sao tôi cần cờ -T. trong lệnh trên)

$ time sort -u test > /dev/null
339.24user 3.54system 1:28.87elapsed 385%CPU (0avgtext+0avgdata 2365856maxresident)k
9555544inputs+0outputs (0major+591298minor)pagefaults 0swaps

$ time awk '!x[$0]++' test > /dev/null                                                                                                                             
51.15user 1.55system 0:52.94elapsed 99%CPU (0avgtext+0avgdata 10976maxresident)k
0inputs+0outputs (0major+1923minor)pagefaults 0swaps

$ time uniq test > /dev/null                                                                                                                                  
421.89user 2.76system 7:06.63elapsed 99%CPU (0avgtext+0avgdata 1980maxresident)k
52712inputs+0outputs (0major+79minor)pagefaults 0swaps

Vì vậy, có vẻ như giải pháp awk của bạn là nhanh nhất trong số 3 và thực sự sử dụng ít bộ nhớ nhất

update2 và bây giờ với ngôn ngữ đơn giản hơn

$ export LC_ALL=c
$ time sort -u test > /dev/null                                                                                                                                             1.2m ? Tue Apr 21 17:09:22 2020
119.18user 3.64system 0:38.24elapsed 321%CPU (0avgtext+0avgdata 2013472maxresident)k

$ time awk '!x[$0]++' test > /dev/null                                                                                                                                1161ms ? Tue Apr 21 17:07:31 2020
67.23user 2.50system 1:10.16elapsed 99%CPU (0avgtext+0avgdata 10480maxresident)k
7187520inputs+0outputs (0major+1912minor)pagefaults 0swaps

$ time uniq test > /dev/null                                                                                                                                               
22.05user 2.02system 0:24.24elapsed 99%CPU (0avgtext+0avgdata 1488maxresident)k
2959648inputs+0outputs (1major+72minor)pagefaults 0swaps

Lần này uniq chiến thắng cuộc đua ... vì Stéphane Chazelas gợi ý trong các bình luận, đặt ngôn ngữ của bạn thành C giúp sắp xếp và uniq nhanh hơn cả nhóm!


Những gì thực hiện sortuniq? Địa phương nào?
Stéphane Chazelas
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.