2010-09-07 70 views
13

Tôi cần viết chương trình sắp xếp trong C và sẽ thật tuyệt nếu tệp có thể được sắp xếp để tiết kiệm dung lượng đĩa. Dữ liệu có giá trị, vì vậy tôi cần đảm bảo rằng nếu quá trình bị gián đoạn (ctrl-c), tệp không bị hỏng. Tôi có thể đảm bảo dây nguồn trên máy sẽ không được kéo mạnh.Thuật toán sắp xếp tại chỗ gián đoạn

chi tiết thêm: tập tin là ~ 40GB, hồ sơ được 128-bit, máy được 64-bit, hệ điều hành là POSIX

Bất kỳ gợi ý về thực hiện điều này, hoặc ghi chú nói chung?

Cảm ơn!

Để làm rõ: Tôi hy vọng người dùng sẽ muốn ctrl-c quá trình. Trong trường hợp này, tôi muốn thoát ra một cách duyên dáng và đảm bảo rằng dữ liệu được an toàn. Vì vậy, câu hỏi này là về xử lý ngắt và chọn một thuật toán sắp xếp có thể kết thúc nhanh chóng nếu được yêu cầu.

Theo dõi (2 năm sau): Chỉ vì hậu thế, tôi đã cài đặt bộ xử lý SIGINT và nó hoạt động rất tốt. Điều này không bảo vệ tôi khỏi bị mất điện, nhưng đó là một nguy cơ tôi có thể xử lý. Mã số tại https://code.google.com/p/pawnsbfs/source/browse/trunk/hsort.chttps://code.google.com/p/pawnsbfs/source/browse/trunk/qsort.c

+0

Tôi nghĩ bạn phải hỏi Điều nào quan trọng hơn? Không gian, hoặc liệu quá trình có nên bị gián đoạn hay không.Nếu bạn cần đảm bảo tệp không bị hỏng, bạn sẽ cần phải theo dõi tiến trình của nó và các trạng thái trước đó bằng cách nào đó - điều này sẽ chiếm nhiều không gian hơn tệp. –

+0

Để an toàn. Tạo một bản sao và sắp xếp bản sao. Tôi có thể; hình ảnh hệ thống tập tin sẽ gặp rắc rối với 40GB –

+0

@Martin khác: trí tưởng tượng của bạn không hoạt động theo cách của tôi. Trên ổ đĩa chính của tôi, tôi hiện có 36,4 GB miễn phí. –

Trả lời

5

Cài đặt trình xử lý cho SIGINT chỉ cần đặt cờ "quy trình phải thoát sớm".

Trong sắp xếp của bạn, hãy kiểm tra cờ sau mỗi lần hoán đổi hai bản ghi (hoặc sau mỗi lần hoán đổi N). Nếu cờ được đặt, hãy bảo lãnh.

+0

Điều này nghe có vẻ giống như giải pháp tốt nhất cho tôi. Một số khuyến nghị khác cho tôi ấn tượng rằng Ctrl-C sẽ bị bỏ qua nếu nó không được nhấn vào đúng thời điểm, điều đó có vẻ như là một trải nghiệm người dùng kém với tôi. –

+0

Điều này giống như người chiến thắng. Nó xuất hiện để áp dụng cho quicksort và heapsort. Cảm ơn! –

+0

Nó sẽ không lưu dữ liệu của bạn từ kill -9 hoặc mất điện. –

3

Sử dụng phân loại đống và ngăn chặn gián đoạn (ví dụ: tín hiệu chặn) trong mỗi thao tác hoán đổi.

+0

Vâng. Đơn giản và dễ dàng. Cũng thực hiện thay đổi trong bộ nhớ và sau đó viết/tuôn ra tất cả cùng một lúc (khối tín hiệu cho phần này). –

+0

Heapsort yêu cầu đánh tất cả các tập tin, mà có thể không tốt cho bất cứ điều gì mà không phải tất cả phù hợp với bộ nhớ vật lý. –

+0

Mặt khác, nếu tập tin khổng lồ như trong trường hợp của bạn, big-O và không phải là yếu tố không đổi từ io cache nhớ sẽ thống trị. Heap sắp xếp là loại duy nhất tại chỗ với tốt hơn so với bậc hai lớn-O. –

0

Sao lưu mọi thứ bạn định thay đổi. Đặt một lá cờ đánh dấu một loại thành công. Nếu mọi thứ đều OK thì hãy giữ kết quả, nếu không thì sao lưu lại.

+0

-1 không đáp ứng tiêu chí tại chỗ của câu hỏi, trong đó nêu rõ có một tập dữ liệu khổng lồ và có lẽ không có không gian để sao lưu nó. –

+0

