15

我一般是编程新手,所以当你回答我的问题时请记住这一点。

我有一个程序,它采用大型 3D 数组(10 亿个元素)并沿各个轴汇总元素以生成数据每一侧的投影的 2D 数组。这里的问题是它非常密集,因为程序不断地从 ram 中获取信息,包括读取和写入。

问题是,如果我对程序进行多线程处理,我会获得任何性能提升,还是最终会遇到 RAM 访问瓶颈?当我说多线程时,我只是指 2 或 4 个内核的多线程,仅此而已。

如果有帮助,我当前的计算机配置是 2.4ghz core2 quad、1033 fsb、4gb ram at 667mhz。

提前致谢,

- 伪造

编辑:

在我看来,这里的人们对我最初预料到的这个问题更感兴趣。我将扩展问题并为感兴趣的人发布一些代码。

首先,了解一下我的背景,以便您了解我来自哪里。我是一名机械工程研究生,他设法选择了一个与机械工程几乎无关的主题。大约 5 年前,我参加了 1 门 Java 入门课程(强制),直到大约一个月前我认真开始我的论文时才接触过编程。我还参加了(再次被迫,仍然不知道为什么)电子和计算机工程课程,我们处理了微控制器(8 位)、它们的内部工作原理以及它们的一些 ASM 编码。除此之外,我对编程几乎一无所知。

这是代码:

int dim = 1000;
int steps = 7 //ranges from 1 to  255

for (int stage = 1; stage < steps; stage++)
for (int j = 0; j < dim; j++)
    for (int i = 0; i < dim; i++)
    {
        sum = 0;
        for (int k = 0; k < dim; k++)
            if (partMap[(((i * dim) + k) * dim) + j] >= stage)
                sum++;

        projection[(j*dim) + i] = sum;
    } 

这部分代码仅在 z 轴上运行。主要数据,由于它的构造方式,有一个奇怪的寻址系统,但你不必担心。还有其他代码用于对立方体的其他面进行投影,但它们做的事情非常不同。

4

19 回答 19

32

跨多个内核的多线程可以减少跨轴求和所需的时间,但需要特别小心。实际上,您可以对单线程代码进行一些更改,从而获得更大的性能提升:

  1. 您只需要尽可能多的线程来匹配您可用的内核数量。这是一个 CPU 密集型操作,线程不太可能等待 I/O。

  2. 如果整个阵列不适合 RAM,则上述假设可能不成立。如果数组的某些部分被分页进出,一些线程将等待分页操作完成。在这种情况下,程序可能会受益于拥有比内核更多的线程。但是,太多了,性能会由于上下文切换的成本而下降。您可能需要尝试线程数。一般规则是最小化就绪线程之间的上下文切换次数。

  3. 如果整个数组不适合 RAM,您希望尽量减少分页!每个线程访问内存的顺序很重要,所有正在运行的线程的内存访问模式也很重要。在可能的范围内,您希望在移动到下一个之前完成阵列的一部分,永远不要返回到覆盖区域。

  4. 每个核心都将受益于必须访问一个完全独立的内存区域。您希望避免由锁和总线争用引起的内存访问延迟。至少对于多维数据集的一个维度,这应该很简单:为每个线程设置它自己的多维数据集部分。

  5. 与从 RAM 中获取相比,每个内核还可以从其缓存中访问更多数据中受益。这意味着对循环进行排序,以便内部循环访问附近的单词,而不是跳过行。

  6. 最后,根据阵列中的数据类型,英特尔/AMD 处理器(SSE,各代)的 SIMD 指令可以通过一次对多个单元求和来帮助加速单核性能。VC++ 有一些内置的支持

  7. 如果您必须优先考虑您的工作,您可能希望首先最小化磁盘分页,然后专注于优化内存访问以利用 CPU 缓存,然后才处理多线程。

于 2009-07-09T21:46:02.210 回答
12

