在我的机器(Quad core,8gb ram)上运行Vista x64 Business,使用Visual Studio 2008 SP1,我试图非常快地交叉两组数字。
我在C ++中实现了两种方法,在C#中实现了一种方法。到目前为止,C#方法更快,我想改进C ++方法,因此它比C#更快,我希望C ++能做到这一点。
这是C#输出:(发布版本)
Found the intersection 1000 times, in 4741.407 ms
以下是两种不同方法(Release x64 build)的初始C ++输出:
Found the intersection (using unordered_map) 1000 times, in 21580.7ms
Found the intersection (using set_intersection) 1000 times, in 22366.6ms
这是最新的C ++输出,有三种方法(Release x64 build):
最新基准:
Found the intersection of 504 values (using unordered_map) 1000 times, in 28827.6ms
Found the intersection of 495 values (using set_intersection) 1000 times, in 9817.69ms
Found the intersection of 504 values (using unordered_set) 1000 times, in 24769.1ms
因此,set_intersection方法现在比C#慢大约2倍,但比初始C ++方法快2倍。
最新的C ++代码:
Code:
// MapPerformance.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>
#include <unordered_set>
#include <boost\unordered\unordered_map.hpp>
#include "timer.h"
using namespace std;
using namespace stdext;
using namespace boost;
using namespace tr1;
int runIntersectionTest2(const vector<int>& set1, const vector<int>& set2)
{
// hash_map<int,int> theMap;
// map<int,int> theMap;
unordered_set<int> theSet;
theSet.insert( set1.begin(), set1.end() );
int intersectionSize = 0;
vector<int>::const_iterator set2_end = set2.end();
for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
{
if ( theSet.find(*iterator) != theSet.end() )
{
intersectionSize++;
}
}
return intersectionSize;
}
int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
// hash_map<int,int> theMap;
// map<int,int> theMap;
unordered_map<int,int> theMap;
vector<int>::const_iterator set1_end = set1.end();
// Now intersect the two sets by populating the map
for ( vector<int>::const_iterator iterator = set1.begin(); iterator != set1_end; ++iterator )
{
int value = *iterator;
theMap[value] = 1;
}
int intersectionSize = 0;
vector<int>::const_iterator set2_end = set2.end();
for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
{
int value = *iterator;
unordered_map<int,int>::iterator foundValue = theMap.find(value);
if ( foundValue != theMap.end() )
{
theMap[value] = 2;
intersectionSize++;
}
}
return intersectionSize;
}
int runSetIntersection(const vector<int>& set1_unsorted, const vector<int>& set2_unsorted)
{
// Create two vectors
std::vector<int> set1(set1_unsorted.size());
std::vector<int> set2(set2_unsorted.size());
// Copy the unsorted data into them
std::copy(set1_unsorted.begin(), set1_unsorted.end(), set1.begin());
std::copy(set2_unsorted.begin(), set2_unsorted.end(), set2.begin());
// Sort the data
sort(set1.begin(),set1.end());
sort(set2.begin(),set2.end());
vector<int> intersection;
intersection.reserve(1000);
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), back_inserter(intersection));
return intersection.size();
}
void createSets( vector<int>& set1, vector<int>& set2 )
{
srand ( time(NULL) );
set1.reserve(100000);
set2.reserve(1000);
// Create 100,000 values for set1
for ( int i = 0; i < 100000; i++ )
{
int value = 1000000000 + i;
set1.push_back(value);
}
// Try to get half of our values intersecting
float ratio = 200000.0f / RAND_MAX;
// Create 1,000 values for set2
for ( int i = 0; i < 1000; i++ )
{
int random = rand() * ratio + 1;
int value = 1000000000 + random;
set2.push_back(value);
}
// Make sure set1 is in random order (not sorted)
random_shuffle(set1.begin(),set1.end());
}
int _tmain(int argc, _TCHAR* argv[])
{
int intersectionSize = 0;
vector<int> set1, set2;
createSets( set1, set2 );
Timer timer;
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runIntersectionTest(set1, set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
timer.Reset();
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runSetIntersection(set1,set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
timer.Reset();
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runIntersectionTest2(set1,set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using unordered_set) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
getchar();
return 0;
}
C#代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DictionaryPerformance
{
class Program
{
static void Main(string[] args)
{
List<int> set1 = new List<int>(100000);
List<int> set2 = new List<int>(1000);
// Create 100,000 values for set1
for (int i = 0; i < 100000; i++)
{
int value = 1000000000 + i;
set1.Add(value);
}
Random random = new Random(DateTime.Now.Millisecond);
// Create 1,000 values for set2
for (int i = 0; i < 1000; i++)
{
int value = 1000000000 + (random.Next() % 200000 + 1);
set2.Add(value);
}
long start = System.Diagnostics.Stopwatch.GetTimestamp();
for (int i = 0; i < 1000; i++)
{
runIntersectionTest(set1,set2);
}
long duration = System.Diagnostics.Stopwatch.GetTimestamp() - start;
Console.WriteLine(String.Format("Found the intersection 1000 times, in {0} ms", ((float) duration * 1000.0f) / System.Diagnostics.Stopwatch.Frequency));
Console.ReadKey();
}
static int runIntersectionTest(List<int> set1, List<int> set2)
{
Dictionary<int,int> theMap = new Dictionary<int,int>(100000);
// Now intersect the two sets by populating the map
foreach( int value in set1 )
{
theMap[value] = 1;
}
int intersectionSize = 0;
foreach ( int value in set2 )
{
int count;
if ( theMap.TryGetValue(value, out count ) )
{
theMap[value] = 2;
intersectionSize++;
}
}
return intersectionSize;
}
}
}
C ++代码:
// MapPerformance.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>
#include <boost\unordered\unordered_map.hpp>
#include "timer.h"
using namespace std;
using namespace stdext;
using namespace boost;
int runIntersectionTest(vector<int> set1, vector<int> set2)
{
// hash_map<int,int> theMap;
// map<int,int> theMap;
unordered_map<int,int> theMap;
// Now intersect the two sets by populating the map
for ( vector<int>::iterator iterator = set1.begin(); iterator != set1.end(); iterator++ )
{
int value = *iterator;
theMap[value] = 1;
}
int intersectionSize = 0;
for ( vector<int>::iterator iterator = set2.begin(); iterator != set2.end(); iterator++ )
{
int value = *iterator;
unordered_map<int,int>::iterator foundValue = theMap.find(value);
if ( foundValue != theMap.end() )
{
theMap[value] = 2;
intersectionSize++;
}
}
return intersectionSize;
}
int runSetIntersection(set<int> set1, set<int> set2)
{
set<int> intersection;
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.end()));
return intersection.size();
}
int _tmain(int argc, _TCHAR* argv[])
{
srand ( time(NULL) );
vector<int> set1;
vector<int> set2;
set1.reserve(10000);
set2.reserve(1000);
// Create 100,000 values for set1
for ( int i = 0; i < 100000; i++ )
{
int value = 1000000000 + i;
set1.push_back(value);
}
// Create 1,000 values for set2
for ( int i = 0; i < 1000; i++ )
{
int random = rand() % 200000 + 1;
random *= 10;
int value = 1000000000 + random;
set2.push_back(value);
}
Timer timer;
for ( int i = 0; i < 1000; i++ )
{
runIntersectionTest(set1, set2);
}
timer.Stop();
cout << "Found the intersection (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
set<int> set21;
set<int> set22;
// Create 100,000 values for set1
for ( int i = 0; i < 100000; i++ )
{
int value = 1000000000 + i;
set21.insert(value);
}
// Create 1,000 values for set2
for ( int i = 0; i < 1000; i++ )
{
int random = rand() % 200000 + 1;
random *= 10;
int value = 1000000000 + random;
set22.insert(value);
}
timer.Reset();
for ( int i = 0; i < 1000; i++ )
{
runSetIntersection(set21,set22);
}
timer.Stop();
cout << "Found the intersection (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
getchar();
return 0;
}
好的,这是最新的,有一些变化:
C ++(Release,x64)结果:
Found the intersection of 503 values (using unordered_map) 1000 times, in 35131.1ms
Found the intersection of 494 values (using set_intersection) 1000 times, in 10317ms
所以它比C#慢2倍。 @Jalf:你得到了一些非常快的数字,这里有什么我做错了吗?
C ++代码:
// MapPerformance.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>
#include <boost\unordered\unordered_map.hpp>
#include "timer.h"
using namespace std;
using namespace stdext;
using namespace boost;
int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
// hash_map<int,int> theMap;
// map<int,int> theMap;
unordered_map<int,int> theMap;
vector<int>::const_iterator set1_end = set1.end();
// Now intersect the two sets by populating the map
for ( vector<int>::const_iterator iterator = set1.begin(); iterator != set1_end; ++iterator )
{
int value = *iterator;
theMap[value] = 1;
}
int intersectionSize = 0;
vector<int>::const_iterator set2_end = set2.end();
for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
{
int value = *iterator;
unordered_map<int,int>::iterator foundValue = theMap.find(value);
if ( foundValue != theMap.end() )
{
theMap[value] = 2;
intersectionSize++;
}
}
return intersectionSize;
}
int runSetIntersection(const vector<int> set1_unsorted, const vector<int> set2_unsorted)
{
// Create two vectors
std::vector<int> set1(set1_unsorted.size());
std::vector<int> set2(set2_unsorted.size());
// Copy the unsorted data into them
std::copy(set1_unsorted.begin(), set1_unsorted.end(), set1.begin());
std::copy(set2_unsorted.begin(), set2_unsorted.end(), set2.begin());
// Sort the data
sort(set1.begin(),set1.end());
sort(set2.begin(),set2.end());
vector<int> intersection;
intersection.reserve(1000);
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.end()));
return intersection.size();
}
void createSets( vector<int>& set1, vector<int>& set2 )
{
srand ( time(NULL) );
set1.reserve(100000);
set2.reserve(1000);
// Create 100,000 values for set1
for ( int i = 0; i < 100000; i++ )
{
int value = 1000000000 + i;
set1.push_back(value);
}
// Try to get half of our values intersecting
float ratio = 200000.0f / RAND_MAX;
// Create 1,000 values for set2
for ( int i = 0; i < 1000; i++ )
{
int random = rand() * ratio + 1;
int value = 1000000000 + random;
set2.push_back(value);
}
// Make sure set1 is in random order (not sorted)
random_shuffle(set1.begin(),set1.end());
}
int _tmain(int argc, _TCHAR* argv[])
{
int intersectionSize = 0;
vector<int> set1, set2;
createSets( set1, set2 );
Timer timer;
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runIntersectionTest(set1, set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
timer.Reset();
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runSetIntersection(set1,set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
getchar();
return 0;
}
答案 0 :(得分:27)
您的测试有几个问题。
首先,您不是测试集合交集,而是“创建几个数组,用随机数填充它们,然后执行集合交集”。你应该只对你真正感兴趣的代码部分计时。即使你想要做那些事情,也不应该在这里进行基准测试。一次测量一件事,以减少不确定性。如果您希望C ++实现更好地执行,首先需要知道它的哪个部分比预期慢。这意味着您必须将设置代码与交叉点测试分开。
其次,您应该多次运行测试以考虑缓存效果和其他不确定性。 (并且可能输出一个总时间,例如1000次运行,而不是每次运行的单独时间。这样,您可以减少定时器的不确定性,这可能会限制分辨率并在0-20ms范围内使用时报告不准确的结果。< / p>
此外,就我可以从文档中读取而言,应该对set_intersection的输入进行排序,而set2将不会。似乎没有理由使用unordered_map
,而unordered_set
会更好地匹配您所做的事情。
关于所需的设置代码,请注意您可能不需要填充向量才能运行交集。你自己的实现和set_intersection
都已经在迭代器上工作了,所以你可以简单地将一对迭代器传递给你的输入所在的数据结构。
关于您的代码的一些更具体的评论:
++iterator
代替iterator++
unordered_set
进行实验(不是unordered_map
)修改强>
我没有尝试过您的C#版本,所以我无法正确比较这些数字,但这是我的修改后的测试。每个运行1000次,在Core 2 Quad 2.5GHz上使用4GB RAM:
std::set_intersection on std::set: 2606ms
std::set_intersection on tr1::unordered_set: 1014ms
std::set_intersection on sorted vectors: 171ms
std::set_intersection on unsorted vectors: 10140ms
最后一个有点不公平,因为它必须复制和排序向量。理想情况下,只有排序应该是基准的一部分。我尝试创建一个使用1000个未排序向量的数组的版本(因此我不必在每次迭代中复制未排序的数据),但性能大致相同或稍差,因为这会导致持续缓存未命中,所以我又回到了这个版本
我的代码:
#define _SECURE_SCL 0
#include <ctime>
#include <vector>
#include <set>
#include <iostream>
#include <algorithm>
#include <unordered_set>
#include <windows.h>
template <typename T, typename OutIter>
void stl_intersect(const T& set1, const T& set2, OutIter out){
std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), out);
}
template <typename T, typename OutIter>
void sort_stl_intersect(T& set1, T& set2, OutIter out){
std::sort(set1.begin(), set1.end());
std::sort(set2.begin(), set2.end());
std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), out);
}
template <typename T>
void init_sorted_vec(T first, T last){
for ( T cur = first; cur != last; ++cur)
{
int i = cur - first;
int value = 1000000000 + i;
*cur = value;
}
}
template <typename T>
void init_unsorted_vec(T first, T last){
for ( T cur = first; cur != last; ++cur)
{
int i = rand() % 200000 + 1;
i *= 10;
int value = 1000000000 + i;
*cur = value;
}
}
struct resize_and_shuffle {
resize_and_shuffle(int size) : size(size) {}
void operator()(std::vector<int>& vec){
vec.resize(size);
}
int size;
};
int main()
{
srand ( time(NULL) );
std::vector<int> out(100000);
std::vector<int> sortedvec1(100000);
std::vector<int> sortedvec2(1000);
init_sorted_vec(sortedvec1.begin(), sortedvec1.end());
init_unsorted_vec(sortedvec2.begin(), sortedvec2.end());
std::sort(sortedvec2.begin(), sortedvec2.end());
std::vector<int> unsortedvec1(sortedvec1.begin(), sortedvec1.end());
std::vector<int> unsortedvec2(sortedvec2.begin(), sortedvec2.end());
std::random_shuffle(unsortedvec1.begin(), unsortedvec1.end());
std::random_shuffle(unsortedvec2.begin(), unsortedvec2.end());
std::vector<int> vecs1[1000];
std::vector<int> vecs2[1000];
std::fill(vecs1, vecs1 + 1000, unsortedvec1);
std::fill(vecs2, vecs2 + 1000, unsortedvec2);
std::set<int> set1(sortedvec1.begin(), sortedvec1.end());
std::set<int> set2(sortedvec2.begin(), sortedvec2.end());
std::tr1::unordered_set<int> uset1(sortedvec1.begin(), sortedvec1.end());
std::tr1::unordered_set<int> uset2(sortedvec2.begin(), sortedvec2.end());
DWORD start, stop;
DWORD delta[4];
start = GetTickCount();
for (int i = 0; i < 1000; ++i){
stl_intersect(set1, set2, out.begin());
}
stop = GetTickCount();
delta[0] = stop - start;
start = GetTickCount();
for (int i = 0; i < 1000; ++i){
stl_intersect(uset1, uset2, out.begin());
}
stop = GetTickCount();
delta[1] = stop - start;
start = GetTickCount();
for (int i = 0; i < 1000; ++i){
stl_intersect(sortedvec1, sortedvec2, out.begin());
}
stop = GetTickCount();
delta[2] = stop - start;
start = GetTickCount();
for (int i = 0; i < 1000; ++i){
sort_stl_intersect(vecs1[i], vecs1[i], out.begin());
}
stop = GetTickCount();
delta[3] = stop - start;
std::cout << "std::set_intersection on std::set: " << delta[0] << "ms\n";
std::cout << "std::set_intersection on tr1::unordered_set: " << delta[1] << "ms\n";
std::cout << "std::set_intersection on sorted vectors: " << delta[2] << "ms\n";
std::cout << "std::set_intersection on unsorted vectors: " << delta[3] << "ms\n";
return 0;
}
没有理由为什么C ++应该总是比C#更快。 C#具有一些关键优势,需要在C ++中与之竞争。
我能想到的主要原因是动态分配在.NET-land中非常便宜。每次C ++向量,set或unordered_set(或任何其他容器)都必须调整大小或扩展时,这是一个非常昂贵的malloc
操作。在.NET中,堆分配只不过是为指针添加偏移量。
因此,如果您希望C ++版本竞争,您可能必须解决这个问题,允许您的容器调整大小而不必执行实际的堆分配,可能是通过为容器使用自定义分配器(也许boost :: pool可能是一个不错的选择,或者你可以尝试自己动手)
另一个问题是set_difference
仅适用于排序输入,并且为了重现涉及排序的测试结果,我们必须在每次迭代中制作未排序数据的新副本,这是昂贵的(尽管再次,使用自定义分配器将有很大帮助)。我不知道您的输入采用何种形式,但您可以直接对输入进行排序,而无需复制,然后直接在其上运行set_difference
。 (如果您的输入至少是数组或STL容器,那将很容易做到。)
STL的一个主要优点是它非常灵活,几乎可以在任何输入序列上运行。在C#中,您几乎必须将输入复制到List或Dictionary或其他东西,但在C ++中,您可能能够在原始输入上运行std::sort
和set_intersection
。
最后,当然,尝试通过分析器运行代码,并确切地查看花费的时间。您可能还想尝试通过GCC运行代码。我的印象是,MSVC中的STL性能有时有点古怪。可能值得在另一个编译器下测试,看看你是否在那里得到类似的时间。
最后,您可能会发现这些博客帖子与C ++与C#的性能相关: http://blogs.msdn.com/ricom/archive/2005/05/10/416151.aspx
这些人的士气基本上是肯定的,你可以在C ++中获得更好的表现,但这是一项令人惊讶的工作量。
答案 1 :(得分:9)
我马上看到的一个问题是你是按值传递C ++中的集合,而不是通过const引用。因此,每次传递它们时都要复制它们!
另外,我不会为set_intersection
的目标使用集合。我会使用像
int runSetIntersection(const set<int>& set1, const set<int>& set2)
{
vector<int> intersection;
intersection.reserve(10000) // or whatever the max is
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), back_inserter(intersection));
return intersection.size();
}
但是,此代码仍在函数内部分配。
更快int runSetIntersection(const set<int>& set1, const set<int>& set2, vector<int>& scratch)
{
scratch.reserve(10000) // or whatever the max is
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), back_inserter(scratch));
return scratch.size();
}
然后在启动计时器之前分配临时。
但是,如果你只是在寻找大小,手写的for循环,结合set :: find可能会给出更好的结果。
答案 2 :(得分:4)
使用此...
vector<int> set1(10000);
vector<int> set2(1000);
...获得非零初始大小的向量。然后不要使用push_back,而只是直接更新值。
答案 3 :(得分:2)
我会更改C ++“runIntersectionTest”以对容器进行const引用,而不是在每次调用时对它们进行复制构造。 (C#代码将使用refs。)
答案 4 :(得分:2)
查看boost Disjoint Set容器也是值得的,该容器专门针对某些类型的大型集合操作进行了优化。
它的工作原理是将一组集合视为几个不相交集合的集合,这样就可以在构建初始的不相交集合后,以非常便宜的方式构建其他集合,例如交叉点或联合。如果您希望在不会发生太大变化的集合上进行大量的集合操作,那么您可能会认为这非常快。另一方面,如果你将使用每一套并扔掉它,它可能不会做太多。
无论如何,你至少要亲自尝试一下,看看它是否会在你的具体情况下给你带来任何影响。
答案 5 :(得分:2)
顺便说一下,如果你有大的排序集std :: set_intersection不是最快的算法。 std :: set_intersection最多需要2 *(m + n)-1次比较,但像Baeza-Yates这样的算法可以更快。对于小m,Baeza-Yates是O(m * log(n)),而对于n = alpha * m,它是O(n)。基本思想是进行一种二向二分搜索。
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.91.7899&rep=rep1&type=pdf
排序序列快速交叉算法的实验分析 Ricardo Baeza-Yates和Alejandro Salinger
OR
R上。巴埃萨 - 耶茨。一种用于排序序列的快速集合交集算法。在 第15届组合模式匹配年会研讨会论文集 (CPM 2004),Springer LNCS 3109,pp 400-408,土耳其伊斯坦布尔,2004年7月。
以下是Erik Frey的解释和实现,其中显示的结果比使用二进制探针的std :: set_intersection快得多。我还没有尝试过他的代码。
http://fawx.com/
/*
* baeza_intersect
*/
template< template class Probe,
class RandomAccessIterator, class OutputIterator>
void baeza_intersect(RandomAccessIterator begin1, RandomAccessIterator end1,
RandomAccessIterator begin2, RandomAccessIterator end2,
OutputIterator out)
{
RandomAccessIterator probe1, probe2;
if ( (end1 - begin1) < ( end2 - begin2 ) )
{
if ( begin1 == end1 )
return;
probe1 = begin1 + ( ( end1 - begin1 ) >> 1 );
probe2 = lower_bound< Probe >( begin2, end2, *probe1 );
baeza_intersect< Probe >(begin1, probe1, begin2, probe2, out); // intersect left
if (! (probe2 == end2 || *probe1 < *probe2 ))
*out++ = *probe2++;
baeza_intersect< Probe >(++probe1, end1, probe2, end2, out); // intersect right
}
else
{
if ( begin2 == end2 )
return;
probe2 = begin2 + ( ( end2 - begin2 ) >> 1 );
probe1 = lower_bound< Probe >( begin1, end1, *probe2 );
baeza_intersect< Probe >(begin1, probe1, begin2, probe2, out); // intersect left
if (! (probe1 == end1 || *probe2 < *probe1 ))
*out++ = *probe1++;
baeza_intersect< Probe >(probe1, end1, ++probe2, end2, out); // intersect right
}
}
/*
* with a comparator
*/
template< template class Probe,
class RandomAccessIterator, class OutputIterator, class Comparator >
void baeza_intersect(RandomAccessIterator begin1, RandomAccessIterator end1,
RandomAccessIterator begin2, RandomAccessIterator end2,
OutputIterator out, Comparator cmp)
{
RandomAccessIterator probe1, probe2;
if ( (end1 - begin1) < ( end2 - begin2 ) )
{
if ( begin1 == end1 )
return;
probe1 = begin1 + ( ( end1 - begin1 ) >> 1 );
probe2 = lower_bound< Probe >( begin2, end2, *probe1, cmp );
baeza_intersect< Probe >(begin1, probe1, begin2, probe2, out, cmp); // intersect left
if (! (probe2 == end2 || cmp( *probe1, *probe2 ) ))
*out++ = *probe2++;
baeza_intersect< Probe >(++probe1, end1, probe2, end2, out, cmp); // intersect right
}
else
{
if ( begin2 == end2 )
return;
probe2 = begin2 + ( ( end2 - begin2 ) >> 1 );
probe1 = lower_bound< Probe >( begin1, end1, *probe2, cmp );
baeza_intersect< Probe >(begin1, probe1, begin2, probe2, out, cmp); // intersect left
if (! (probe1 == end1 || cmp( *probe2, *probe1 ) ))
*out++ = *probe1++;
baeza_intersect< Probe >(probe1, end1, ++probe2, end2, out, cmp); // intersect right
}
}
// probe.hpp
/**
* binary probe: pick the next element by choosing the halfway point between low and high
*/
template< class RandomAccessIterator, class T >
struct binary_probe
{
RandomAccessIterator operator()(RandomAccessIterator begin, RandomAccessIterator end, const T & value)
{
return begin + ( (end - begin) >> 1);
}
};
/**
* lower_bound: like stl's lower_bound but with different kinds of probing
* note the appearance of the rare template parameter template!
*/
template< template class Probe, class RandomAccessIterator, class T >
RandomAccessIterator lower_bound(RandomAccessIterator begin, RandomAccessIterator end, const T & value)
{
RandomAccessIterator pit;
Probe< RandomAccessIterator, T > pfunc; // probe-functor (wants to get func'd up)
while ( begin < end )
{
pit = pfunc(begin, end, value);
if ( *pit < value )
begin = pit + 1;
else
end = pit;
}
return begin;
}
/*
* this time with a comparator!
*/
template< template class Probe, class RandomAccessIterator, class T, class Comparator >
RandomAccessIterator lower_bound(RandomAccessIterator begin, RandomAccessIterator end, const T & value, Comparator cmp)
{
RandomAccessIterator pit;
Probe< RandomAccessIterator, T > pfunc;
while ( begin < end )
{
pit = pfunc(begin, end, value);
if ( cmp(*pit, value) )
begin = pit + 1;
else
end = pit;
}
return begin;
}
答案 6 :(得分:1)
由于您使用的是Visual Studio,因此应检查是否将_SECURE_SCL
设置为1(通常,如果您未明确设置它,则为1)。如果已设置,则所有STL代码都将进行范围检查,即使在发布版本中也是如此。通常将代码减慢10-15%。
似乎微软并不知道例如std :: vector已经有了一个接口,如果你想要范围检查:std :: vector :: at()!
(对不起,不得不把它从胸前拿走)。
无论如何,主要的低效率是你正在复制容器而不是按值传递容器。使用参考(尝试)比较苹果和苹果而不是苹果和香蕉。
答案 7 :(得分:0)
答案 8 :(得分:0)
C ++优化标志是否已打开?
答案 9 :(得分:0)
好的,经过多次反馈后,我已多次更新原始问题:
到目前为止,结果是C#仍然比C ++快〜5倍。
感谢大家的意见/建议。
答案 10 :(得分:0)
更新
我修改了set_intersection代码以使用向量,并对它们进行排序(而不是使用有序集合类),现在它的速度更快了:
Found the intersection of 319 values (using unordered_map) 1000 times, in 22187.5ms
Found the intersection of 315 values (using set_intersection) 1000 times, in 2401.62ms
请记住:较大的集合已创建排序,因此在此示例中排序可能不会花费太多时间。
C ++代码:
// MapPerformance.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>
#include <boost\unordered\unordered_map.hpp>
#include "timer.h"
using namespace std;
using namespace stdext;
using namespace boost;
int runIntersectionTest(vector<int> set1, vector<int> set2)
{
// hash_map<int,int> theMap;
// map<int,int> theMap;
unordered_map<int,int> theMap;
// Now intersect the two sets by populating the map
for ( vector<int>::iterator iterator = set1.begin(); iterator != set1.end(); iterator++ )
{
int value = *iterator;
theMap[value] = 1;
}
int intersectionSize = 0;
for ( vector<int>::iterator iterator = set2.begin(); iterator != set2.end(); iterator++ )
{
int value = *iterator;
unordered_map<int,int>::iterator foundValue = theMap.find(value);
if ( foundValue != theMap.end() )
{
theMap[value] = 2;
intersectionSize++;
}
}
return intersectionSize;
}
int runSetIntersection(vector<int> set1, vector<int> set2)
{
sort(set1.begin(),set1.end());
sort(set2.begin(),set2.end());
set<int> intersection;
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), inserter(intersection, intersection.end()));
return intersection.size();
}
int _tmain(int argc, _TCHAR* argv[])
{
srand ( time(NULL) );
vector<int> set1;
vector<int> set2;
set1.reserve(10000);
set2.reserve(1000);
// Create 100,000 values for set1
for ( int i = 0; i < 100000; i++ )
{
int value = 1000000000 + i;
set1.push_back(value);
}
// Create 1,000 values for set2
for ( int i = 0; i < 1000; i++ )
{
int random = rand() % 200000 + 1;
random *= 10;
int value = 1000000000 + random;
set2.push_back(value);
}
int intersectionSize = 0;
Timer timer;
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runIntersectionTest(set1, set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
timer.Reset();
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runSetIntersection(set1,set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
getchar();
return 0;
}
答案 11 :(得分:0)
你仍然按值传递向量。如果你没有复制它们也没关系。
插入器没有将值放在向量的末尾,快速。它只在第一个插入后执行,它在数组的开头插入值(用于指向的结尾)。
在更新值时,您在哈希映射版本中查找值的位置。为什么要更新此值事件?
运行此代码并发布您的时间。
// MapPerformance.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>
#include <boost\unordered\unordered_set.hpp>
#include "timer.h"
using namespace std;
using namespace stdext;
using namespace boost;
int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
// hash_map<int,int> theMap;
// map<int,int> theMap;
unordered_set<int> theSet;
theSet.insert( set1.begin(), set2.end() );
int intersectionSize = 0;
vector<int>::const_iterator set2_end = set2.end();
for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
{
if ( theSet.find(*iterator) != theSet.end() )
{
intersectionSize++;
}
}
return intersectionSize;
}
int runSetIntersection( vector<int> set1, vector<int> set2)
{
// Sort the data
sort(set1.begin(),set1.end());
sort(set2.begin(),set2.end());
vector<int> intersection;
intersection.reserve(1000);
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), back_inserter(intersection));
return intersection.size();
}
void createSets( vector<int>& set1, vector<int>& set2 )
{
srand ( time(NULL) );
set1.reserve(100000);
set2.reserve(1000);
// Create 100,000 values for set1
for ( int i = 0; i < 100000; i++ )
{
int value = 1000000000 + i;
set1.push_back(value);
}
// Try to get half of our values intersecting
float ratio = 200000.0f / RAND_MAX;
// Create 1,000 values for set2
for ( int i = 0; i < 1000; i++ )
{
int random = rand() * ratio + 1;
int value = 1000000000 + random;
set2.push_back(value);
}
// Make sure set1 is in random order (not sorted)
random_shuffle(set1.begin(),set1.end());
}
int _tmain(int argc, _TCHAR* argv[])
{
int intersectionSize = 0;
vector<int> set1, set2;
createSets( set1, set2 );
Timer timer;
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runIntersectionTest(set1, set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
timer.Reset();
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runSetIntersection(set1,set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
getchar();
return 0;
}
答案 12 :(得分:0)
最新基准:
Found the intersection of 504 values (using unordered_map) 1000 times, in 28827.6ms
Found the intersection of 495 values (using set_intersection) 1000 times, in 9817.69ms
Found the intersection of 504 values (using unordered_set) 1000 times, in 24769.1ms
我认为504 - 495之间存在差异,因为有几个欺骗值。
Code:
// MapPerformance.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <hash_map>
#include <vector>
#include <iostream>
#include <time.h>
#include <algorithm>
#include <set>
#include <unordered_set>
#include <boost\unordered\unordered_map.hpp>
#include "timer.h"
using namespace std;
using namespace stdext;
using namespace boost;
using namespace tr1;
int runIntersectionTest2(const vector<int>& set1, const vector<int>& set2)
{
// hash_map<int,int> theMap;
// map<int,int> theMap;
unordered_set<int> theSet;
theSet.insert( set1.begin(), set1.end() );
int intersectionSize = 0;
vector<int>::const_iterator set2_end = set2.end();
for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
{
if ( theSet.find(*iterator) != theSet.end() )
{
intersectionSize++;
}
}
return intersectionSize;
}
int runIntersectionTest(const vector<int>& set1, const vector<int>& set2)
{
// hash_map<int,int> theMap;
// map<int,int> theMap;
unordered_map<int,int> theMap;
vector<int>::const_iterator set1_end = set1.end();
// Now intersect the two sets by populating the map
for ( vector<int>::const_iterator iterator = set1.begin(); iterator != set1_end; ++iterator )
{
int value = *iterator;
theMap[value] = 1;
}
int intersectionSize = 0;
vector<int>::const_iterator set2_end = set2.end();
for ( vector<int>::const_iterator iterator = set2.begin(); iterator != set2_end; ++iterator )
{
int value = *iterator;
unordered_map<int,int>::iterator foundValue = theMap.find(value);
if ( foundValue != theMap.end() )
{
theMap[value] = 2;
intersectionSize++;
}
}
return intersectionSize;
}
int runSetIntersection(const vector<int>& set1_unsorted, const vector<int>& set2_unsorted)
{
// Create two vectors
std::vector<int> set1(set1_unsorted.size());
std::vector<int> set2(set2_unsorted.size());
// Copy the unsorted data into them
std::copy(set1_unsorted.begin(), set1_unsorted.end(), set1.begin());
std::copy(set2_unsorted.begin(), set2_unsorted.end(), set2.begin());
// Sort the data
sort(set1.begin(),set1.end());
sort(set2.begin(),set2.end());
vector<int> intersection;
intersection.reserve(1000);
set_intersection(set1.begin(),set1.end(), set2.begin(), set2.end(), back_inserter(intersection));
return intersection.size();
}
void createSets( vector<int>& set1, vector<int>& set2 )
{
srand ( time(NULL) );
set1.reserve(100000);
set2.reserve(1000);
// Create 100,000 values for set1
for ( int i = 0; i < 100000; i++ )
{
int value = 1000000000 + i;
set1.push_back(value);
}
// Try to get half of our values intersecting
float ratio = 200000.0f / RAND_MAX;
// Create 1,000 values for set2
for ( int i = 0; i < 1000; i++ )
{
int random = rand() * ratio + 1;
int value = 1000000000 + random;
set2.push_back(value);
}
// Make sure set1 is in random order (not sorted)
random_shuffle(set1.begin(),set1.end());
}
int _tmain(int argc, _TCHAR* argv[])
{
int intersectionSize = 0;
vector<int> set1, set2;
createSets( set1, set2 );
Timer timer;
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runIntersectionTest(set1, set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using unordered_map) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
timer.Reset();
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runSetIntersection(set1,set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using set_intersection) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
timer.Reset();
for ( int i = 0; i < 1000; i++ )
{
intersectionSize = runIntersectionTest2(set1,set2);
}
timer.Stop();
cout << "Found the intersection of " << intersectionSize << " values (using unordered_set) 1000 times, in " << timer.GetMilliseconds() << "ms" << endl;
getchar();
return 0;
}