R: Bạn không cần lưu toàn bộ tệp. Chỉ cần lưu các miếng nhỏ được sử dụng trong sắp xếp. Tôi không bao giờ nói rằng toàn bộ tập tin phải được sao lưu. –

12

Quyền của Jerry, nếu chỉ là Ctrl-C bạn lo lắng, bạn có thể bỏ qua SIGINT cho các dấu chấm tại một thời điểm. Nếu bạn muốn được chứng minh chống lại cái chết quá trình nói chung, bạn cần một số loại journalling tối thiểu. Để trao đổi hai phần tử:

1) Thêm bản ghi vào cấu trúc điều khiển ở cuối tệp hoặc trong một tệp riêng biệt, cho biết hai phần tử của tệp bạn sẽ trao đổi, A và B.

2) Sao chép A vào khoảng trống đầu, ghi lại rằng bạn đã làm như vậy, tuôn ra.

3) Bản sao B qua A, sau đó ghi lại trong không gian đầu mà bạn đã làm như vậy, tuôn ra

4) Sao chép từ không gian đầu qua B.

5) Hủy bỏ các kỷ lục.

Đây là không gian phụ (O) cho tất cả các mục đích thực tế, do đó, vẫn được tính là tại chỗ theo hầu hết các định nghĩa. Trong lý thuyết ghi một chỉ số là O (log n) nếu n có thể được tùy ý lớn: trong thực tế nó là một log rất nhỏ n, và phần cứng hợp lý/thời gian chạy nó ở trên tại 64.

Trong mọi trường hợp khi tôi nói " tuôn ra ", tôi có nghĩa là cam kết những thay đổi" đủ xa ". Đôi khi hoạt động tuôn ra cơ bản của bạn chỉ xóa bộ đệm trong quá trình, nhưng nó không thực sự đồng bộ hóa môi trường vật lý, bởi vì nó không xóa bộ đệm tất cả các cách thông qua trình điều khiển thiết bị/mức phần cứng. Đó là đủ khi tất cả các bạn đang lo lắng về là quá trình chết, nhưng nếu bạn đang lo lắng về việc phá vỡ phương tiện truyền thông đột ngột sau đó bạn sẽ phải tuôn ra qua người lái xe. Nếu bạn lo lắng về mất điện, bạn phải đồng bộ hóa phần cứng, nhưng bạn thì không. Với một UPS hoặc nếu bạn nghĩ rằng cắt điện là rất hiếm bạn không nhớ mất dữ liệu, đó là tốt.

Khi khởi động, hãy kiểm tra khoảng trống cho bất kỳ bản ghi "hoán đổi" nào.Nếu bạn tìm thấy, hãy tìm hiểu xem bạn đã nhận được bao nhiêu và hoàn tất việc hoán đổi từ đó để đưa dữ liệu trở lại trạng thái âm thanh. Sau đó bắt đầu sắp xếp lại.

Rõ ràng là có vấn đề về hiệu suất ở đây, vì bạn đang ghi hai lần nhiều bản ghi như trước đây, đồng thời xóa/đồng bộ có thể tốn kém đáng kinh ngạc. Trong thực tế, sắp xếp tại chỗ của bạn có thể có một số thao tác di chuyển phức tạp, liên quan đến nhiều giao dịch hoán đổi, nhưng bạn có thể tối ưu hóa để tránh mọi phần tử chạm vào khoảng trống. Bạn phải đảm bảo rằng trước khi ghi đè lên bất kỳ dữ liệu nào, bạn có một bản sao của nó an toàn ở đâu đó và bản ghi nơi bản sao đó sẽ đi để đưa tệp của bạn trở lại trạng thái chứa chính xác một bản sao của từng phần tử.

Jerry cũng đúng là sắp xếp đúng chỗ tại chỗ quá khó và chậm cho hầu hết các mục đích thực tế. Nếu bạn có thể dành một số phần tuyến tính của kích thước tệp gốc làm không gian đầu, bạn sẽ có thời gian tốt hơn nhiều với một loại hợp nhất.

Dựa trên làm rõ của bạn, bạn sẽ không cần bất kỳ hoạt động tuôn ra nào ngay cả khi có sắp xếp tại chỗ. Bạn cần khoảng trống trong bộ nhớ hoạt động theo cách tương tự và trình xử lý SIGINT của bạn có thể truy cập để có được dữ liệu an toàn trước khi thoát, thay vì khôi phục khi khởi động sau khi thoát một lối thoát bất thường và bạn cần truy cập bộ nhớ đó theo cách an toàn bằng tín hiệu (về mặt kỹ thuật có nghĩa là sử dụng sig_atomic_t để gắn cờ những thay đổi đã được thực hiện). Mặc dù vậy, bạn có thể tốt hơn với một mergesort hơn một loại thực sự tại chỗ.

+0

