4

我正在将执行数字模拟的程序从 FORTRAN 翻译为 C++。

我必须处理 800MB 大小的双倍大矩阵。这个

double M[100][100][100][100];

给出分段错误,因为堆栈不是那么大。使用 new,delete 很尴尬,因为我需要四个 for 循环来分配我的数组,甚至释放它。

std::array 在堆栈中,所以不好。std::vector 将是一个不错的选择,所以

第一个问题 是 std::vector 适合快速模拟还是

vector<vector<vector<vector<int,100>,100>,100>,100> 

会携带大量无用且繁重的数据吗?

第二个问题 你知道我可以使用的任何其他数据结构吗?也许有来自boost的东西。

目前我只是使用这个解决方案:

double * M = new double [100000000];

我正在手动访问我需要的条目。如果我没有找到任何其他高性能解决方案,我将编写一个自动管理最后一个方法的类。

第三个问题你认为这会显着降低性能吗?

4

5 回答 5

1

您可能需要考虑std::valarray哪个旨在与 FORTRAN 竞争。它将元素存储为平面数组并支持数学运算,以及切片和间接访问的操作。

听起来就像你计划的那样。尽管即使联机帮助页也暗示可能有更灵活的选择。

于 2016-09-17T14:53:08.053 回答
0

在某些情况下,将 1-D 指针转换为 4-D 矩形数组以启用笛卡尔索引(而不是线性索引)可能很有用:

#include <cstdio>

#define For( i, n ) for( int i = 0; i < n; i++ )

double getsum( double *A, int *n, int loop )
{
    // Cast to 4-D array.
    typedef double (* A4d_t)[ n[2] ][ n[1] ][ n[0] ];
    A4d_t A4d = (A4d_t) A;

    // Fill the array with linear indexing.
    int ntot = n[0] * n[1] * n[2] * n[3];
    For( k, ntot ) A[ k ] = 1.0 / (loop + k + 2);

    // Calc weighted sum with Cartesian indexing.
    double s = 0.0;
    For( i3, n[3] )
    For( i2, n[2] )
    For( i1, n[1] )
    For( i0, n[0] )
        s += A4d[ i3 ][ i2 ][ i1 ][ i0 ] * (i0 + i1 + i2 + i3 + 4);

    return s;
}

int main()
{
    int n[ 4 ] = { 100, 100, 100, 100 };

    double *A = new double [ n[0] * n[1] * n[2] * n[3] ];

    double ans = 0.0;
    For( loop, 10 )
    {
        printf( "loop = %d\n", loop );
        ans += getsum( A, n, loop );
    }
    printf( "ans = %30.20f\n", ans );
    return 0;
}

在 Mac OSX 10.9 上使用 g++-6.0 -O3 需要 5.7 秒。将性能与基于vector<vector<...>>或自定义数组视图类的性能进行比较可能会很有趣。(我之前尝试过后者,当时上面的数组转换比我的(天真的)数组类要快一些。)

于 2016-09-18T00:56:07.220 回答
0

在堆栈上使用某些东西肯定会更有效。进程内存受操作系统(堆栈+堆)的限制,您的问题是在大多数情况下您可能会超过分配给进程的内存。

为了解决内存限制,我建议你看看stxxl。它是一个库,它实现了大多数 STL 容器和算法,但在需要时使用外部存储器。当然这会影响性能......

于 2016-09-17T13:19:48.187 回答
0

程序员倾向于通过首先编写更多代码来解决每个问题。然后必须对其进行维护。每个问题都不是钉子...

更多代码在这里并不是最简单、最有效的解决方案。更多的代码也可能产生更慢的可执行文件。

堆栈内存就是内存——它与堆内存没有什么不同。它只是由进程以不同的方式管理,并且受到不同的资源限制。一个进程是在其堆栈上使用 1 GB 内存,还是从其堆中使用 1 GB 内存,对于操作系统来说并没有真正的区别。

在这种情况下,堆栈大小限制可能是人为的配置设置。在 Linux 系统上,可以为 shell 及其子进程重置堆栈大小限制:

bash-4.1$ ulimit -s unlimited
bash-4.1$ ulimit -s
unlimited
bash-4.1$

有关更多详细信息,请参阅此问题及其答案

所有符合 POSIX 的系统都应该具有类似的功能,因为堆栈大小限制是POSIX 标准的资源限制

此外,您可以很容易地运行具有任意大堆栈的线程:

#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <stdio.h>

void *threadFunc( void *arg )
{
    double array[1024][1024][64];
    memset( array, 0, sizeof( array ) );
    return( NULL );
}
int main( int argc, char **argv )
{
    // create and memset the stack lest the child thread start thrashing
    // the machine with "try to run/page fault/map page" cycles
    // the memset will create the physical page mappings
    size_t stackSize = strtoul( argv[ 1 ] ? argv[ 1 ] : "1073741824",
        NULL, 0 );    
    void *stack = mmap( 0, stackSize, PROT_READ | PROT_WRITE,
        MAP_PRIVATE | MAP_ANON, -1, 0 );
    memset( stack, 0, stackSize );

    // create a large stack for the child thread
    pthread_attr_t attr;    
    pthread_attr_init( &attr );
    pthread_attr_setstacksize( &attr, stackSize );
    pthread_attr_setstackaddr( &attr, stack );

    pthread_t tid;
    pthread_create( &tid, &attr, threadFunc, NULL );
    void *result;
    pthread_join( tid, &result );
    return( 0 );
}

已省略错误检查。

如果您ulimit -s unlimited在运行已编译的程序之前运行(当然,如果机器有足够的虚拟内存......),这也有效:

#include <string.h>

int main( int argc, char **argv )
{
    double array[1024][1024][64];
    memset( array, 0, sizeof( array ) );

    return( 0 );
}
于 2016-09-17T14:31:39.700 回答
-1

我认为这个问题的答案是基于意见的。“”的概念严格依赖于数据结构的使用。

无论如何,如果元素的数量在执行期间没有改变,并且您的问题实际上是内存访问,那么在我看来,最好的解决方案是一个连续的块数组。

通常在这些情况下,我的选择是简单T* data = new T[SIZE];地封装到一个可以正确处理访问的类中。

指针的使用让我对内存控制感觉更舒服一点,但实际上 astd::vector<T>几乎是一回事。


根据您在问题中提供的知识,我只能说这些。无论如何,我还可以建议您注意数据的使用。

例如:为了最大化您的应用程序的性能,请尝试利用缓存并避免错过。因此,您可以尝试了解您的数据是否存在一些“访问模式”,或者,即使您可以考虑多线程上下文来扩展您的问题。


总而言之,在我看来,通常一个连续的向量double是最好的选择。这回答了你的问题。但是如果你关心性能,你应该考虑如何尽可能地利用缓存处理器机制(如多线程)。

于 2016-09-17T12:53:52.403 回答