只有一种方法可以优化代码:弄清楚你在做什么很慢,然后少做。“少做事”的一个特例是做其他事情,而不是更快。

所以首先,这是我根据您发布的代码所做的事情:

#include <fstream>
#include <sstream>
using std::ios_base;

template<typename Iterator, typename Value>
void iota(Iterator start, Iterator end, Value val) {
    while (start != end) {
        *(start++) = val++;
    }
}

int main() {

    const int dim = 1000;
    const int cubesize = dim*dim*dim;
    const int squaresize = dim*dim;
    const int steps = 7; //ranges from 1 to  255
    typedef unsigned char uchar;

    uchar *partMap = new uchar[cubesize];
    // dummy data. I timed this separately and it takes about
    // a second, so I won't worry about its effect on overall timings.
    iota(partMap, partMap + cubesize, uchar(7));
    uchar *projection = new uchar[squaresize];

    for (int stage = 1; stage < steps; stage++) {
        for (int j = 0; j < dim; j++) {
                for (int i = 0; i < dim; i++)
                {
                        int sum = 0;
                        for (int k = 0; k < dim; k++)
                            if (partMap[(((i * dim) + k) * dim) + j] >= stage)
                                sum++;

                        projection[(j*dim) + i] = sum;
                }
        }

        std::stringstream filename;
        filename << "results" << stage << ".bin";
        std::ofstream file(filename.str().c_str(), 
            ios_base::out | ios_base::binary | ios_base::trunc);
        file.write((char *)projection, squaresize);
    }

    delete[] projection;
    delete[] partMap;
}

(编辑:刚刚注意到“投影”应该是一个int数组,而不是uchar。我的错。这会对一些时间产生影响,但希望不会太大。)

然后我复制result*.bingold*.bin,所以我可以检查我未来的变化如下:

$ make big -B CPPFLAGS="-O3 -pedantic -Wall" && time ./big; for n in 1 2 3 4 5
6; do diff -q results$n.bin gold$n.bin; done
g++  -O3 -pedantic -Wall   big.cpp   -o big

real    1m41.978s
user    1m39.450s
sys     0m0.451s

好的,现在是 100 秒。

因此,推测它正在通过缓慢的十亿项数据数组,让我们尝试只通过一次,而不是每个阶段一次:

    uchar *projections[steps];
    for (int stage = 1; stage < steps; stage++) {
         projections[stage] = new uchar[squaresize];
    }

    for (int j = 0; j < dim; j++) {
            for (int i = 0; i < dim; i++)
            {
                    int counts[256] = {0};
                    for (int k = 0; k < dim; k++)
                            counts[partMap[(((i * dim) + k) * dim) + j]]++;

                    int sum = 0;
                    for (int idx = 255; idx >= steps; --idx) {
                        sum += counts[idx];
                    }
                    for (int stage = steps-1; stage > 0; --stage) {
                        sum += counts[stage];
                        projections[stage][(j*dim) + i] = sum;
                    }
            }
    }

    for (int stage = 1; stage < steps; stage++) {
        std::stringstream filename;
        filename << "results" << stage << ".bin";
        std::ofstream file(filename.str().c_str(),
            ios_base::out | ios_base::binary | ios_base::trunc);
        file.write((char *)projections[stage], squaresize);
    }

    for (int stage = 1; stage < steps; stage++) delete[] projections[stage];
    delete[] partMap;

它有点快:

$ make big -B CPPFLAGS="-O3 -pedantic -Wall" && time ./big; for n in 1 2 3 4 5
6; do diff -q results$n.bin gold$n.bin; done
g++  -O3 -pedantic -Wall   big.cpp   -o big

real    1m15.176s
user    1m13.772s
sys     0m0.841s

