2009-12-16 28 views
7

Tôi đang làm việc với một cấu trúc cây không nhỏ (đó là một Burkhard-Keller-Tree,> 100 MB trong bộ nhớ) được thực hiện trong C + +. Các con trỏ đến con của mỗi nút được lưu trữ trong một QHash.Cách nhanh nhất để deserialize một cây trong C++

Mỗi nút x có n con y [1] ... y [n], các cạnh của trẻ được gắn nhãn với khoảng cách chỉnh sửa d (x, y [i]), do đó, sử dụng băm để lưu trữ nút là một giải pháp rõ ràng.

class Node { 
    int value; 
    QHash<int, Node*> children; 
    /* ... */ 
}; 

Tôi cũng muốn tuần tự hóa và deserialize nó thành một tệp (Tôi hiện đang sử dụng QDataStream). Cây chỉ được xây dựng một lần và không thay đổi sau đó.

Xây dựng cây và deserializing nó là khá chậm. Tôi đang tải cây theo cách hiển nhiên: Xây dựng đệ quy từng nút. Tôi nghĩ rằng điều này là tối ưu do nhiều nút được tạo riêng biệt với toán tử new. Tôi đọc ở đâu đó rằng new là khá chậm. Việc xây dựng ban đầu không phải là một vấn đề lớn bởi vì cây khá ổn định và không cần phải xây dựng lại thường xuyên. Nhưng tải cây từ một tập tin nên càng nhanh càng tốt.

Cách tốt nhất để thực hiện việc này là gì?

Nó phải tốt hơn nhiều để lưu toàn bộ cây trong một khối bộ nhớ duy nhất với các nút lân cận. Tuần tự hóa và deserializing sau đó sẽ được giảm xuống để tiết kiệm và tải toàn bộ khối, mà tôi phải phân bổ chỉ một lần.

Nhưng để thực hiện điều này, tôi sẽ phải triển khai lại QHash, AFAIK.

Bạn sẽ làm gì để tăng tốc độ deserialization?

Phụ Lục

Cảm ơn bạn đã gợi ý của bạn để làm một số hồ sơ. Dưới đây là kết quả:

Trong khi xây dựng lại cây từ một tập tin

1 % of the time is consumed by my own new calls 
65 % is consumed by loading the QHash objects (this is implemented by the 
    Qt Library) of each node 
12 % is consumed by inserting the nodes into the existing tree 
20 % is everything else 

Vì vậy, nó chắc chắn là không cuộc gọi mới của tôi mà gây ra sự chậm trễ nhưng xây dựng lại của QHash đối tượng tại mỗi nút. Điều này về cơ bản được thực hiện với:

QDataStream in(&infile); 
in >> node.hash; 

Tôi có phải tìm hiểu về QHash và xem điều gì đang xảy ra dưới mui xe ở đó không? Tôi nghĩ rằng giải pháp tốt nhất sẽ là một đối tượng băm có thể được nối tiếp với một hoạt động đọc và ghi mà không cần phải xây dựng lại cấu trúc dữ liệu nội bộ.

+0

Bạn có cần truy cập nhanh vào các nút cụ thể y [i] không? Hãy thử sử dụng một QList thay vì QHash, nó sẽ nhanh hơn để làm việc với khi nói đến I/O. – rpg

+0

Có. Tra cứu phải nhanh. – WolfgangA

Trả lời

3

Một cách tiếp cận khác là sắp xếp lại con trỏ của bạn và khôi phục chúng khi tải.Ý tôi là:

serializing:

nodeList = collectAllNodes(); 

for n in nodelist: 
write (&n) 
writeNode(n) //with pointers as-they-are. 

deserializing:

//read all nodes into a list. 
while (! eof(f)) 
    read(prevNodeAddress) 
    readNode(node) 
    fixMap[prevNodeAddress] = &node; 
    nodeList.append(node); 

