2010-08-09 81 views
16

Tôi đang duy trì trình phân tích cú pháp CSV hiệu suất cao và cố gắng tận dụng tối đa công nghệ mới nhất để cải thiện thông lượng. Đối với nhiệm vụ đặc biệt này này có nghĩa là:Java: giải mã luồng ký tự đa luồng

  • bộ nhớ Flash (Chúng tôi sở hữu một card PCI-Express tương đối rẻ tiền, hiệu suất 1 TB dung lượng lưu trữ mà đạt 1 GB/s duy trì đọc)
  • Nhiều lõi (Chúng tôi sở hữu một giá rẻ Máy chủ Nehalem có 16 luồng phần cứng)

Việc triển khai đầu tiên của trình phân tích cú pháp CSV là đơn luồng. Đọc tệp, giải mã ký tự, phân tách trường, phân tích cú pháp văn bản, tất cả trong cùng một chuỗi. Kết quả là một thông lượng khoảng 50MB/s. Không tệ nhưng thấp hơn giới hạn lưu trữ ...

Triển khai thứ hai sử dụng một chuỗi để đọc tệp (ở cấp byte), một chuỗi để giải mã các ký tự (từ ByteBuffer sang CharBuffer) và nhiều chuỗi để phân tích cú pháp các trường (tôi có nghĩa là phân tích cú pháp các trường văn bản được phân tách thành đôi, số nguyên, ngày tháng ...). Điều này hoạt động tốt hơn, gần 400MB/s trên hộp của chúng tôi.

Nhưng vẫn thấp hơn hiệu suất của bộ nhớ của chúng tôi. Và những SSD đó sẽ cải thiện một lần nữa trong tương lai, chúng tôi sẽ không tận dụng tối đa nó trong Java. Rõ ràng là giới hạn hiện tại là giải mã ký tự (CharsetDecoder.read (...)). Đó là nút cổ chai, trên một bộ xử lý mạnh mẽ Nehalem nó biến đổi byte thành ký tự tại 400MB/s, khá tốt, nhưng điều này phải được đơn luồng. Bộ mã CharsetDecoder hơi có trạng thái, tùy thuộc vào bộ mã được sử dụng và không hỗ trợ giải mã đa luồng.

Vì vậy, câu hỏi của tôi đối với cộng đồng là (và cảm ơn bạn đã đọc bài đăng cho đến nay): có ai biết cách song song hóa hoạt động giải mã ký tự trong Java không?

Trả lời

2

có ai biết cách song song hoạt động giải mã ký tự trong Java không?

Bạn có thể mở nhiều luồng đầu vào để thực hiện việc này (Tôi không chắc chắn về cách bạn thực hiện việc này với NIO, nhưng phải có khả năng).

Mức độ khó này sẽ phụ thuộc vào mã hóa bạn giải mã. Bạn sẽ cần một giải pháp riêng biệt cho mã hóa đích. Nếu mã hóa có độ rộng cố định (ví dụ: Windows-1252), thì một byte == một ký tự và giải mã rất dễ dàng.

Mã hóa chiều rộng biến hiện đại (như UTF-8 và UTF-16) chứa quy tắc xác định byte đầu tiên của chuỗi ký tự, vì vậy có thể chuyển đến giữa tệp và bắt đầu giải mã (bạn sẽ phải lưu ý phần cuối của đoạn trước, do đó, nó là khôn ngoan để bắt đầu giải mã kết thúc của tập tin đầu tiên).

Một số mã hóa chiều rộng biến kế thừa có thể không được thiết kế tốt, vì vậy bạn sẽ không có tùy chọn nào khác ngoài giải mã từ đầu dữ liệu và đọc tuần tự.

Nếu đó là tùy chọn, hãy tạo dữ liệu của bạn dưới dạng UTF-16BE. Sau đó, bạn có thể cắt ra giải mã và đọc hai byte thẳng đến một char.

Nếu tệp là Unicode, hãy chú ý xử lý BOM, nhưng tôi đoán bạn đã quen thuộc với nhiều chi tiết cấp thấp.

+0

Thật không may, UTF-16 là mã hóa độ dài biến đổi. Bạn cần UTF-32 để phân tích cú pháp Unicode đơn giản như vậy. – grddev

+0

@grddev - Tôi đã đề cập đến điều này trong bài đăng của mình - có thể xác định chuỗi ký tự ở giữa các dòng dữ liệu UTF-16 - các cặp thay thế cao là 0xD800-0xDBFF và các thay thế thấp là 0xDC00-0xDFFF. Mọi thứ khác được chứa trong cặp byte. – McDowell

+0