现在,steps在这个例子中非常小,所以我们对“counts”数组做了很多不必要的工作。甚至没有分析,我猜测与计数到 1000(沿着我们的列运行)相比,计数到 256 两次(一次用于清除数组,一次用于求和)非常重要。所以让我们改变一下:

    for (int j = 0; j < dim; j++) {
            for (int i = 0; i < dim; i++)
            {
                    // steps+1, not steps. I got this wrong the first time,
                    // which at least proved that my diffs work as a check
                    // of the answer...
                    int counts[steps+1] = {0};
                    for (int k = 0; k < dim; k++) {
                        uchar val = partMap[(((i * dim) + k) * dim) + j];
                        if (val >= steps) 
                            counts[steps]++;
                        else counts[val]++;
                    }

                    int sum = counts[steps];
                    for (int stage = steps-1; stage > 0; --stage) {
                        sum += counts[stage];
                        projections[stage][(j*dim) + i] = sum;
                    }
            }
    }

现在我们只使用实际需要的桶数。

$ make big -B CPPFLAGS="-O3 -pedantic -Wall" && time ./big; for n in 1 2 3 4 5
6; do diff -q results$n.bin gold$n.bin; done
g++  -O3 -pedantic -Wall   big.cpp   -o big

real    0m27.643s
user    0m26.551s
sys     0m0.483s

欢呼。该代码的速度几乎是第一个版本的 4 倍,并且产生了相同的结果。我所做的只是改变数学运算的顺序:我们甚至还没有研究多线程或预取。而且我没有尝试过任何高度技术性的循环优化,只是把它留给了编译器。所以这可以被认为是一个不错的开始。

然而,它仍然比 iota 运行的 1 长一个数量级。所以可能还有很大的收获。一个主要区别是 iota 按顺序在 1d 数组上运行,而不是到处跳跃。正如我在第一个答案中所说,您的目标应该是始终在多维数据集上使用顺序。

