长阵列性能问题

Posted

技术标签:

【中文标题】长阵列性能问题【英文标题】:Long array performance issue 【发布时间】:2011-10-26 01:35:18 【问题描述】:

我有一个长度为 175,000 的 char 指针数组。每个指针指向一个长度为 100 的 c 字符串数组,每个字符要么是 1 要么是 0。我需要比较字符串之间的差异。

char* arr[175000];

到目前为止,我有两个 for 循环,我将每个字符串与其他每个字符串进行比较。比较函数基本上采用两个 c 字符串并返回一个整数,该整数是数组的差异数。

这在我的 4 核机器上需要很长时间。上次我让它运行了 45 分钟,但它从未完成执行。请告知更快的解决方案或一些优化。


例子:

000010
000001

由于最后两位不匹配,因此相差 2。

在我计算出差异后,我将值存储在另一个数组中

                int holder;

                for(int x = 0;x < UsedTableSpace; x++)
                    int min = 10000000;

                    for(int y = 0; y < UsedTableSpace; y++)

                        if(x != y)
                            //compr calculates difference between two c-string arrays
                            int tempDiff =compr(similarity[x]->matrix, similarity[y]->matrix);

                            if(tempDiff < min)
                                min = tempDiff;
                                holder = y;
                            
                               
                    
                    similarity[holder]->inbound++;

                

【问题讨论】:

"我有一个长度为 175,000 的 char 指针数组。每个指针指向一个长度为 100 的 c 字符串数组,每个字符为 1 或 0。"这个设计可以修改吗?这似乎极其效率低下。 答案完全取决于“比较字符串之间的差异”的确切含义。平等?更大?还有什么? 还有:C 还是 C++?它们不可互换。 第一:使用bitset/dynamic_bitset;第二:你在比较什么,结果在哪里? (无论如何,结果是什么?)根据这一点,您也许可以使用常见的优化全文搜索算法 你需要知道所有对之间的距离,还是别的什么?例如,找到与目标字符串的编辑距离为 n 的所有字符串的快速方法有用吗? 【参考方案1】:

有了更多信息,我们可能会给你更好的建议,但根据我对这个问题的理解,这里有一些想法:

    由于您使用每个字符来表示 1 或 0,因此您使用的内存是您需要使用的数倍,这在缓存等方面会产生很大的性能影响。取而代之的是,您可以使用数字值来表示您的数据,您可以将其视为一系列位。 一旦你实现了#1,你可以一次抓取一个整数或长整数,然后进行按位异或运算,得到一个在两个数字没有的地方都有一个 1 的数字相同的值。然后你可以使用一些the tricks mentioned here 来快速计算这些位。

    在一定程度上“展开”循环以避免必要的跳转次数。例如以下代码:

    total = total + array[i];
    total = total + array[i + 1];
    total = total + array[i + 2];
    

    ... 会比只循环 total = total + array[i] 三次更快。跳转很昂贵,并且会干扰处理器的流水线。 更新:我应该提一下,您的编译器可能已经为您完成了一些工作——您可以检查编译后的代码来查看。

    将您的整个数据集分解成块,这样您就可以充分利用缓存。将您的问题想象成一个“正方形”,一个轴上的i 索引和另一个轴上的j 轴。如果您从一个i 开始并遍历所有175000 个j 值,那么当您到达行尾时,您访问的第一个j 值将从缓存中消失。另一方面,如果您从左上角从 j=0 转到 256,则在您循环以将它们与 i= 进行比较时,j 轴上的大多数值仍将位于低级缓存中0、1、2 等。

最后,虽然这不言而喻,但我想值得一提:确保您的编译器设置为优化!

【讨论】:

将两个字符串转换为长整数、异或和做一些位魔术对我来说似乎比只比较两个数组要慢。 @vz0:GMan 是对的。理想情况下,您可以首先将它们表示为数值。但即使不是,将它们转换为整数也需要一次通过数组,这将是整个函数成本的 (1/175000)th。因此,如果按位魔法使事情变得快 2 倍,那将产生巨大的影响。我的猜测是这些技术的组合将使事情变得快几倍。【参考方案2】:

一个简单的优化是只比较字符串一次。如果AB 之间的差是12,那么BA 之间的差也是12。您的运行时间将减少近一半。

在代码中:

int compr(const char* a, const char* b) 
  int d = 0, i;
  for (i=0; i < 100; ++i)
    if (a[i] != b[i]) ++d;
  return d;


void main_function(...) 

    for(int x = 0;x < UsedTableSpace; x++)
        int min = 10000000;

        for(int y = x + 1; y < UsedTableSpace; y++)

            //compr calculates difference between two c-string arrays
            int tempDiff = compr(similarity[x]->matrix, similarity[y]->matrix);

            if(tempDiff < min)
                min = tempDiff;
                holder = y;
            
        
        similarity[holder]->inbound++;
    

注意第二个for 循环,我已经更改了开始索引。

其他一些优化是在单独的线程上运行 run 方法以利用您的 4 个内核。

【讨论】:

感谢您的回复,请详细说明。运行运行命令。 我在你发布双循环代码之前回复了。我已经更新了我的答案。 +1:这个单一的优化可能会以最少的努力提供最大的改进。【参考方案3】:

你的目标是什么,在你得到Hamming Distances(它们是什么)之后,你想做什么?例如,如果您正在寻找最近的对或最远的对,您可能可以获得 O(n ln n) 算法,而不是目前建议的 O(n^2) 方法。 (在 n=175000 时,n^2 是 n ln n 的 15000 倍。)

例如,您可以用 8 个 4 位数字(即 m 的 8 段中设置的位数)来表征每个 100 位数字 m,并将生成的 32 位签名按升序排序。最近对的签名很可能在排序列表中附近。如果两个数字的签名不同,则很容易降低两个数字之间的距离,从而在找到距离更小的数字时提供有效的分支定界过程。

【讨论】:

以上是关于长阵列性能问题的主要内容,如果未能解决你的问题,请参考以下文章

提高 3d 阵列的性能

列表或单线阵列/参数:一个或另一个性能更好吗?

对于 VS Foreach 的阵列性能(在 AS3/Flex 中)

双循环性能问题

javascript 本机阵列性能测试

阵列处理性能