用于高性能计算的c++ std::vector
C++ std::vector for HPC?
我正在把一个执行数值模拟的程序从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>
将携带大量无用和沉重的数据?
第二个问题你知道我可以使用的其他数据结构吗?
目前我只是使用这个解决方案:
double * M = new double [100000000];
,我手动访问我需要的条目。如果我找不到任何其他高性能解决方案,我将编写一个类来自动管理最后一个方法。
第三个问题你认为这会显著降低性能吗?
您可能想要考虑设计为与FORTRAN竞争的std::valarray
。它将元素存储为平面数组,并支持数学操作,以及切片和间接访问操作。
听起来像是你的计划。尽管手册也暗示可能有更灵活的替代方案。
在堆栈上使用某些东西肯定更高效。进程内存受到操作系统的限制(堆栈+堆),您的问题是,在大多数情况下,您可能会超过分配给进程的内存。
要解决内存限制,我建议您看一下stxxl。它是一个实现大多数STL容器和算法的库,但在需要时使用外部内存。当然这会影响性能…
程序员倾向于先编写更多的代码来解决每个问题。然后需要维护。每个问题都是不是一个钉子…
在这里,更多的代码并不是最简单、最有效的解决方案。更多的代码也可能产生更慢的可执行文件。堆栈内存就是内存——它和堆内存没有什么不同。它只是由不同的流程管理,并且受到不同的资源限制。对于操作系统来说,进程是在栈上使用1gb内存还是从堆中使用1gb内存并没有真正的区别。
在这种情况下,堆栈大小限制可能是人为的配置设置。在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 );
}
在某些情况下,将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 = %dn", loop );
ans += getsum( A, n, loop );
}
printf( "answers = %30.20fn", ans );
return 0;
}
在Mac OSX 10.9上使用g++-6.0 -O3需要5.7秒。将性能与基于vector<vector<...>>
或自定义数组视图类的性能进行比较可能会很有趣。(我之前尝试过后者,当时上面的数组强制转换比我的(幼稚的)数组类要快一些。)
我认为这个问题的答案是基于个人观点的。"好"的概念严格依赖于数据结构的使用。
无论如何,如果元素的数量在执行期间没有改变,并且您的问题实际上是内存访问,那么在我看来,最好的解决方案是一个连续的块数组。
通常在这些情况下,我的选择是将一个简单的T* data = new T[SIZE];
封装到一个正确处理访问的类中。
指针的使用让我对内存控制感觉更舒服,但实际上std::vector<T>
实际上是同样的事情。
根据你在问题中提供的知识,我只能说这些。无论如何,我可以额外建议你也要注意数据的使用。
例如:为了最大化您的性能应用程序,尝试利用缓存并避免错过,因此您可以尝试了解是否有一些"访问模式"到您的数据,或者,甚至,如果您可以将问题扩展到多线程上下文中。
综上所述,在我看来,通常double
的连续向量是最佳选择。这就回答了你的问题。但是如果您关心性能,您应该考虑如何尽可能地利用缓存和处理器机制(如多线程)。
- 使用std::vector的OpenCL矩阵乘法
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- std::vector的包装器,使数组的结构看起来像结构的数组
- 编译器如何区分std::vector的构造函数
- 使用 pqxx 将 std::vector 存储在 postgresql 中,并从数据库中检索它
- 在std::vector上存储带有模板的类实例
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 为什么std::vector比数组慢
- std::vector::迭代器是否可以合法地作为指针
- 如何将二进制格式的 C++ 对象的 std::vector 保存到磁盘?
- 为什么std::vector和std::valarray初始化构造函数不同
- ";结果类型必须是可从输入范围的值类型""构造的;创建std::vector时
- 在没有未定义行为的情况下实现类似std::vector的容器
- 如何调整 std::vector of Eigen::MatrixXd 的大小
- 使用 std::vector::reverse_iterator 将 int 序列化为字节向量?
- 如何将AERT_Allocate与 std:vector 一起使用
- 推导 std::vector::back() 的返回类型
- 如何将原始字节附加到 std::vector?
- std::vector 没有重载函数的实例与参数列表匹配
- 如果 KEY 是 std::list 或 std::vector 而不是值,那么 std::map 的默认行为是什么?