所以,让我们进行一行更改,切换 i 和 j 循环:

            for (int i = 0; i < dim; i++)
    for (int j = 0; j < dim; j++) {

这仍然不是顺序,但这确实意味着我们一次只关注一百万字节的多维数据集。现代 CPU 至少有 4MB 高速缓存,所以如果运气好的话,我们只会在整个程序中为多维数据集的任何给定部分访问主内存一次。有了更好的局部性,我们也可以减少进出 L1 缓存的流量,但主内存是最慢的。

它有多大的不同?

$ make big -B CPPFLAGS="-O3 -pedantic -Wall" && time ./big; for n in 1 2 3 4 5
6; do diff -q results$n.bin gold$n.bin; done
g++  -O3 -pedantic -Wall   big.cpp   -o big

real    0m8.221s
user    0m4.507s
sys     0m0.514s

不错。实际上,仅此更改就将原始代码从 100 秒变为 20 秒。所以这是 5 的原因,而我所做的其他一切都是 5 的原因(我认为上面“用户”和“实时”之间的差异主要是由于我的病毒扫描程序是运行,它不是更早。“用户”是程序占用 CPU 的时间,“真实”包括暂停所花费的时间,等待 I/O 或给另一个进程运行时间)。

当然,我的桶排序依赖于这样一个事实,即我们对每列中的值所做的任何事情都是可交换和关联的。减少存储桶的数量仅起作用,因为大值都被视为相同。这可能不适用于您的所有操作,因此您必须依次查看每个操作的内部循环以弄清楚如何处理它。

而且代码有点复杂。我们不是在每个阶段运行“废话”的数据,而是在一次对数据的运行中同时计算所有阶段。如果您按照我在第一个答案中的建议,一次性开始进行行和列计算,情况会变得更糟。您可能必须开始将代码分解为函数以保持可读性。

最后,我的很多性能提升来自对“步数”很小这一事实的优化。steps=100,我得到:

$ make big -B CPPFLAGS="-O3 -pedantic -Wall" && time ./big; for n in 1 2 3 4 5
6; do diff -q results$n.bin gold$n.bin; done
g++  -O3 -pedantic -Wall   big.cpp   -o big

real    0m22.262s
user    0m10.108s
sys     0m1.029s

这还不错。使用 steps=100 时,原始代码可能需要大约 1400 秒,尽管我不会运行它来证明这一点。但值得记住的是,我并没有完全消除对“步骤”的时间依赖性,只是让它成为亚线性的。

于 2009-07-11T12:59:43.240 回答
5

你的代码是如何工作的。会这样吗?

for each row: add up the values
for each column: add up the values
for each stack: add up the values

如果是这样,您可能需要阅读“参考位置”。根据数据的存储方式,您可能会发现在进行堆栈时,必须为每个值拉入整个缓存行,因为这些值在内存中彼此相距不远。事实上,如果有十亿个值,您可能会一直从磁盘中提取数据。大步长(值之间的距离)的顺序访问是缓存最糟糕的用途。尝试分析,如果您发现添加堆栈比添加行花费更长的时间,这几乎肯定是原因。

我认为您可能会使内存总线(*)饱和,在这种情况下,只有 core2 quad 为不同的内核使用不同的总线时,多线程才会有所帮助。但是,如果您没有使总线带宽饱和,那么即使是多线程,您也无法通过这种方式获得最佳性能。您将有 4 个内核将所有时间都花在缓存未命中而不是一个上。

如果您受内存缓存限制,那么您的目标应该是尽可能少地访问每个内存页/行。所以我会尝试运行一次数据,然后将每个值添加到三个不同的总数中。如果它在单核上运行得更快,那么我们就在做生意。下一步是使用 1000x1000x1000 的多维数据集,您将拥有 300 万个总数。这也不适合缓存,因此您必须担心写入和读取时相同的缓存未命中问题。

您要确保当您在 RAM 中沿着一行 1000 个相邻值运行时,将它们都共享的行总数添加到列和堆栈(它们不存储)的相邻总数中。因此,列总数的“平方”应该以适当的方式存储,堆栈的“平方”也应该如此。这样,您只需将大约 12k 的内存拉入缓存即可处理十亿个值中的 1000 个(1000 个值需要 4k,1000 个列总计需要 4k,1000 个堆栈总计需要 4k)。与此相反,通过一次专注于 1 个总数(因此可能在寄存器中),您所做的商店比您做的要多。

所以我不保证任何事情,但我认为值得关注内存访问的顺序,无论你是否是多线程的。如果您可以在只访问相对较少的内存的情况下完成更多的 CPU 工作,那么您将加快单线程版本的速度,但也会让自己处于更好的多线程状态,因为内核共享有限的缓存、内存总线和主 RAM。

(*) 信封后计算:在互联网上的随机随机评论中,到目前为止我发现的 Core2 处理器的最高估计 FSB 带宽是 12GB/s 的 Extreme,每个通道有 4x199MHz 的 2 个通道)。缓存行大小为 64 字节,小于您的步幅。因此,以不好的方式对列或堆栈求和,每个值抓取 64 个字节,只有在每秒处理 2 亿个值时才会使总线饱和。我猜它没有这么快(整个过程需要 10-15 秒),否则你不会问如​​何加快速度。

所以我的第一个猜测可能是遥遥无期。除非您的编译器或 CPU 插入了一些非常聪明的预取,否则单个内核不能使用 2 个通道和每个周期 4 个同时传输。就此而言,4 个核心不能使用 2 个通道和 4 个同时传输。一系列请求的有效总线带宽可能远低于物理限制,在这种情况下,您希望看到多线程带来的良好改进,因为您有 4 个内核请求 4 个不同的缓存线,所有这些都可以同时加载而不会影响 FSB 或缓存控制器。但是延迟仍然是杀手,因此如果每个值的总和加载少于一个缓存行,你会做得更好。

于 2009-07-09T21:47:33.343 回答
4

一般来说,这是不可能的,因为您没有指定 CPU 和 RAM 的速度。很有可能它会改善事情,因为我无法想象即使是 4 个并行线程求和也会使 RAM 饱和到足以成为瓶颈(而不是 CPU)。