Tôi sẽ suy nghĩ về việc sử dụng một tệp ánh xạ bộ nhớ cho không gian đầu. Nó sẽ cung cấp cho bạn tốt nhất của cả hai thế giới. 1) Chi phí thấp cho truy cập vì nó được lưu trữ và không có API callls 2) Nếu quá trình chết hệ điều hành vẫn nên tuôn ra bộ nhớ sửa đổi vào đĩa bất kể quá trình chết như thế nào. – torak

+0

@torak: Tin tuyệt vời, tôi không biết POSIX đã cung cấp sự bảo đảm đó cho mmap. Tôi vẫn còn lo lắng rằng việc truy cập vào tập tin có thể được sắp xếp lại, mặc dù, làm cho nó không đáng tin cậy để phục hồi. Vì vậy, tất cả các con trỏ có thể cần phải được "dễ bay hơi" hoặc một cái gì đó. –

+0

Bạn sẽ cần sử dụng 'msync()' với 'MS_SYNC' tại mỗi điểm tuôn ra. 'msync()' cũng phải ngụ ý một rào cản trình biên dịch, vì vậy 'biến động' không cần thiết. – caf

5

Phần để bảo vệ chống lại ctrl-c là khá dễ dàng: signal(SIGINT, SIG_IGN);.

Theo như bản thân sắp xếp, một sắp xếp hợp nhất thường hoạt động tốt để sắp xếp bên ngoài. Ý tưởng cơ bản là đọc nhiều bản ghi vào bộ nhớ khi bạn có thể, sắp xếp chúng, sau đó ghi chúng ra đĩa. Đến nay, cách dễ nhất để xử lý việc này là viết từng lần chạy đến một tệp riêng biệt trên đĩa. Sau đó, bạn hợp nhất chúng lại với nhau - đọc bản ghi đầu tiên từ mỗi lần chạy vào bộ nhớ, và viết bản ghi nhỏ nhất của chúng vào tệp gốc; đọc một bản ghi khác từ chạy đã cung cấp bản ghi đó và lặp lại cho đến khi hoàn thành. Giai đoạn cuối cùng là thời gian duy nhất bạn đang sửa đổi tệp gốc, vì vậy đó là lần duy nhất bạn thực sự cần phải đảm bảo chống lại các gián đoạn và như vậy.

Một khả năng khác là sử dụng loại sắp xếp. Điểm xấu là việc phân loại chính nó là khá chậm. Điểm tốt là nó khá dễ dàng để viết nó để tồn tại hầu như bất cứ điều gì, mà không cần sử dụng nhiều không gian thêm. Ý tưởng chung khá đơn giản: tìm bản ghi nhỏ nhất trong tệp và trao đổi nó thành vị trí đầu tiên. Sau đó tìm bản ghi nhỏ nhất của những gì còn lại, và trao đổi nó thành vị trí thứ hai, và cứ thế cho đến khi hoàn thành. Điểm tốt của việc này là việc ghi nhật ký là tầm thường: trước khi bạn thực hiện hoán đổi, bạn ghi lại các giá trị của hai bản ghi mà bạn sẽ trao đổi. Vì sắp xếp chạy từ bản ghi đầu tiên đến bản ghi cuối cùng, điều duy nhất bạn cần theo dõi là có bao nhiêu bản ghi đã được sắp xếp tại bất kỳ thời điểm nào.

+1

+1 - không phù hợp với nhu cầu tại chỗ, nhưng đó là IMHO cách tiếp cận an toàn nhất. Không sửa đổi tập tin tổng thể trong khi sắp xếp các khối, và nếu quá trình hợp nhất gặp sự cố, bạn đã có các tập tin đã được sắp xếp làm bản sao lưu. – snemarch

+1

Thay vì bỏ qua 'SIGINT', bạn nên ** chặn ** nó (và tất cả các tín hiệu khác) trong các hoạt động hoán đổi, nhưng định kỳ bỏ chặn nó để các tín hiệu đang chờ xử lý đến trong khi bị chặn có thể được xử lý. –

-1

Giả sử hệ điều hành 64 bit (bạn nói nó là máy 64 bit nhưng vẫn có thể chạy hệ điều hành 32 bit), bạn có thể sử dụng mmap để ánh xạ tệp vào mảng sau đó sử dụng qsort trên mảng.

Thêm trình xử lý cho SIGINT để gọi msync và munmap để cho phép ứng dụng trả lời Ctrl-C mà không làm mất dữ liệu.

+1

-1 cho câu trả lời không đúng. Nếu tín hiệu ngắt 'qsort', dữ liệu ở trạng thái không xác định cho đến khi' qsort' kết thúc. Hoàn toàn không có cách nào để sử dụng 'qsort' của hệ thống để đáp ứng nhu cầu của OP. –

Các vấn đề liên quan