几乎相同的代码运行速度要慢得多

Posted

技术标签:

【中文标题】几乎相同的代码运行速度要慢得多【英文标题】:Almost same code running much slower 【发布时间】:2016-04-28 13:03:32 【问题描述】:

我正在尝试解决这个问题:

给定一个字符串数组words,求length(word[i]) * length(word[j]) 的最大值,其中两个单词不共享共同的字母。您可以假设每个单词只包含小写字母。如果不存在这两个词,则返回0。

https://leetcode.com/problems/maximum-product-of-word-lengths/

您可以为每个单词创建一个 char 位图,以检查它们是否共享 chars,然后计算最大乘积。

我有两个方法差不多,但是第一个通过检查,而第二个太慢,你能理解为什么吗?

class Solution 
public:

    int maxProduct2(vector<string>& words) 
        int len = words.size();
        int *num = new int[len];
        // compute the bit O(n)
        for (int i = 0; i < len; i ++) 
            int k = 0;
            for (int j = 0; j < words[i].length(); j ++) 
                k = k | (1 <<(char)(words[i].at(j)));
            
            num[i] = k;
        
        int c = 0;
        // O(n^2)
        for (int i = 0; i < len - 1; i ++) 
            for (int j = i + 1; j < len; j ++) 
                if ((num[i] & num[j]) == 0)  // if no common letters
                    int x = words[i].length() * words[j].length();
                    if (x > c) 
                        c = x;
                    
                
            
        
        delete []num;
        return c;
    

    int maxProduct(vector<string>& words) 
        vector<int> bitmap(words.size());
        for(int i=0;i<words.size();++i) 
            int k = 0;
            for(int j=0;j<words[i].length();++j) 
                 k |= 1 << (char)(words[i][j]);
            
            bitmap[i] = k;
        

        int maxProd = 0;
        for(int i=0;i<words.size()-1;++i) 
            for(int j=i+1;j<words.size();++j) 
                if ( !(bitmap[i] & bitmap[j])) 
                    int x = words[i].length() * words[j].length();
                    if ( x > maxProd  )
                        maxProd = x;
                
            
        
        return maxProd;
    
;

为什么第二个函数(maxProduct)对于 leetcode 来说太慢了?

解决方案

第二种方法重复调用words.size()。如果您将其保存在 var 中,则它可以正常工作

【问题讨论】:

好吧,你有一个循环来做(字数)²检查;这将是一个开始...有多少字? 你也称之为位图,但实际上它是一个vector&lt;int&gt;,而不是由可单独寻址的位组成的东西。 考虑所有单词都有共同字母的情况,并计算每个函数的乘法次数。 @molbdnilo:现在乘法只在 if 之后完成,就像第一个代码一样。但还是太慢了 @MarcusMüller:和第一个差不多(第一个使用数组) 【参考方案1】:

由于我的评论被证明是正确的,我会将我的评论变成答案并尝试解释我认为正在发生的事情。

我编写了一些简单的代码来在我自己的机器上进行基准测试,其中包含两个循环的两个解决方案。唯一的区别是对 words.size() 的调用是在循环内部而不是在循环外部。第一个解决方案大约为 13.87 秒,而第二个解决方案为 16.65 秒。这不是很大,但速度会慢 20%。

尽管 vector.size() 是一个恒定时间操作,但这并不意味着它与检查寄存器中已经存在的变量一样快。恒定时间仍然可以有很大的差异。在加起来的嵌套循环内时。

可能发生的另一件事(比我聪明得多的人可能会插话并让我们知道)是您正在损害您的 CPU 优化,例如分支和流水线。每次到达循环结束时,它必须停止,等待对 size() 的调用返回,然后根据该返回值检查循环变量。如果 CPU 可以向前看并猜测 j 仍然会小于 len,因为它没有看到 len 发生变化(len 甚至不在循环内!)每次都进行分支预测,无需等待。

【讨论】:

不确定你是如何测试它的,但是vector版本零初始化bitmap,而另一个缓冲区在分配之前未初始化。 @jarod42 我用一个我在计时之前初始化的向量进行了测试,然后对两组相同的两个循环进行了计时,唯一的区别是.size() 是在比较中或完成一次第一的。第二个循环速度较慢,尽管它具有在同一个向量上第二个并做同样事情的优势。

以上是关于几乎相同的代码运行速度要慢得多的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 SQL Azure 上运行查询要慢得多?

OpenMP atomic 比对数组的关键速度要慢得多

xcodebuild 比 Xcode 慢得多?

String.replaceAll 比自己完成工作要慢得多

使用 System.Data.SQLite (C#) 的 SQLite 查询比在 SQLiteStudio 中要慢得多

使用汇总操作训练 TensorFlow 模型比不使用汇总操作要慢得多