长阵列性能问题
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】:一个简单的优化是只比较字符串一次。如果A
和B
之间的差是12,那么B
和A
之间的差也是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 位签名按升序排序。最近对的签名很可能在排序列表中附近。如果两个数字的签名不同,则很容易降低两个数字之间的距离,从而在找到距离更小的数字时提供有效的分支定界过程。
【讨论】:
以上是关于长阵列性能问题的主要内容,如果未能解决你的问题,请参考以下文章