//fix pointers to new values. 
for n in nodeList: 
    for child in n.children: 
     child->node = fixMap[child->node] 

cách này, nếu bạn không chèn-loại bỏ các nút mới, bạn có thể phân bổ một vector một lần và sử dụng rằng bộ nhớ, giảm phân bổ của bạn để các bản đồ (như rpg nói, nó có thể nhanh hơn với các danh sách hoặc thậm chí cả vectơ).

+0

Câu trả lời hay! Cảm ơn bạn – WolfgangA

1

Tôi rất khuyên bạn nên sử dụng boost serialization library. Nó sẽ làm việc với các giải pháp bạn đang sử dụng.

+0

Tôi thứ hai này: Boost là một giải pháp tốt đẹp và xử lý tự động tất cả các con tàu quan hệ cha/mẹ. Đó là giá trị điều tra, cho rằng các điểm chuẩn cho thấy rằng QHash (giải pháp hiện tại cho trẻ em/cha mẹ) là những gì ăn phần lớn thời gian. Nó cũng có sẵn trên một loạt các nền tảng. Mặt khác, tôi không có ý tưởng làm thế nào Boost cũng chơi với QT. – DrYak

1

Cách nhanh nhất tuyệt đối của tuần tự/deserialising là viết một khối bộ nhớ liền kề vào đĩa như bạn nói. Nếu bạn thay đổi cấu trúc cây của bạn để tạo ra điều này (có thể sử dụng một thói quen phân bổ tùy chỉnh) điều này sẽ rất dễ dàng.

Thật không may là tôi không quen với QHash, nhưng nhìn vào nó trông giống như một Hashtable hơn là một cái cây. Tôi đã hiểu lầm bạn chưa? Bạn đang sử dụng tính năng này để ánh xạ các nút trùng lặp?

Tôi muốn sử dụng trình định dạng (Tôi đã sử dụng Quantify, bây giờ được gọi là Rational PurifyPlus, nhưng có rất nhiều listed here) để tìm nơi bạn đang sử dụng thời gian, nhưng tôi đoán đó là phân bổ bộ nhớ nhiều hơn là một phân bổ đơn lẻ hoặc nhiều lần đọc thay vì một lần đọc. Để giải quyết cả hai vấn đề bạn biết trước (vì bạn lưu trữ nó) bạn cần bao nhiêu nút, sau đó ghi/đọc một chuỗi các nút có độ dài chính xác, trong đó mỗi con trỏ là một chỉ mục trong mảng, thay vì một con trỏ trong bộ nhớ .

+0