于 2009-07-09T21:15:04.173 回答
3

我的直觉告诉你,你会看到适度的改进。然而,预测优化结果是出了名的容易出错的事情。

试一试并对结果进行基准测试。

于 2009-07-09T21:16:36.480 回答
2

如果计算可以分解成可以独立和并发处理的块,多线程只会使您的代码更快。


编辑

我之所以这么说(这几乎是一种自动响应),是因为我看到许多开发人员在多线程代码上花费了大量时间,而根本没有提高性能。当然,它们最终会得到相同的(甚至更慢的性能)以及管理多个线程的额外复杂性。

是的,在再次阅读您的问题并考虑到您将从多线程中受益的具体情况后,它确实会出现。

RAM 非常快,所以我认为除非你有很多很多线程,否则很难使内存带宽饱和。

于 2009-07-09T21:17:11.143 回答
2

如果,这是一个大的 IF,它被适当地编码,你肯定会看到一个加速。现在,正如我的一位教授一直指出的那样,人们经常尝试采用一种算法,将其线程化,最终它会变慢。这通常是因为同步效率低下。因此,基本上,如果您想深入研究线程(如果您是编程新手,老实说我不会建议您这样做),那就去吧。

在您的特定情况下,同步可能非常简单。也就是说,您可以将每个线程分配给大型 3-d 矩阵的一个象限,其中每个线程都保证可以单独访问输入和输出矩阵的特定区域,因此没有真正需要“保护' 来自多次访问/写入的数据。

总而言之,在这种特定的简单情况下,线程可能很容易,但一般来说,如果同步做得不好,可能会导致程序花费更长的时间。这真的取决于。

于 2009-07-09T21:19:03.387 回答
2

我认为即使多线程可以提高性能,它也是进行优化的错误方法。多核之所以风靡一时,是因为它们是 CPU 制造商以适销对路的速度提供更快 CPU 速度的唯一途径——不一定是因为它们是一种了不起的编程工具(还有很多成熟的工作需要发生)。

始终关注您正在使用的算法。你说你的程序非常占用内存——你能做些什么来提高缓存命中率?有没有办法对数组进行排序,以便可以线性应用计算?您正在使用哪种编程语言,使用较低级别的语言进行优化是否对您有益?有没有一种方法可以使用动态编程来存储结果?

通常,将所有资源用于在数学上和编译器优化方面更有效的算法,然后再担心多核。当然,您可能已经处于那个阶段,在这种情况下,此评论不是很有用;p

于 2009-07-09T21:49:28.207 回答
2

您需要为特定应用回答的问题是众所周知的。

首先,工作是否可并行化? 阿姆达尔定律会给你一个上限,你可以用多线程加速多少。

其次,多线程解决方案会引入大量开销吗?您说该程序是“RAM 密集型程序,因为程序不断从 RAM 中获取信息,包括读取和写入。” 所以你需要确定读/写是否会导致显着的协调开销. 这并不容易。虽然每个 CPU 都可以随时访问计算机的整个 RAM(读取和写入),但这样做会减慢内存访问速度——即使没有锁——因为各种 CPU 保留自己的缓存并且需要协调缓存中的内容彼此(CPU 1 在缓存中有一个值,CPU 2 更新 RAM 中的值,CPU 2 必须告诉 CPU 1 使其缓存无效)。而且,如果您确实需要锁(这几乎是一种保证,因为您既要“读又要写”内存),那么您需要尽可能避免争用。

第三,你有记忆力吗?“内存密集型。” 与“内存绑定”不同。如果您当前受 CPU 限制,那么多线程将加快速度。如果您当前受内存限制,那么多线程甚至可能会减慢速度(如果一个线程对内存来说太快,那么多个线程会发生什么?)。

第四,你是否因为其他原因变慢了?如果您在算法中使用newmalloc使用大量内存,您可能会看到仅此一项的开销。 而且在许多平台上都new不能malloc很好地处理多线程,所以如果你现在因为malloc不好而变慢,那么多线程程序会更慢,因为malloc会更糟。

