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,您可以查看到底做了什么,包括所有开销(typeoffset 加上lbounduboundstride 对于每个维度)。 评论 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的主要内容,如果未能解决你的问题,请参考以下文章

如何判断一个 fortran 数组指针是直接分配的,还是与另一个对象相关联?

在 Fortran 90 中使用二维数组与派生类型数组

在 Fortran 中创建具有不同类型元素的数组

数据类与生成数据库表所需组成

Fortran函数,根据输入返回标量OR数组

对象数组如何存储在内存中?