2014-08-27 24 views
0

我想實現的寫作類型擦除的數據結構和 讀取任何類型的大數組列表中,具有以下 要求:實現快速寫一個類型擦除列表/讀

  • 快速插入批量數據(接收std::vector<T>,其中T是原始類型)。
  • 如果類型匹配,快速讀取所有/最新值
  • 如果類型不匹配,則讀取/轉換。在從原始社會到原語(如雙>浮動,內部 - >雙)大多數情況下

我在想的接口將是這個樣子:

class Trace { 
     template<typename T> std::vector<T> read(); 
     template<typename T> std::vector<T> latest(); 
     template<typename T> void append(std::vector<T> values); 
     template<typename T> void replace(std::vector<T> values); 
     void clear(); 
}; 

,然後在使用TraceHandler類(單件結構),它允許訪問每個鍵的痕跡:

class TraceHandler { 
public: 
    template<typename T> 
    std::vector<T> read(std::string const& key); 
    template<typename T> 
    void write(std::string const& key, std::vector<T> const& val); 
private: 
    // STore all Traces for different types 
}; 

而一個使用率會是這個樣子:

TraceHandler th; 
std::vector<double> vals(1000,1.0), res; 
th.write("values",vals); 
std::vector<int> resInt; 
res = th.read<double>("values"); 
resInt = th.read<int>("values"); 

我們當前的實現爲每個數據類型創建一個跟蹤和 用戶必須保持正確的類型,這是賽道不是很靈活 (如編寫使用writeDouble(),讀取使用readDouble)。

我的第一種方法是內部存儲 載體的類型更改爲any類型(我們使用波科庫,所以我是用 Poco::AnyPoco::DynamicAny),但是這會導致大 性能損失。

數據從設備寫入的高頻(數據被採集 與高達20kHz,然後被寫入到跟蹤的周圍4K塊), 和一個普通的向量之間所測量的性能差和任何之一 類型是因子500-1000(測量800ms與4ms,大 大容量插入/讀入循環)。由於 構造函數調用vs簡單的memcopy,大多數時間會丟失。

所以我的問題是:有沒有一種方法來實現這個接口(或 備選)具有良好的批量插入/讀取性能?


編輯: 這是當前實現我使用:

class LWDynamicVector 
{ 
private: 
    typedef std::vector<Poco::DynamicAny> DataList; 
    DataList m_data; 
public: 
    LWDynamicVector() {} 

    template<typename T> std::vector<T> read() { 
    return std::vector<T>(m_data.begin(),m_data.end()); 
    } 

    template<typename T> void writeAppend(std::vector<T> v) { 
    m_data.insert(m_data.end(),v.begin(),v.end()); 
    } 

    template<typename T> void writeReplace(std::vector<T> v) { 
    m_data.assign(v.begin(),v.end()); 
    } 
}; 

,我使用的測試:

TEST(DynamicVector,Performance) { 
    typedef double Type; 
    size_t runs = 100; size_t N = 20480; 
    std::vector<Type> input; 
    input.reserve(N); 
    for(size_t i = 0; i < N; ++i) { 
    input.push_back(rand()); 
    } 

    { 
    OldVector<Type> instance; 
    Poco::Timestamp ts; 
    for(size_t i = 0; i < runs; ++i) { 
     instance.writeAppend(input); 
    } 
    std::cout << "Old vector: time elapsed(ms) = " << ts.elapsed()/1000.0 << std::endl; 
    std::vector<Type> output = instance.read(); 
    EXPECT_EQ(output.back(),output.back()); 
    } 
    { 
    LWDynamicVector dbv; 
    Poco::Timestamp ts; 
    for(size_t i = 0; i < runs; ++i) { 
     dbv.writeAppend(input); 
    } 
    std::cout << "New vector: time elapsed(ms) = " << ts.elapsed()/1000.0 << std::endl; 

    std::vector<Type> output = dbv.read<Type>(); 
    EXPECT_EQ(output.back(),output.back()); 
    } 

} 

導致:

Old vector: time elapsed(ms) = 44.004 
New vector: time elapsed(ms) = 4380.44 

關於c ompiler選項和優化:不幸的是,我被困在當前的設置中,沒有選擇來改變它們。在大多數情況下,構建以調試模式運行,但仍需滿足時序要求。但不管怎麼說,性能並不在釋放模式改進:

Old vector: time elapsed(ms) = 20.002 
New vector: time elapsed(ms) = 1013.1 
+0

如何OldVector樣子?舊的也轉換類型?你在使用C++ 11嗎? – Surt 2014-10-01 11:38:08

+0

@Surt目前'OldVector'只能存儲一種類型。特別是它是一個帶有鎖和新界面的'std :: vector '。目前我們並沒有使用C++ 11,但我會對使用C++ 11的任何解決方案感興趣。 – MatthiasB 2014-10-01 11:41:46

+0

是否爲同一次運行寫入相同類型的數據,例如,在同一次測量中沒有double和int的組合?沒有重複例如7個雙打,然後在同一個軌跡中有10個整數和3個字符串? – Surt 2014-10-01 12:13:14

回答

1

你知道你只寫了原始類型。你事先知道所有這些類型。使用普通的舊聯合+類型標籤。無法打敗。 boost::variant也應該有效。

