C++ 实现快速写入/读取的类型擦除列表

C++ 实现快速写入/读取的类型擦除列表,c++,type-conversion,type-erasure,C++,Type Conversion,Type Erasure,我正在尝试实现一个类型擦除的数据结构,用于写和写 读取列表中任何类型的大型数组,并执行以下操作 要求: 快速插入批量数据(接收一个std::vector,其中T是一个基本类型) 如果类型匹配,则快速读取所有/最新值 如果类型不匹配,则读取/转换。在大多数情况下,从基元到基元(例如双精度->浮点,整数->双精度) 我想到的界面应该是这样的: class Trace { template<typename T> std::vector<T> read();

我正在尝试实现一个类型擦除的数据结构,用于写和写 读取列表中任何类型的大型数组,并执行以下操作 要求:

  • 快速插入批量数据(接收一个
    std::vector
    ,其中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 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");
我使用的测试是:

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) = 20.002
New vector: time elapsed(ms) = 1013.1
std::vector 它是一个专用于实现类型擦除技术的类型的库

std::vector
它是一个专用于实现类型擦除技术的类型的库

std::vector
它是一个专用于实现类型擦除技术的类型的库

std::vector
它是一个专用于实现类型擦除技术的类型的库


我认为问题出在收集数据阶段,而不是评估阶段

第一点是,您的
OldVector
不需要进行任何类型转换,因此在POD数据上,它在插入数据时基本上会在数据上使用memcpy

如果您真的需要动态变量内容,那么DynamicAny是一个非常好的类,但是在类的深处,我们可以看到性能问题

VarHolder* _pHolder;
这意味着为每个插入的数据分配一些内存,并进行一些内部管理

现在是一个我无法测试的概念实现,您的跟踪类

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();
};
模板
类跟踪{
std::矢量跟踪;
公众:
模板std::vector read();
模板std::vector latest();
模板void append(std::向量值);
模板无效替换(标准::向量值);
无效清除();
};
如果只使用一个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
};
类跟踪处理器{
公众:
模板
std::矢量读取(std::字符串常量和键);
模板
无效写入(std::string const&key,std::vector const&val);
私人:
//存储不同类型的所有跟踪
std::无序的_映射值;//抽象
};
仅当每个关键点仅使用一个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
}
模板
void TraceHandler::write(std::string const&key,std::vector const&val){
如果(values.find(key)=values.end()){//不存在
微量温度;
附加温度(val);
值[键]=温度;
}否则
值[key].append(val);//仅当DynamicCany可以返回原始类型时才有效
}
它是否适用于您的用例

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???
}
TraceHandler-th;
标准:向量VAL(1000,1.0),res;
写入(“值”,VAL);
std::载体抗性;
//res=th.读取(“值”);//如果DynamicAny可以返回原始版本,则可以工作。
td.read(“值”,res);
//resInt=th.读取(“值”);//我猜不出是什么类型的
读取(“值”,resInt);//read可以猜测返回类型
//处理从存储类型到所需返回类型的转换
模板
void TraceHandler::read(std::string const&key,std::vector&res){
//从T转换为U,可能使用Poco???
…在这里工作!!!在它被捕获后可以慢下来
}
//其中T==U。。。需要做更多的工作。
模板
std::vector和TraceHandler::read(std::string const和key,std::vector和res){
//不需要转换
//说服DynamicAny返回原始数据
res=values[key];//无法开箱即用???
}

这应该具有更好的性能,因为每次调用每个表只使用一次Poco::DynamicAny。可以进行一些进一步的优化以减少复制,但这可以在运行之后再进行。

我认为问题出在收集数据阶段,而不是评估阶段

第一点是,您的
OldVector
不需要进行任何类型转换,因此在POD数据上,它在插入数据时基本上会在数据上使用memcpy

如果您真的需要动态变量内容,那么DynamicAny是一个非常好的类,但是在类的深处,我们可以看到性能问题

VarHolder* _pHolder;
这意味着为每个插入的数据分配一些内存,并进行一些内部管理

现在是一个我无法测试的概念实现,您的跟踪类

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();
};
模板
类跟踪{
std::矢量跟踪;
公众:
模板std::vector read();
模板std::vector latest();
模板void append(std::向量值);
模板无效替换(标准::向量值);
无效清除();
};
如果只使用一个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
};
类跟踪处理器{
公众:
模板
std::矢量读取(std::字符串常量和键);
模板
无效写入(std::string const&key,std::vector const&val);
私人:
//存储不同类型的所有跟踪
std::无序的_映射值;//抽象
};
仅当每个关键点仅使用一个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
}
模板
void TraceHandler::write(std::string const&key,std::vector const&val){
如果(values.find(key)=values.end()){//不存在
微量温度;
附加温度(val);
值[键]=温度;
}否则
值[key].append(val);//仅当DynamicCany可以返回原始类型时才有效
}
它是否适用于您的用例

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???
}
TraceHandler-th;
标准:向量VAL(1000,1.0),res;
写入(“值”,VAL);
std::载体抗性;
//res=th.读取(“值”);//如果DynamicAny可以返回原始版本,则可以工作。
td.read(“值”,res);
//resInt=th.读取(“值”);//我猜不出是什么类型的
阅读
std::map<std::string, 
          boost::variant<std::vector<int>, 
                         std::vector<double>, 
                         ...
                        >
        > mymap;