通过数组最有效的方法?

时间:2014-05-31 04:56:23

标签: c arrays iteration

我一直在想,并且想知道最快的算法是什么来通过一个(大的 - 比说说10,000个大小)unsorted int数组的每个元素。我的第一个想法是通过线性运动并一次检查每个元素 - 然后我的思绪徘徊到递归,并想知道是否每次都将数组切成平行并检查元素是否正常。

我试图找出的目标是一个数字(在这种数组中)是否是一个看似随机的数字"生成int。然后在此之后我将尝试查找大数组的子集是否也等于此数字的倍数。 (但我会在另一天到达那一部分!)

你有什么想法?有问题吗?评论?顾虑?

5 个答案:

答案 0 :(得分:4)

您似乎错误地认为,顺序运行数组的瓶颈是CPU:它不是,它是您的内存总线。现代平台非常善于预测顺序访问并尽一切可能简化访问,您不能做更多的事情。并行化通常没有用,因为你只有一个内存总线,这是瓶颈,相反,你冒着错误共享的风险,甚至可能会变得更糟。

如果由于某种原因你在数组的每个元素上进行了大量的计算,那么图片会发生变化。然后,你可以开始尝试一些平行的东西了。

答案 1 :(得分:0)

对于未排序的数组,线性搜索就像您可以做的一样好。每次切割数组然后搜索元素对你没有多大帮助,相反,它可能会减慢你的程序,因为调用函数需要堆栈维护。

答案 2 :(得分:0)

在单个线程中处理连续数组的每个元素的最有效方法是顺序执行。所以最简单的解决方案是最好的。启用编译器优化可能会对简单的迭代代码产生重大影响。

但是,如果您有多个内核和非常大的阵列,则可以通过将任务分成单独的线程来实现更高的效率。正如所建议的那样,使用专门用于执行并行处理的库可能更好,更具确定性,只需使用OS支持进行线程处理。

另一种可能性是将任务卸载到GPU,但这是特定于硬件的并且需要GPU库支持,例如CUDA。

所有这些说10000个元素似乎并不多 - 你需要它多快,它目前需要多长时间?如果表现特别感兴趣,您需要对此进行测量。

答案 3 :(得分:0)

如果你想对数组的每个元素执行某种任务,那么除了访问每个元素一次之外,它不可能做得更好;如果你确实设法以某种方式对N大小的数组的N / 2个元素执行操作,那么唯一的可能性是你没有访问一半的元素。最好的情况是访问数组的每个元素不超过一次。

你可以递归地解决问题,但它不会比简单的线性方法更好。如果使用尾递归(递归调用位于函数的末尾),那么编译器可能会将其转换为循环。如果它没有把它变成一个循环,那么你必须处理推入调用堆栈的额外成本,并且你有可能为非常大的数组堆栈溢出。

很酷的现代方式是并行编程。但是,不要被所有建议图书馆的人所愚弄;即使运行时间看起来比线性方法快,每个元素仍然被访问一次。并行性(请参阅OpenMP,MPI或GPU编程)通过将工作划分为不同的执行单元(例如处理器中的不同核心或网络上的不同计算机)来作弊。但是,如果问题集不够大,那么添加并行性的开销很可能会比通过划分工作节省的时间产生更大的成本。

我建议调查OpenMP;有了它,一行代码可以自动将任务划分到不同的执行单元,而无需处理任何类型的线程间通信或任何令人讨厌的事情。

答案 4 :(得分:0)

以下程序显示了为您描述的案例实现并行化概念的简单方法 - 时序基准测试表明它没有提供任何好处(因为内部循环"没有&t; t做足够的工作"来证明并行化的开销。)

#include <stdio.h>
#include <time.h>
#include <math.h>
#include <omp.h>
#include <stdlib.h>
#define N 1000000

int main(void) {
int ii,jj, kk;
int *array;
double t1, t2;
int threads;

// create an array of random numbers:
array = malloc(N * sizeof *array);
for(ii=0; ii<N; ii++) {
  array[ii]=rand();
}

for(threads = 1; threads < 5; threads++) {
  jj=0;
  omp_set_num_threads(threads);
  t1=omp_get_wtime();

// perform loop 100 times for better timing accuracy
  for(kk=0; kk<100; kk++) {
    #pragma omp parallel for reduction(+:jj)
    for(ii=0; ii<N; ii++) {
      jj+=(array[ii]%6==0)?1:0;
    }
  }

  t2=omp_get_wtime();
  printf("jj is now %d\n", jj);
  printf("with %d threads, elapsed time = %.3f ms\n", threads, 1000*(t2-t1));
  }

return 0;

}

编译
gcc -Wall -fopenmp parallel.c -o parallel

,输出

jj is now 16613400
with 1 threads, elapsed time = 467.238 ms
jj is now 16613400
with 2 threads, elapsed time = 248.232 ms
jj is now 16613400
with 3 threads, elapsed time = 314.938 ms
jj is now 16613400
with 4 threads, elapsed time = 251.708 ms

这表明答案是相同的,无论使用的线程数是多少;但是所花费的时间确实会有所改变。因为我是在一台6岁的双核机器上做这件事的,所以你实际上并不期望有超过两个线程的加速,而且你确实没有看到一个;但是1线程和2之间存在差异。

我的观点是要表明为你设想的任务实现并行循环是多么容易 - 而且还要表明它并不值得(对我来说,在我的硬件上)。

它是否对您的案例有所帮助取决于最内层循环内的工作量以及可用的核心数量。如果您受到内存访问速度的限制,这没有任何帮助;但由于模运算相对较慢,因此可能会因此而获得较小的速度 - 更多内核和更复杂的计算将提高性能。

最后一点 - omp语法相对简单易懂。唯一奇怪的是reduction(+:jj)语句。这意味着&#34;创建jj的单独副本。完成后,将它们全部加在一起。&#34;

这就是我们如何确保可被6整除的数字的总数在不同的线程中保持跟踪。