Performance 有序集的高性能合并
我们希望将一组数字按升序或降序排序,但下面的示例仅显示升序。最大速度的数据结构表示是一个问题 例如,一个聚合程序不断地从许多不同的监控代理获取数字包,例如通过网络。这样做的目的是让它们一直快速分类。例如,您可以使用ints获取这些数据包,但实际情况是按顺序使用double:Performance 有序集的高性能合并,performance,algorithm,sorting,c++11,real-time-updates,Performance,Algorithm,Sorting,C++11,Real Time Updates,我们希望将一组数字按升序或降序排序,但下面的示例仅显示升序。最大速度的数据结构表示是一个问题 例如,一个聚合程序不断地从许多不同的监控代理获取数字包,例如通过网络。这样做的目的是让它们一直快速分类。例如,您可以使用ints获取这些数据包,但实际情况是按顺序使用double: A = [1, 3, 4, 6] B = [1, 2, 3] C = [2, 3, 5] A = [2, 4, 7, 8] 等等。在第一个数据包之后,聚合器中的数据结构将已排序。数据结构会记住排序中的每个数字所指的源: [
A = [1, 3, 4, 6]
B = [1, 2, 3]
C = [2, 3, 5]
A = [2, 4, 7, 8]
等等。在第一个数据包之后,聚合器中的数据结构将已排序。数据结构会记住排序中的每个数字所指的源:
[1,3,4,6]=>事件
在下一个数据包之后,因为它是一个新的源,所以数据结构将如下所示
[1,1,2,3,3,4,6]=>事件
下一包之后
[1,1,2,2,3,3,4,5,6]=>事件
现在,由于一个新的数据包被发送了,我们必须找到A的旧值,并用新值替换它们,最后得到一个新的排序。更换和分拣可以单独进行,也可以不在现场进行,目标是极速:
[1,2,2,2,3,3,4,5,7,8]=>事件
请注意,当您获得第二个A时,在维护排序的同时,必须用新的As数据包替换所有旧的As。在将每个数据包排序到数据结构中后,将对其进行复制,并需要将其作为事件发送。每几微秒,这些数据包就会在合并排序算法中不断地狂暴地出现
*实现这一点的最佳数据结构是什么?也许是八字树还是AVL树* 对于您的特定用途,我想这不会是最快的数据结构和算法,但它可能已经足够快了。自己测试一下 请注意,std::forward_列表甚至std::vector可能更快,具体取决于实际场景->大O表示法中的常量因子 提到了另一种方法:根据场景的不同,按需合并可能会更快,例如单独存储所有数据集并将它们合并为一个向量以传递给事件处理程序,或者甚至使用合并迭代器,其增量获取单个数据集的下一个元素 通过使用自定义内存池->自定义分配器,可以进一步提高性能
#include <set>
#include <iostream>
#include <iterator>
#include <algorithm>
// inserts a sorted range into the `to` container
template < typename To, typename InputIt >
void insert_new_sorted(To& to,
InputIt beg_old, InputIt end_old,
InputIt beg_new, InputIt end_new)
{
auto const& comp = to.value_comp();
typename To::iterator i = to.begin();
// might improve performance: don't remove elements which are in both
// ranges (old and new)
while(beg_old != end_old && beg_new != end_new)
{
if(comp(*beg_old, *beg_new))
{
// remove old element
i = to.find(*beg_old); // "slow", no hint :(
i = to.erase(i);
++beg_old;
}else if(comp(*beg_new, *beg_old))
{
// insert new element
// using the hint to achieve better performance
i = to.insert(i, *beg_new);
++beg_new;
}else
{
// both equal, do nothing
++beg_new;
++beg_old;
}
}
// remove remaining old elements
for(; beg_old != end_old; ++beg_old)
{
to.erase(to.find(*beg_old)); // "slow", no hint :(
}
// insert remaining new elements
for(; beg_new != end_new; ++beg_new)
{
i = to.insert(i, *beg_new);
}
std::copy(to.begin(), to.end(),
std::ostream_iterator<typename To::value_type>(std::cout, ", "));
std::cout << std::endl;
}
int main()
{
using set_t = std::multiset<double>;
set_t const A = {1, 3, 4, 6};
set_t const B = {1, 2, 3};
set_t const C = {2, 3, 5};
set_t const A2 = {2, 4, 7, 8};
set_t result;
insert_new_sorted(result, A.end(), A.end(), A.begin(), A.end());
insert_new_sorted(result, B.end(), B.end(), B.begin(), B.end());
insert_new_sorted(result, C.end(), C.end(), C.begin(), C.end());
insert_new_sorted(result, A.begin(), A.end(), A2.begin(), A2.end());
}
输出:
1,3,4,6,,
1, 1, 2, 3, 3, 4, 6,
1, 1, 2, 2, 3, 3, 3, 4, 5, 6,
1,2,2,3,3,4,5,7,8
另一种方法:存储插入元素的迭代器,以加快擦除速度。如果您进行向下投票,至少要留下一条注释,以便OP可以改进问题。可以想象的最快算法是。每个数据包中的数字是否总是排序?aka:您的示例A、B、C和A都是从低到高排序的。如果是这样的话,您可以在排序中利用它。@CollinJSimpson双精度满足键模板参数的所有要求,默认比较std::less AFAIK。为什么它们需要在最后排序?如中所示,观察数据结构的是什么,我们如何利用,以及我们可以利用哪些属性?有几个排序数组并给观测者一个奇特的迭代器有什么错呢?DyP,谢谢你的例子。问题是,A2不是一个新的源,它与a是同一个源。因此A2的值必须替换a的值-实际上,来自源的新数据包使其在排序集中的旧状态无效。见原帖。如果您遵循数据包,您的列表应该与我给出的最终列表相同。@user1676605已修复,但在两个方面都不能实现高性能,即擦除和查找都不能接受提示。DyP,谢谢。我将测试这个版本和我天真的版本,并进行比较。如果你的版本没有我的两倍快,我会非常惊讶,尽管我的建议是tmyklebu提出的。完成后我将再次发布。更重要的是,我还学到了一些关于语言本身的趣闻。@user1676605注意tmyklebu的方法;如果您需要迭代每个事件的所有元素,那么该版本将比multiset更快,特别是如果您将单个集合存储为具有fast iteration.DyP的向量,那么在您的版本中复制的次数会更少。标准模板库喜欢复制,可能是为了保持它的功能和线程安全?但如果不需要复制,但不进行测量,则会影响性能,我不知道。