但是,总的来说,如果没有看到您的代码,我希望它受 CPU 限制,并且我希望多线程能够加快速度——实际上几乎与阿姆达尔定律所暗示的一样多。不过,您可能想查看 OpenMP 或英特尔的线程构建块库,或某种线程队列来执行此操作。

于 2009-07-09T21:50:39.610 回答
2

在你使用多线程之前,你应该对你的代码运行一个分析器。关于在哪里可以找到一个好的(可能)免费的 C++ 分析器,这可能是一个不同的问题。

这将帮助您识别占用大量计算时间的代码中的任何位。在一些分析之后在这里和那里进行调整有时会对性能产生巨大影响。

于 2009-07-09T21:53:43.677 回答
2

尽管如果您是编程新手,这对您来说可能会非常具有挑战性,但加速事情的一种非常有效的方法是使用 GPU 的强大功能。VRAM 不仅比普通 RAM 快得多,GPU 还可以在大约 128 个或更多内核上并行运行您的代码。当然,对于这么大的数据量,您将需要一个相当大的 VRAM。

如果您决定检查这种可能性,您应该查找 nVidia CUDA。我自己没有检查过,但它适用于这样的问题。

于 2009-07-09T21:58:21.280 回答
1

如果您正确地对数据进行分区,那么是的,您将获得性能提升。如果你现在检查你的 cpu 使用率,一个核心将处于 100%,其他 3 个应该接近 0%

这完全取决于您构建线程和内存使用情况的程度。

此外,不要期望 x4 改进。x4 是可达到的最大值,取决于很多因素,它总是低于这个值。

于 2009-07-09T21:20:24.537 回答
1

您的计算机系统通常有一些限制粗糙性能的元素。哪一部分是你的限制因素,取决于具体情况。通常,以下因素之一可能是您的性能问题的原因。

  • 磁盘 I/O 带宽:在大多数企业应用程序中,处理数据的庞大规模要求将其存储在某个数据库中。访问这些数据可能会因以下两个因素而减慢:最大传输速度,但通常最大的影响是由大量小磁盘访问造成的,这些访问在这里和那里读取一些块。您将看到磁盘磁头四处移动的延迟时间,甚至磁盘完全旋转所需的时间可能会限制您的应用程序。很久以前,我在使用一些扩展的 SUN E430 安装时遇到了一个真正的问题,它的性能比我的小型 NeXTstation 更胜一筹……这是我的数据库的持续 fsync()ing 因磁盘不缓存写访问而减慢了速度(有充分的理由) . 通常,您可以通过添加额外的磁盘来提高系统速度,从而获得更多的每秒 I/O。

  • 网络延迟:几乎所有影响磁盘应用程序速度的因素都等同于网络 I/O。

  • RAM:如果您的 RAM 不足以存储完整的应用程序映像,则需要将其存储在外部磁盘上。因此,磁盘 I/O 减速再次困扰您。

  • CPU 处理速度(整数或浮点):CPU 处理能力是限制 CPU 密集型任务的下一个因素。CPU 具有无法超出的物理速度限制。加快速度的唯一方法是添加更多 CPU。

这些限制可以帮助您找到特定问题的答案。

您是否只需要更多的处理能力并且您的系统有多个 CPU 或内核?在这种情况下,多线程将提高您的性能。

您是否观察到明显的网络或磁盘延迟?如果您看到这一点,您宝贵的 CPU 可能会丢弃等待一些缓慢 I/O 的 CPU 周期。如果有多个线程处于活动状态,则该线程可能会在内存中找到处理所需的所有数据,并且可以拾取这些否则会浪费的 CPU 周期。

因此,您需要观察您现有的应用程序。尽量减少数据的内存带宽。如果应用程序在一个低于 100% 的 CPU 上处于活动状态,则您可能已达到内存带宽限制。在这种情况下,额外的线程对您没有好处,因为这不会为您提供内存带宽。

