我正在尝试实现一种用于写入和删除的类型擦除数据结构 读取列表中任何类型的大型数组,具有以下内容 要求:
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类(Singleton结构)中使用它,它允许每个键访问跟踪:
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库,所以我正在使用
Poco::Any
和Poco::DynamicAny
),但这会带来很大的影响
表现很受欢迎。
从具有高频率的设备写入数据(获取数据 高达20khz,然后以大约4k的块写入Trace), 和普通矢量与一个矢量之间的测量性能差异 任何类型的因素是因子500-1000(测量800毫秒与大4毫秒 批量插入/读取循环)。大部分时间都因为失去了 构造函数调用与简单的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
关于编译器选项和优化:不幸的是,我没有选择更改它们,而是停留在当前设置。在大多数情况下,构建在调试模式下运行,但仍然必须满足时序要求。但无论如何,在发布模式下性能没有提高:
Old vector: time elapsed(ms) = 20.002
New vector: time elapsed(ms) = 1013.1
答案 0 :(得分:2)
我认为问题出在收集数据阶段而不是评估中。
首先,您的OldVector
不需要进行任何类型的转换,因此在POD数据上,它在插入数据时基本上会使用memcpy。
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。可以进行一些进一步的优化以减少复制,但这可以在它运行之后完成。
答案 1 :(得分: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
个数据存储在
std::map<std::string,
boost::variant<std::vector<int>,
std::vector<double>,
...
>
> mymap;
答案 2 :(得分:0)