当对的数量非常巨大时,如何替换(或加速)并行比较?

时间:2013-05-31 01:50:11

标签: c++ data-structures

当地图中的密钥数量很大(例如100000)并且其第二个元素中的每个元素都具有巨大元素(例如100000)时,下面的代码的运行时间(并行比较)将永远存在。

有没有办法加速比较?我的CPU是Xeon E5450 3.00G 4核心。拉姆是公平的。

// There is a map with long as its key and vector<long> as second element, 
//     the vector's elements are increasing sorted.
map<long, vector<long> > = aMap() ;
map<long, vector<long> >::iterator it1 = aMap.begin() ;
map<long, vector<long> >::iterator it2; 

// the code need compare each key's second elements 
for( ; it1 != aMap.end(); it1++ ) {
  it2 = it1; 
  it2++;

  // Parallel comparsion: THE MOST TIME CONSUMING PART
  for( ; it2 != aMap.end(); it2++ ) {
    unsigned long i = 0, j = 0, _union = 0, _inter = 0 ;

    while( i < it1->second.size() && j < it2->second.size() ) {
      if( it1->second[i] < it2->second[j] ) {
        i++; 
      } else if( it1->second[i] > it2->second[j] ) {
        j++; 
      } else {
        i++; j++; _inter++;
      }
    }
    _union = it1->second.size() + it2->second.size() - _inter;

    if ( (double) _inter / _union > THRESH )
      cout << it1->first << " might appears frequently with " << it2->first << endl;
  }
}

2 个答案:

答案 0 :(得分:2)

(这不是一个完整的答案。它只解决了部分问题;关于位操作的部分。)

这是一个你可以用来计算两组之间的交叉点数(交叉点的基数)的类很快。

它使用位向量来存储集合,这意味着可能的集合成员的Universe必须很小。

#include <cassert>
#include <vector>

class BitVector
{
    // IMPORTANT: U must be unsigned
    // IMPORTANT: use unsigned long long in 64-bit builds.
    typedef unsigned long U;
    static const unsigned UBits = 8 * sizeof(U);

public:
    BitVector (unsigned size)
        : m_bits ((size + UBits - 1) / UBits, 0)
        , m_size (size)
    {
    }

    void set (unsigned bit)
    {
        assert (bit < m_size);
        m_bits[bit / UBits] |= (U)1 << (bit % UBits);
    }

    void clear (unsigned bit)
    {
        assert (bit < m_size);
        m_bits[bit / UBits] &= ~((U)1 << (bit % UBits));
    }

    unsigned countIntersect (BitVector const & that) const
    {
        assert (m_size == that.m_size);

        unsigned ret = 0;
        for (std::vector<U>::const_iterator i = m_bits.cbegin(),
             j = that.m_bits.cbegin(), e = m_bits.cend(), f = that.m_bits.cend();
             i != e && j != f; ++i, ++j)
        {
            U x = *i & *j;

            // Count the number of 1 bits in x and add it to ret
            // There are much better ways than this,
            // e.g. using the POPCNT instruction or intrinsic
            while (x != 0)
            {
                ret += x & 1;
                x >>= 1;
            }
        }

        return ret;
    }

    unsigned countUnion (BitVector const & that) const
    {
        assert (m_size == that.m_size);

        unsigned ret = 0;
        for (std::vector<U>::const_iterator i = m_bits.cbegin(),
             j = that.m_bits.cbegin(), e = m_bits.cend(), f = that.m_bits.cend();
             i != e && j != f; ++i, ++j)
        {
            U x = *i | *j;

            while (x != 0)
            {
                ret += x & 1;
                x >>= 1;
            }
        }

        return ret;
    }

private:
    std::vector<U> m_bits;
    unsigned m_size;
};

这是一个非常小的测试程序,看看你如何使用上面的类。它产生两组(每组最多100K),向它们添加一些项目(使用set()成员函数),然后计算它们的交集10亿次。它在我的机器上运行不到两秒钟。

#include <iostream>

using namespace std;

int main ()
{
    unsigned const SetSize = 100000;
    BitVector a (SetSize), b (SetSize);

    for (unsigned i = 0; i < SetSize; i += 2) a.set (i);
    for (unsigned i = 0; i < SetSize; i += 3) b.set (i);

    unsigned x = a.countIntersect (b);
    cout << x << endl;

    return 0;
}

不要忘记在启用优化的情况下编译它!否则它的表现非常糟糕。

POPCNT

现代处理器有一条指令来计算一个字中的设置位数,称为POPCNT。这比上面写的天真的东西要快得多(作为旁注,在软件中也有更快的方法,但是我不想污染代码。)

无论如何,在C / C ++代码中使用POPCNT的方法是使用编译器内在内置。在MSVC中,您可以使用适用于32位整数的__popcount()内在函数。在GCC中,您可以将__builtin_popcountl()用于32位整数,将__builtin_popcountll()用于64位。请注意,由于您的编译器版本,目标体系结构和/或编译开关,这些功能可能无法使用。

答案 1 :(得分:0)

也许您想尝试PPL。或者它的一些类似物。我真的不明白你的代码想要做什么,因为它似乎没有任何输出。但是,可以使用Parallel Patterns Library等工具有效地并行化副作用免费代码。