如果 CPU 为 100%,请尝试一下,但请查看算法。多线程会增加额外的同步开销(以及复杂性,大量的复杂性),这可能会略微降低内存带宽。首选可以避免细粒度同步的算法。

如果您看到 I/O 等待时间,请考虑巧妙的分区或缓存,然后再考虑线程。GNU-make 在 90 年代支持并行构建是有原因的 :-)

您描述的问题域使我首先了解聪明的算法。尽量在主存上使用顺序读/写操作,以尽可能多地支持 CPU 和内存子系统。保持操作“本地”和数据结构尽可能小和优化,以减少在切换到第二个核心之前需要重新洗牌的内存量。

于 2009-07-09T21:42:16.757 回答
1

消除虚假分享

这是多个内核相互阻塞的地方,试图读取或更新共享相同块缓存的不同内存地址。处理器缓存锁定是每个块的,并且一次只有一个线程可以写入该块。

Herb Sutter 有一篇关于虚假共享的非常好的文章,介绍了如何在并行算法中发现它以及如何避免它。

显然,他还有很多其他关于并发编程的优秀文章,请参阅他的博客

于 2009-07-10T12:52:19.147 回答
1

是矩阵问题?

英特尔和 AMD 都有针对各种繁重数学问题的超级优化库。这些库使用线程、安排数据以实现最佳缓存使用、缓存预取、SSE 矢量指令。一切。

我相信您必须为图书馆付费,但它们物有所值。

于 2009-07-11T20:43:57.883 回答
0

如果您可以以线程不向/从数组中的相同位置写入/读取的方式划分数组,它应该会提高您的速度。

于 2009-07-09T21:18:33.217 回答
0

我想如果您只是处理位,您可能不必分页或使用交换文件,在这种情况下,YES 多线程会有所帮助。

如果您不能一次将所有内容加载到内存中,那么您需要更具体地了解您的解决方案——它需要针对线程进行定制。

例如:假设您以较小的块加载数组(大小可能无关紧要)。如果你要加载一个 1000x1000x1000 的立方体,你可以总结一下。结果可以暂时存储在它们自己的三个平原中,然后添加到您的 3 个“最终结果”平面中,然后可以丢弃 1000^3 块,不再读取。

如果你做这样的事情,你不会耗尽内存,你不会强调交换文件,你不必担心任何线程同步,除了一些非常小的特定区域(如果有的话)。

那么唯一的问题是确保您的数据采用这样一种格式,即您可以直接访问单个 1000^3 多维数据集,而无需到处寻找硬盘磁头。

编辑:评论是正确的,我错了——他完全有道理。

从昨天开始,我意识到整个问题可以通过读入来解决——读入的每一条数据都可以立即汇总到结果中并丢弃。当我这样想时,你是对的,除非线程可以同时读取两个流而不会发生冲突,否则不会有太大帮助。

于 2009-07-09T22:19:27.413 回答
0

试试这个代码:

int dim = 1000;
int steps = 7 //ranges from 1 to  255

for (int stage = 1; stage < steps; stage++)
for (int k = 0; k < dim; k++)
    for (int i = 0; i < dim; i++)
    {
            sum = 0;
            for (int j = 0; j < dim; j++)
                    if (partMap[(((i * dim) + k) * dim) + j] >= stage)
                            projection[i*dim + j] ++ ;
                            // changed order of i and j
    }


transponse(projection)

我更改了循环的顺序以使代码缓存友好...您将获得一个巨大的性能提升顺序...请放心。

这是您在尝试使用多线程之前应该执行的步骤

于 2009-07-11T13:12:44.710 回答
-1

绝对地。至少让线程上的每个核心同时处理您的问题会有所帮助。目前尚不清楚更多线程是否会有所帮助,但这是可能的。

于 2009-07-09T21:20:38.563 回答