typedef enum { type_int, type_double } type_tag_t; 
struct data_t { 
    type_tag_t tag; 
    union { 
    int int_elem; 
    double double_elem; 
    } 
}; 

boost::variant也應該工作。

另外,存儲數據的整個std::vector fuls在

std::map<std::string, 
      boost::variant<std::vector<int>, 
         std::vector<double>, 
         ... 
         > 
     > mymap; 
+0

你說的是內部的'std :: vector ',對不對?我喜歡這個想法,當然必須嘗試一下。但是我猜即使兩種類型都匹配,閱讀速度也會很慢,因爲您必須從內部向量逐個元素複製到結果向量元素。 – MatthiasB 2014-10-01 16:03:50

+0

它肯定比'std :: vector 更快。 – 2014-10-01 17:00:20

+0

我已更新另一種方法。 – 2014-10-01 17:24:15

0
std::vector<boost::any> 

這是一個致力於實現類型擦除技術類型庫。

boost::any

+0

我剛剛嘗試過這一點,不幸的是這仍然與'Poco :: Any'或'Poco :: DynamicAny'具有相同的性能。在我的測試用例中,boost :: any實現大約需要20秒,而直接實現的實現大約需要20ms到150ms(取決於使用的類型,字符要加倍)。這是相同類型的讀/寫,所以不會發生轉換。 – MatthiasB 2014-08-27 15:03:07

+0

@MatthiasB你試過用不同的選項編譯它嗎?如果您使用的是msvc,請嘗試釋放模式。什麼是您的平臺和參考編譯器? – user3784961 2014-08-27 17:03:47

+0

問題在於,它應該在調試模式下運行在不同的平臺上(在Core2Duo範圍內,所有運行VisualStudio2008的老電腦)。我測試了一個發佈版本以供參考,相關案例的差異降至30倍,但仍然緩慢。 – MatthiasB 2014-08-28 12:56:11

2

我相信這個問題是在收集數據階段而不是評價。

第一點是您的OldVector不需要進行任何類型轉換,因此在POD數據上,它基本上在插入數據時使用memcpy。

DynamicAny是一個非常好的類,如果你真的真的需要動態變量的內容,但是類的深處,我們可以看到(之一),這意味着每一個數據的一些分配的內存性能

VarHolder* _pHolder; 

問題插入和一些家務。

現在的概念實現,因爲我無法測試它,你的Trace類

template<class T> 
class Trace { 
    std::vector<T> trace; 

public: 
    template<typename T, class U> std::vector<U> read(); 
    template<typename T, class U> std::vector<T> latest(); 
    template<typename T> void append(std::vector<T> values); 
    template<typename T> void replace(std::vector<T> values); 
    void clear(); 
}; 

這將正常工作,如果你只使用一個T.隱藏類型TraceHandler

class TraceHandler { 
public: 
    template<typename T, class U> 
    std::vector<U> read(std::string const& key); 
    template<typename T> 
    void write(std::string const& key, std::vector<T> const& val); 
private: 
    // Store all Traces for different types 
    std::unordered_map<const std::string, Poco::DynamicAny> values; // abstraction 
}; 

這僅適用於每個鍵只使用一個T並且DynamicAny可以接受一個向量。

template<class T> 
void TraceHandler::write(std::string const& key, std::vector<T> const& val) { 
    if (values.find(key) == values.end()) { // doesn't exists 
    Trace<T> temp; 
    temp.append(val); 
    values[key] = temp; 
    } else 
    values[key].append(val); // only works if DynamicAny can return original type 
} 

它會與你的用例一起工作嗎?

TraceHandler th; 
std::vector<double> vals(1000,1.0), res; 
th.write("values",vals); 
std::vector<int> resInt; 
//res = th.read("values"); // could work if DynamicAny could return original. 
td.read("values", res); 
//resInt = th.read("values"); // wont work as read can't guess the type 
th.read("values", resInt); // read can guess the return type 

// handle conversion from stored type to wanted return type 
template<class T, class U> 
void TraceHandler::read(std::string const& key, std::vector<U>& res) { 
    // convert from T to U, maybe use Poco??? 
    ... work here!!! can be slow as its after it is captured 
} 

// specialization where T==U ... more work needed. 
template<class T, class U> 
std::vector<T>& TraceHandler::read(std::string const& key, std::vector<T>& res) { 
    // no conversion needed 
    // convince DynamicAny to return the original data 
    res = values[key]; // will not work out of the box??? 
} 

這應該有更好的表現,因爲每個表每次調用只有一次使用Poco :: DynamicAny。可以進行一些進一步的優化以減少複製,但是可以在完全運行後進行。

+0

我試圖將我的Traces存儲在DynamicAny持有者中。問題是你失去了存儲哪種類型的信息。如果您調用'TraceHandler :: read'並嘗試從T轉換爲U,那麼您不再具有T類型的信息,因此您無法對其進行轉換。 – MatthiasB 2014-10-01 14:09:52

+0

@MatthiasB,如果原來是std :: vector ,那麼值得更進一步,那麼值[key] .extract >應該返回一個const ref到原始值? – Surt 2014-10-01 15:33:43

+0

副本會更好。非簡化版本的功能不是返回整個向量,而是簡化版本(例如,只有自上次寫入後的數據)。 – MatthiasB 2014-10-01 15:53:12