【问题标题】:Performance issues when using iterators?使用迭代器时的性能问题?
【发布时间】:2011-08-06 16:19:20
【问题描述】:

我有一个函数,它接收一个字符列表并生成下一个字典排列。为了好玩,我尝试泛化代码以使用迭代器,以及能够生成更多不同类型的排列。

template<typename ITER>
bool nextPermutation(ITER start, ITER end, std::random_access_iterator_tag)
{
    for(ITER i = end-1; i != start; --i)
    {
        if(*(i-1) < *i)
        {
            // found where can be swapped
            for(ITER j = end-1; j != (i-1); --j)
            {
                if(*(i-1) < *j)
                {
                    // found what to swap with
                    auto temp = *j;
                    *j = *(i-1);
                    *(i-1) = temp;
                    // put everything from i on into "sorted" order by reversing
                    for(ITER k = end-1; k > i; --k,++i)
                    {
                        temp = *k;
                        *k = *i;
                        *i = temp;
                    }
                    return true;
                }
            }
        }
    }
    return false;
}

但是,我遇到了一些问题,即当我不使用原始指针时,代码的性能会明显变慢。这是我的测试台:

template<typename ITER>
bool nextPermutation(ITER start, ITER end, std::random_access_iterator_tag);

template<typename ITER>
bool nextPermutation(ITER start, ITER end)
{
    return nextPermutation(start, end, std::iterator_traits<ITER>::iterator_category());
}

#define USE_VECTOR

int main(void)
{
    bool hasNext = true;
#ifdef USE_VECTOR
    std::vector<char> c;
    for(char i = '0'; i <= '9'; ++i)
    {
        c.push_back(i);
    }
    for(size_t i = 0; i < 999999 && hasNext; ++i)
    {
        hasNext = nextPermutation(c.begin(), c.end());
    }
#else
    char c[] = "0123456789";
    size_t LENGTH = 10;
    for(size_t i = 0; i < 999999 && hasNext; ++i)
    {
        hasNext = nextPermutation(c, c+LENGTH);
    }
#endif
    std::cout << "done" << std::endl;
    std::cin.ignore();
    return 0;
}

定义 USE_VECTOR 后,运行此测试台大约需要 20 秒。当我取消定义它时,代码在不到一秒的时间内运行(我没有写任何计时代码,但足以说明性能上有非常显着的差异)。

现在我的问题是,我在哪里会受到如此巨大的性能影响,这会影响使用迭代器(std::string 迭代器、std::vector 迭代器等)与原始指针的对比?

【问题讨论】:

  • 嗯,你开启优化了吗?另外,为什么不使用std::swap 之类的东西?
  • 主要是懒惰。我最初使用char* 编写代码,当我回来时,我只是修改了函数定义以便能够使用迭代器。编辑:哦,我忘记了 Visual Studio 默认情况下不优化调试版本。杜尔:(
  • 好吧,不提std::next_permutation就太残忍了……阅读标准库的实现,即使你想自己动手,也是很有教育意义的。
  • 是的,但我是从学习的角度写这篇文章的(另外,我是那些发现算法令人着迷的人之一)。我仍然对使用指针有点不舒服,当标准库中的东西混入时更是如此,所以我认为这将是一个很好的练习。
  • 如果您喜欢处理排列和组合的算法,这里对您来说更有趣:home.roadrunner.com/~hinnant/combinations.html 只阅读规范,而不是源代码,然后尝试实现它。但是源代码也在那里。

标签: c++ c++11


【解决方案1】:

在我的盒子上,这些是时间,从采用上述时间,删除cin.ignore(),并使用以下方法进行基准测试:

$ g++-4.6 -O4 -DUSE_VECTOR -std=gnu++0x t.cpp -o t
$ time for a in $(seq 1 1000); do ./t; done > /dev/null

真正的 0m10.145s 用户 0m7.204s 系统 0m1.088s

$ g++-4.6 -O4 -std=gnu++0x t.cpp -o t
$ time for a in $(seq 1 1000); do ./t; done > /dev/null

真正的 0m7.693s 用户 0m3.280s 系统 0m0.984s

** 如果你问我,那没有令人震惊的区别 **

现在是一拳:

$ g++-4.6 -O0 -std=gnu++0x t.cpp -o t
$ time for a in $(seq 1 1000); do ./t; done > /dev/null

真正的 0m29.540s 用户 0m27.294s 系统 0m0.976s

【讨论】:

    【解决方案2】:

    没有优化,由于迭代器调试繁重(_ITERATOR_DEBUG_LEVEL 在调试模式下默认为 2,也就是完全调试),代码在我的机器上也很慢。
    但是,使用/02,迭代器调试被完全禁用,代码在控制台窗口出现之前就完全执行了。
    在这里,您有一个很好的调试示例,它使事情变得更慢但更安全。 :)

    【讨论】:

    • 一般来说,调试速度较慢的另一个好处是,您不太可能遇到任何在发布模式下困扰您的令人讨厌的旧竞争条件。更安全;-)
    • @Steve:我很高兴我还不必编写多线程应用程序。 ;)
    猜你喜欢
    • 1970-01-01
    • 2022-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多