C++ 用于快速插入和查找n维实向量的适当容器(提供初始基准测试) 1.问题的描述
我试图选择最合适(有效)的容器来存储由浮点数组成的唯一的n维向量。 解决整个问题,最重要的步骤(与问题相关)包括:C++ 用于快速插入和查找n维实向量的适当容器(提供初始基准测试) 1.问题的描述,c++,performance,stl,performance-testing,C++,Performance,Stl,Performance Testing,我试图选择最合适(有效)的容器来存储由浮点数组成的唯一的n维向量。 解决整个问题,最重要的步骤(与问题相关)包括: 从外部程序获取一个新向量(在同一次运行期间,所有向量都具有相同的维度) 检查(尽快)此容器中是否已存在新点: 如果存在-跳过许多昂贵的步骤,然后执行其他步骤 如果不-将插入容器中(在容器中订购并不重要),然后执行其他步骤 事先,我不知道我会有多少个向量,但最大数量是预先规定的,等于100000。而且,每次迭代我总是只得到一个新向量。因此,在开始时,这些新向量中的大多数是唯一的,
- 如果存在-跳过许多昂贵的步骤,然后执行其他步骤李>
- 如果不-将插入容器中(在容器中订购并不重要),然后执行其他步骤
-线性O(n)find_if()
-常数,但如果新的push_-back()
大于旧的size()
容量()
-容器大小的对数,O(log(size())insert()
-平均情况:O(1),最坏情况O(size())insert()
向量
第一,为什么未排序向量?我的场景与在强>项目23中描述的情况有很大的不同:考虑用排序向量替换关联容器。< /强>
因此,在这种情况下,使用排序向量没有任何优势
第二,假设两个向量x和y相等,如果
euclideandstance2(x,y)
。
考虑到这一点,我对vector的最初(可能相当糟糕)实现如下:
使用vector
容器对实施的一部分进行基准测试:
// create a vector of double arrays (vda)
std::vector<std::array<double, N>> vda;
const double tol = 1e-6; // set default tolerance
// record start time
auto start = std::chrono::steady_clock::now();
// Generate and insert one hundred thousands new double arrays
for (size_t i = 0; i < 100000; ++i) {
// Get a new random double array (da)
std::array<double, N> da = getRandomArray();
auto pos = std::find_if(vda.begin(), vda.end(), // range
[=, &da](const std::array<double, N> &darr) { // search criterion
return EuclideanDistance2(darr.begin(), darr.end(), da.begin()) < tol*tol;
});
if (pos == vda.end()) {
vda.push_back(da); // Insert array
}
}
// record finish time
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time to generate and insert unique elements into vector: "
<< diff.count() << " s\n";
std::cout << "vector's size = " << vda.size() << std::endl;
// create a set of double arrays (sda) with a special sorting criterion
std::set<std::array<double, N>, compare_arrays> sda;
// create a vector of double arrays (vda)
std::vector<std::array<double, N>> vda;
// record start time
auto start = std::chrono::steady_clock::now();
// Generate and insert one hundred thousands new double arrays
for (size_t i = 0; i < 100000; ++i) {
// Get a new random double array (da)
std::array<double, N> da = getRandomArray();
// Inserts into the container, if the container doesn't already contain it.
sda.insert(da);
}
// record finish time
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time to generate and insert unique elements into SET: "
<< diff.count() << " s\n";
std::cout << "set size = " << sda.size() << std::endl;
// create a unordered set of double arrays (usda)
std::unordered_set<std::array<double, N>, ArrayHash, ArrayEqual> usda;
// record start time
auto start = std::chrono::steady_clock::now();
// Generate and insert one hundred thousands new double arrays
for (size_t i = 0; i < 100000; ++i) {
// Get a new random double array (da)
std::array<double, N> da = getRandomArray();
usda.insert(da);
}
// record finish time
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time to generate and insert unique elements into UNORD. SET: "
<< diff.count() << " s\n";
std::cout << "unord. set size() = " << usda.size() << std::endl;
排序标准基于有缺陷的(打破严格的弱排序)。目前,我想(大致)看看我能从不同的容器中得到什么,然后决定哪一个是最好的
// return whether the elements in the arr1 are “lexicographically less than”
// the elements in the arr2
struct compare_arrays {
bool operator() (const std::array<double, N>& arr1,
const std::array<double, N>& arr2) const {
// Lexicographical comparison compares using element-by-element rule
return std::lexicographical_compare(arr1.begin(), arr1.end(), // 1st range
arr2.begin(), arr2.end(), // 2nd range
compare_doubles); // sorting criteria
}
// return true if x < y and not within tolerance distance
static bool compare_doubles(double x, double y) {
return (x < y) && !(fabs(x-y) < tolerance);
}
private:
static constexpr double tolerance = 1e-6; // Comparison tolerance
};
其中,原始哈希函数:
// Hash Function
struct ArrayHash {
std::size_t operator() (const std::array<double, N>& arr) const {
std::size_t ret;
for (const double elem : arr) {
ret += std::hash<double>()(elem);
}
return ret;
}
};
//散列函数
结构ArrayHash{
std::size\u t运算符()(const std::array&arr)const{
标准:尺寸和重量;
用于(常数双元素:arr){
ret+=std::hash()(elem);
}
返回ret;
}
};
和等效标准:
// Equivalence Criterion
struct ArrayEqual {
bool operator() (const std::array<double, N>& arr1,
const std::array<double, N>& arr2) const {
return EuclideanDistance2(arr1.begin(), arr1.end(), arr2.begin()) < tol*tol;
}
private:
static constexpr double tol = 1e-6; // Comparison tolerance
};
//等价准则
结构ArrayEqual{
布尔运算符()(常数std::数组和arr1,
常数std::数组和arr2)常数{
返回欧几里德常数2(arr1.begin(),arr1.end(),arr2.begin())
3.3.1测试未分类设备的性能
在下面最后一张凌乱的表格中,我再次总结了执行时间和容器大小,这取决于不同的公差(eps)值
|eps |带-O3标志/不带优化标志的时间|大小|
|1e-6 | 57.4823/0.0590703 | 100000/100000|
|1e-3 | 57.9588/0.0618149 | 99978/100000|
|1e-2 | 43.2816/0.0595529 | 82873/100000|
|1e-1 | 0.238788/0.0578297 | 781/99759|
简而言之,与其他两种方法相比,执行时间是最好的,但是即使使用非常宽松的公差(1e-1)几乎所有的随机向量都被识别为唯一的。所以,在我的例子中,节省了查找时间,但浪费了更多的时间来处理问题的其他昂贵步骤。我想,这是因为我的哈希函数真的很幼稚
编辑:这是最意外的行为。对无序集启用-O3优化,会严重降低性能。更令人惊讶的是,唯一元素的数量取决于优化标志,这可能不是唯一的意思,这意味着我必须提供更好的哈希函数
4.开放性问题
如我所知,在前进中,可能的唯一向量的最大大小使用std::vector::reserve(100000)
有意义吗
根据的规定,保留
不会对性能造成太大影响:
我过去在读报纸时,对使用reserve()很小心
向量。我惊讶地发现,对于我的所有用途,
呼叫储备
// Hash Function
struct ArrayHash {
std::size_t operator() (const std::array<double, N>& arr) const {
std::size_t ret;
for (const double elem : arr) {
ret += std::hash<double>()(elem);
}
return ret;
}
};
// Equivalence Criterion
struct ArrayEqual {
bool operator() (const std::array<double, N>& arr1,
const std::array<double, N>& arr2) const {
return EuclideanDistance2(arr1.begin(), arr1.end(), arr2.begin()) < tol*tol;
}
private:
static constexpr double tol = 1e-6; // Comparison tolerance
};
struct ArrayEqual {
bool operator() (const std::array<double, N>& arr1,
const std::array<double, N>& arr2) const {
auto beg1 = arr1.begin(), end1 = arr1.end(), beg2 = arr2.begin();
double val = 0.0;
while (beg1 != end1) {
double dist = (*beg1++) - (*beg2++);
val += dist*dist;
if (val >= tol*tol)
return false;
}
return true;
}
private:
static constexpr double tol = 1e-6; // Comparison tolerance
};