Nhận xét của tôi đề cập đến đề cập đến UTF-16BE. Bạn hoàn toàn không thể giải mã được. Nhưng nó thực sự khá đơn giản. – grddev

1

Rõ ràng là giới hạn hiện tại là giải mã ký tự (CharsetDecoder.read (...))

Làm sao bạn biết điều đó? Trình giám sát/lược tả của bạn có hiển thị độc quyền rằng chuỗi bộ giải mã đang sử dụng 100% của một trong các lõi của bạn không?

Một khả năng khác là hệ điều hành không có khả năng lái SSD ở tốc độ tối đa lý thuyết của nó.

Nếu giải mã UTF-8 chắc chắn là nút cổ chai thì có thể thực hiện tác vụ song song. Nhưng bạn chắc chắn sẽ cần phải thực hiện bộ giải mã của riêng bạn để làm điều này.

+0

Có, một số lần chạy bằng cách sử dụng JProfiler cho thấy rõ ràng rằng chuỗi giải mã ký tự (đơn) đang hoạt động gần như 100% thời gian. Tôi thấy nhiều tham chiếu tại mã hóa UTF-8 và UTF-16 trong câu trả lời. Nhưng chúng tôi đang viết một trình phân tích cú pháp CSV mục đích chung ở đây, sẽ được sử dụng trên các tệp hiện có, bởi khách hàng của chúng tôi ở châu Âu, Mỹ, Nhật Bản, Trung Quốc ... Vì vậy, chúng tôi không thể giả sử bộ ký tự nào sẽ được sử dụng. Đặc biệt, chúng tôi không thể giả sử liệu bộ ký tự có được cố định chiều dài hay không. – Killerchamb

0

Nếu bạn biết mã hóa và kích thước cố định hoặc không chứa chuỗi byte chồng chéo, bạn có thể quét tìm chuỗi đặc biệt. Trong CSV, một chuỗi cho các dòng mới có thể có ý nghĩa. Ngay cả khi bạn tự động phát hiện mã hóa, bạn có thể chạy một vài byte đầu tiên để xác định mã hóa và sau đó chuyển sang giải mã song song.

+0

Tôi thích ý tưởng này rất nhiều. Định vị các dấu phân cách trực tiếp trong các byte thô. Và có, mẫu NEW_LINE là ứng viên phù hợp cho trình phân tích cú pháp CSV. Nhưng tôi phải hỗ trợ bất kỳ bộ ký tự nào.Bạn có biết một số phương pháp chung chung xung quanh việc triển khai bộ ký tự cho biết các mẫu byte chồng lên nhau hay không? Tôi không thấy gì trong Javadoc. – Killerchamb

+0

@Antoine: Thật không may, tôi không biết. Nó không phải là một vấn đề trong bất kỳ mã hóa UTF, hoặc bất kỳ mã hóa chiều rộng cố định nói chung. [Theo câu hỏi này] (http://stackoverflow.com/questions/724247/newline-control-characters-in-multi-byte-character-sets) cũng không có vấn đề gì đối với các mã hóa điển hình của Nhật Bản. Cho dù bất kỳ đại diện dòng mới nào trùng lặp trong các mã hóa Trung Quốc (hay khác), tôi cũng không biết. Mọi trường hợp rõ ràng là các giao diện hiện có trong Java không cung cấp phương tiện để thực hiện điều này một cách độc đáo. :( – grddev

0

Giải pháp thay thế khác (điên) là chỉ tách đầu vào thành các khối có kích thước tùy ý, bỏ qua các vấn đề giải mã và sau đó giải mã từng đoạn song song. Tuy nhiên, bạn muốn đảm bảo rằng các khối chồng lên nhau (với kích thước được tham số). Nếu vùng chồng chéo của hai khối được giải mã theo cùng một cách bởi hai luồng (và chồng chéo của bạn đủ lớn cho mã hóa được chỉ định) thì sẽ an toàn khi tham gia kết quả. Sự chồng chéo càng lớn, yêu cầu xử lý càng nhiều và xác suất lỗi càng nhỏ. Hơn nữa, nếu bạn đang ở trong một tình huống mà bạn biết mã hóa là UTF-8 hoặc mã hóa đơn giản tương tự, bạn có thể đặt chồng chéo khá thấp (đối với khách hàng đó) và vẫn được đảm bảo hoạt động chính xác.

Nếu đoạn thứ hai hóa ra là sai, bạn sẽ phải làm lại nó, vì vậy điều quan trọng là không được thực hiện với các khối lớn song song. Nếu bạn làm nhiều hơn hai đoạn song song, điều quan trọng là 'sửa chữa' từ đầu đến cuối, do đó một khối lệch không dẫn đến việc vô hiệu khối tiếp theo (có thể được căn chỉnh chính xác).