Mỗi nút của cây có khóa và có thể bắt đầu bằng các lá của nó. Mỗi chiếc lá bị dereferenced bởi một số tùy ý. Để được chính xác: Một nút x có n lá y_1 ... y_n, mỗi cạnh từ x đến y_i được gắn nhãn với khoảng cách chỉnh sửa từ d (x, y_i) (xem http://en.wikipedia.org/wiki/BK -cây). – WolfgangA

+0

+1. cho profiling trước khi tối ưu hóa ... – neuro

0

Một giải pháp khác là sử dụng bộ cấp phát bộ nhớ của riêng bạn, bộ nhớ này sẽ sử dụng bộ nhớ liên tục. Sau đó, bạn sẽ có thể đổ bộ nhớ như là và tải nó trở lại. Đó là nền tảng (ví dụ: lớn endian/little endian, 32bit/64bit) nhạy cảm.

+0

-1 cho ý tưởng này: Bạn đề cập đến một số vấn đề nhưng thực tế là đây cũng là trình biên dịch, mức tối ưu hóa và gỡ lỗi/phát hành nhạy cảm - chưa kể đến việc mở rộng cây trong tương lai và xử lý di chuyển độc đáo. – RnR

+0

+1 để bù đắp: Với mức trừu tượng phù hợp chắc chắn có thể xảy ra - ví dụ: sử dụng các trình vòng lặp và lưu trữ các thay đổi thay vì con trỏ. Đặc biệt là cho một "xây dựng một lần, không bao giờ sửa đổi" một phân bổ đấu trường là cực kỳ hiệu quả. Nền tảng tính di động * là * một vấn đề, và nó có lẽ sẽ không giải quyết vấn đề của OP, mặc dù. – peterchen

0

Như bạn đã nói, phân bổ các đối tượng bằng mới có thể chậm. Điều đó có thể được cải thiện phân bổ một hồ bơi đối tượng và sau đó sử dụng các đối tượng được phân bổ trước cho đến khi hồ bơi cạn kiệt. Bạn thậm chí có thể thực hiện điều này để làm việc ở chế độ nền bằng cách nạp chồng các toán tử mới/xóa của lớp đang được đề cập đến.

4

Trước hết - hãy lập hồ sơ ứng dụng của bạn để bạn biết điều gì cần có thời gian - dựa trên nghi ngờ mới vì bạn đã đọc ở đâu đó có thể chậm hoặc lặp lại qua cây là không đủ.

Có thể đó là hoạt động IO - có thể định dạng tệp của bạn không chính xác/không hiệu quả.

Có thể bạn chỉ gặp lỗi ở đâu đó?

Hoặc có thể có một vòng lặp bậc hai ở đâu đó mà bạn không nhớ về việc gây ra sự cố? :)

Đo lường điều thực sự cần thời gian trong trường hợp của bạn và sau đó tiếp cận vấn đề - nó sẽ giúp bạn tiết kiệm rất nhiều thời gian và bạn sẽ tránh phá vỡ thiết kế/mã của mình để khắc phục sự cố hiệu suất không tồn tại trước khi tìm kiếm nguyên nhân thực sự.

+0

+1. Tôi hoàn toàn đồng ý. Luôn luôn hồ sơ trước khi tối ưu hóa. Ngay cả khi gues của bạn là đúng, bạn sẽ biết chính xác có bao nhiêu bạn đã đạt được cho một tối ưu hóa nhất định. – neuro

+0

Mỗi nút được lưu trữ với một toán tử '<<' quá tải thành một QDataStream. Đây là cách được khuyến nghị để lưu trữ các đối tượng Qt. Không, không có vòng lặp bậc hai. Tôi đã làm một số hồ sơ và kết quả giả mạo giả của tôi (xem câu hỏi đã chỉnh sửa). – WolfgangA

0

Phân bổ bộ nhớ của riêng bạn với toán tử bị quá tải new() và delete() là một tùy chọn chi phí thấp (thời gian phát triển). Tuy nhiên, điều này chỉ ảnh hưởng đến thời gian cấp phát bộ nhớ chứ không ảnh hưởng đến thời gian của Ctor. Số dặm của bạn có thể thay đổi, nhưng có thể đáng để thử.

0

tôi sẽ mở rộng nhận xét của tôi một chút:

Kể từ khi hồ sơ của bạn cho thấy serialization QHash mất nhiều thời gian nhất, tôi tin rằng thay QHash với một QList sẽ mang lại một sự cải thiện đáng kể khi nói đến tốc độ deserialization.

Việc tuần tự hóa QHash chỉ xuất ra các cặp khóa/giá trị, nhưng quá trình deserialization tạo cấu trúc dữ liệu băm!

Ngay cả khi bạn nói rằng bạn cần tra cứu nhanh con, tôi khuyên bạn nên thử thay thế QHash bằng QList> làm bài kiểm tra. Nếu không có nhiều trẻ em cho mỗi nút (nói, dưới 30), tra cứu vẫn phải đủ nhanh ngay cả với một Danh sách phát. Nếu bạn thấy rằng QList không đủ nhanh, bạn vẫn có thể sử dụng nó chỉ cho (de) serializaton và sau đó chuyển đổi thành băm khi cây đã được nạp.

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