Fortran:普通数组与存储相同数据量的对象所需的 RAM
Posted
技术标签:
【中文标题】Fortran:普通数组与存储相同数据量的对象所需的 RAM【英文标题】:Fortran: RAM needed for plain arrays vs objects storing the same amount of data 【发布时间】:2013-12-11 19:56:49 【问题描述】:尝试以两种不同的方式使用动态内存分配存储一些数据,我注意到 RAM 需求的巨大差异,我无法解释。一些见解将不胜感激。
在以下示例中,目标是创建一个数据库,该数据库在多边形网格中存储连接到节点的边的 ID。但是,问题的性质无关紧要。
案例 1,使用“普通”数组:
program memorytest
implicit none
integer, dimension(:, :), allocatable :: node_edges
integer :: i
allocate(node_edges(10, 10000000)) ! 10000000 nodes with 10 edges each
node_edges(:, :) = 0
read *, i ! pause
deallocate(node_edges)
end program memorytest
需要的内存:~395,500 K
案例2,使用节点类型:
program memorytest
implicit none
type node
integer, dimension(:), allocatable :: edges
end type
type(node), dimension(:), allocatable :: nodes
integer :: i
allocate(nodes(10000000)) ! 10000000 nodes
do i = 1, 10000000
allocate(nodes(i)%edges(10)) ! with 10 edges each
end do
do i = 1, 10000000
nodes(i)%edges(:) = 0
end do
read *, i ! pause
do i = 1, 10000000
deallocate(nodes(i)%edges)
end do
deallocate(nodes)
end program memorytest
所需内存:~1,060,500 K
为了比较,我尝试了 C++ 中的等效方法。
案例 1,使用“普通”数组:
#include "stdafx.h"
#include <iostream>
int main()
int** node_edges;
int i, j;
node_edges = new int*[10000000]; // 10000000 nodes
for(i = 0; i < 10000000; i++) node_edges[i] = new int[10]; // with 10 edges each
for(i = 0; i < 10000000; i++)
for(j = 0; j < 10; j++) node_edges[i][j] = 0;
std::cin >> i; // pause
for(i = 0; i < 10000000; i++) delete [] node_edges[i];
delete [] node_edges;
return 0;
需要的内存:~510,000 K
案例2,使用节点类:
#include "stdafx.h"
#include <iostream>
class node
public:
int* edges;
;
int main()
node* nodes;
int i, j;
nodes = new node[10000000]; // 10000000 nodes
for(i = 0; i < 10000000; i++) nodes[i].edges = new int[10]; // with 10 edges each
for(i = 0; i < 10000000; i++)
for(j = 0; j < 10; j++) nodes[i].edges[j] = 0;
std::cin >> i; // pause
for(i = 0; i < 10000000; i++) delete [] nodes[i].edges;
delete [] nodes;
return 0;
需要的内存:~510,000 K
使用的开发环境:分别为 Intel Visual Fortran Studio XE 2013 和 MS Visual C++ 2010,均在默认“发布”模式下生成 32 位可执行文件。
如前所述,C++ 对这两种方法使用完全相同数量的 RAM。在 Fortran 中,我会证明一些微小的差异是合理的,但我无法解释。对我来说,这看起来要么与 Fortran 本身有关,要么与我不知道的某些 Intel fortran 编译器标志有关。
有什么想法为什么会发生这种情况和/或有什么建议可以避免在 Fortran 的面向对象方法中出现这种过多的 RAM 需求?
提前谢谢你。
【问题讨论】:
如果您使用的是真正现代的 C++ 而不仅仅是带有类似结构的类的 C,您会看到与 std::vectors 或其他野兽相同的区别。 是的,你是对的,但这也会考虑 std::vector::capacity 或其他一些与现代语言相关的开销。我试图在两种语言之间创建一个公平比较的情况,并将 C++ 方法调整为 Fortran 风格,看看我是否得到相同的行为。顺便说一句,在上面的例子中,C++ 比 Fortran 的“普通数组”版本需要更多的 RAM,但这不是我没有解释的原因。 :) 重点是 Fortran 数组更接近 C++ 向量,而不是 C 数组,后者只是地址。它们的功能要强大一个数量级。 【参考方案1】:要记住的一点是分配二维数组和一维数组是不同的。例如:
allocate(node_edges(10, 100))
分配一个可以包含 1000 个项目的内存块。
allocate(nodes(100)) ! 10000000 nodes
do i = 1, 100
allocate(nodes(i)%edges(10)) ! with 10 edges each
end do
分配一个可以包含 100 个项目并且每个项目有 10 个子项目的块。相同数量的项目那么相同的内存使用?
没有。在第二种情况下,您分配了 100 个新数组。每个都有开销。在 Fortran 中,这可能非常高,因为它必须跟踪数组维度 - 您可能希望稍后再获取一个数组部分。当分配大小较小时,这一点尤其明显。在这种情况下,它是 10,加上额外的数组信息加上填充,它可能会增加一倍分配的大小——在你的情况下就是这样。
【讨论】:
英特尔 Fortran 文档显示,在 64 位机器上,数组描述符是 48 字节加上每个维度 8 字节。 software.intel.com/sites/products/documentation/hpc/composerxe/… 我找到了一个旧的discussion。使用gfortran test.f90 -fdump-tree-original
,您可以查看到底做了什么,包括所有开销(type
和offset
加上lbound
、ubound
和stride
对于每个维度)。
评论 1:对于情况 1 和 2,使用 gfortran(在 linux 中)分别需要 381.6 MiB 和 918.7 MiB。所以这不是编译器特定的问题。注释 2:反转案例 2 中的大小,即 10 个节点,每个节点有 10000000 个边(10 个大数组与我们之前的 10000000 个小数组),也需要 381.6 MiB。这表明正是这些额外信息导致了开销。现在,我不知道是否有解决方法。谢谢你们。 (抱歉,声望还不够投票)
Gfortan 和 Intel 不同,但将来不会。 ISO/IEC TS 29113:2012 定义了数组描述符的通用格式(英特尔已经使用),Gfortran 也将采用该格式。
@georg.balafas 你不必为你的问题的答案投票。接受他们是首要的事情。以上是关于Fortran:普通数组与存储相同数据量的对象所需的 RAM的主要内容,如果未能解决你的问题,请参考以下文章