2011-07-21 75 views
3

在我工作的項目上,我必須通過網絡來回發送float/double數組,我使用Boost.Asio作爲網絡的東西,因爲我需要將通信作爲異步,這似乎是最好的(唯一真正的一個?)在那裏...C++便攜式陣列序列化

發送的數組是浮動/雙打,類型是雙方都知道。 AFAIK,可能存在浮點存儲標準問題+與長整數/整數,尾數等相同的問題。

從邏輯上講,發送的數組是密集矩陣,由Eigen在一端處理,使用BLAS/[MKL | ATLAS]以及另一端的情況,很可能還需要其他用途,所以我將採用最通用的方式&,這似乎正在傳遞數組。

關鍵要求是高性能和可移植性,因爲客戶端和服務器都可以在32/64位的任意組合上運行,並且通信非常緊密(實時監控,每秒更新一次客戶端),所以序列化開銷本身必須最小化。

從我迄今發現的情況來看,這裏要考慮的兩大玩家是Boost.Serialization和Google的Protobuf。

BS的大親是我已經在項目中使用Boost(儘管單元測試在Google測試中),並且使用make_array()來序列化數組似乎很簡單。對它的重要意義在於性能。

從我發現的protobuf的優點是性能,所有的長椅似乎表明它在任何操作上都比BS好10-20倍。我在protobuf文檔中找不到的東西是在消息中添加一個數組。它使用重複的字段,並且據我所知,我必須在陣列的每個元素上使用MsgObject.repeatedProp.Add(const T&),這意味着,即10k對於10k陣列調用它,並且這看起來也是昂貴的。

如何解決這個任何建議,將不勝感激,因爲我與C++的經驗是有限的,我是最近才重新啓動一個較長的休息後寫在裏面......

+0

我無法評論添加項目的C++ api(我避免了在我的C#版本中的這種需求,但這對您沒有幫助) - 但僅僅是說;如果你看* protobuf,這對於「packed arrays」(它避免了每個元素的頭部開銷)似乎是一個很好的用例。只是爲了擠出一些額外的表現提示。 –

+1

如果您確實需要超高性能,並且您的所有客戶端都是x86/x64,則可以直接寫入二進制數據。浮動和雙打分別佔用4和8個字節,沒有填充(unline long double),所以它們在兩個平臺上看起來都是相同的。這不完全是「便攜式」,但實際上它應該工作。 –

+0

@Kerrek SB我想到了這一點,但我記得在某處讀到reinterpret_cast是編譯器實現的依賴,這真的會混淆可移植性... – TC1

回答

1

除了ASIO提振,你可能想看看boost.MPI(消息傳遞接口)。這在後臺使用boost.serialization進行序列化。

Boost.mpi discusses可用於booost.serialization的性能優化。特別地,使用

  • BOOST_CLASS_TRACKING(gps_position類型,track_never)
  • BOOST_CLASS_IMPLEMENTATION(gps_position,object_serializable)

宏和MPI定義宏

  • BOOST_IS_MPI_DATATYPE的(gps_position )

這些優化似乎很好,參見this圖。

我從來沒有使用protobuff所以我不能說這個。

+0

我真的不需要MPI,至少現在不需要,但是感謝BS的小費,我會檢查一下。 – TC1

1

使用protobufs,如果使用「字節」而不是「重複的int32」(或類似的)對數組進行編碼,則可以使10k數組的10k調用無效。 在您的代碼中,如果您投射指針並使用memcpy,它通常非常快。

0

在github上有很多可用的例子。這裏有一些可能的資源去協議緩衝區特徵和矩陣:

  • HAL硬件抽象庫(機器人技術)。
  • Tensorflow對張量(nd數組)做了很多相同的事情,但考慮到它們支持的擴展功能集,它們的實現可能會複雜得多。
  • NUbots有另一個例子。
  • Chromium似乎也有例子。
  • ceres-solver用於支持protobufs for matrices但該功能已被刪除。 (header

這裏是HAL版本,這是簡單的,看起來像它會做的很好:

Protobuf for storing a matrix

package hal; 

message MatrixMsg { 
    required uint32 rows = 1; 
    // columns deduced by division. Data stored as column major 
    repeated double data = 2 [packed=true]; 
} 

message VectorMsg { 
    repeated double data = 1 [packed=true]; 
} 

Loading into eigen and writing to protobuf

#pragma once 

#include <Eigen/Eigen> 
#include <HAL/Messages.pb.h> 

namespace hal { 

inline void ReadMatrix(const MatrixMsg &msg, Eigen::MatrixXd* mat) { 
    mat->resize(msg.rows(),msg.data_size()/msg.rows()); 
    for(int ii = 0 ; ii < msg.data_size() ; ii++){ 
    mat->operator()(ii) = msg.data(ii); 
    } 
} 

inline void ReadVector(const VectorMsg &msg, Eigen::VectorXd* vec) { 
    vec->resize(msg.data_size()); 
    for(int ii = 0 ; ii < msg.data_size() ; ii++){ 
    vec->operator()(ii) = msg.data(ii); 
    } 
} 

inline void WriteMatrix(const Eigen::MatrixXd &mat, MatrixMsg *msg) { 
    msg->set_rows(mat.rows()); 
    msg->mutable_data()->Reserve(mat.rows()*mat.cols()); 
    for(int ii = 0 ; ii < mat.cols()*mat.rows() ; ii++){ 
    msg->add_data(mat(ii)); 
    } 
} 

inline void WriteVector(const Eigen::VectorXd &mat, VectorMsg *msg) { 
    msg->mutable_data()->Reserve(mat.rows()); 
    for(int ii = 0 ; ii < mat.rows() ; ii++){ 
    msg->add_data(mat(ii)); 
    } 
} 

} // namespace hal 

然而如果你打算在圖書館做這件事,那麼你需要考慮更多。還有一個庫有一個很好的實現,其中有類型,寬度,高度的枚舉,然後是一個字節數組,rowmajor/colmajor等。但是我無法再找到它。這些都必須考慮在內,幷包含在protobuf中以便跨矩陣庫兼容。有關如何執行此操作的一些想法,HAL image protobufimage cpp可能有所幫助,它們可以從opencv源讀取/寫入,也可以將其視爲矩陣。