0

目前我正在编写 Fortran 95 中的数值模拟代码。我的平台是 WIndows,我利用 MSVC 环境和 Intel Fortran 编译器。该代码与该领域中的许多代码一样,创建了一个待解决的方程组。在数值上,这发生在存储一个方阵和一个已知值的向量。现在,为了优化内存,矩阵以方便的形式存储,如压缩稀疏行格式 (CSR) 或类似格式,因此不存储零值。

鉴于这个简短的介绍,这里有我的疑问。由于在编译时我不知道数组的维度,我只是将它们声明为:

REAL, DIMENSION(:), ALLOCATABLE :: myArray

一旦我检索到这样一个向量的维度,我就会调用

ALLOCATE(myArray(N)) where N is the number of elements that I want to allocate
  • 尽管如此,内存还是空的,因为没有存储值,但是为了避免堆栈溢出,会进行内存检查。这样对吗?

现在,用值填充它,占用的空间增加了。Fortran 数组的结构,无论是一维向量还是多维数组,都是在列序中填充一个与值的个数相等的空间。也就是说,如果我们有一个维度为 1000x1000 的 2D 数组,它将存储在按列号排序的 1M 个“连续框”中(首先存储第一列,然后存储第二列,依此类推..)。

  • 如果这是真的,那么数据的结构是相同的,对特定值的访问时间是多维向量和一维向量之间的唯一区别吗?

  • 那么命令是否RESHAPE只改变程序“看到”数组的方式?

我需要的数组在每个子例程/函数共享的模块中定义。特别是,子程序分配和填充它。回到主程序,这没有问题,因为我向用户显示了一些关于它的统计数据。假设我们分配了 400M REAL*4 值,使用了大约 1.5GB 的内存。

  • 但是,一旦我进入另一个子程序,程序就会停止说forrtl: severe(170): Program Exception - Stack Overflow. 我内存不足。但是如果矩阵已经分配并且我没有分配更多的东西怎么办?注意:子程序使用相同的模块,所以变量已经声明了;我的 RAM 仍有大约 1.3GB 的可用空间;停止在子程序的第一行。

  • 子程序(以及函数)是否将数据加倍?我认为在这种情况下,Fortran 会传递我的变量地址,避免复制并直接处理这些值。

最后,和你们中的许多人一样,我喜欢 C++ 中的 STD 库函数,比如 vector::push_back 等等。在 Fortran 中,没有这么漂亮的例程,但仍然有一些非常有用的函数。屏蔽数组,使用WHEREor COUNTorMERGE可以帮助你有效地处理一些操作。

  • 但是,当我的矩阵大于 1M 条目时,它们会非常缓慢。在这种情况下,即使是顺序搜索和替换也比创建掩码或使用 where 更快。怎么可能?他们不是多线程的吗?

预先感谢您的耐心等待!!非常欢迎所有建议!

4

1 回答 1

2

评论空间有限,所以我将其发布为答案。显然,您的堆栈空间不足,而不是内存不足。Windows 上主线程的堆栈大小在链接时是固定的(默认为 1 MiB),任何更大的堆栈分配都可能导致堆栈溢出。发生这种情况的原因有很多,但主要是:

  • 您调用的子程序使用大堆栈数组(例如非ALLOCATABLE数组);
  • 您将一个不连续的数组小节传递给子例程,例如myArray(1:10:2),并且您没有该子例程的显式接口。在这种情况下,编译器将对正在传递的数据制作一个最有可能的临时堆栈副本,这可能会耗尽堆栈空间并触发异常。

我猜第一点是与您的情况相关的,因为当您进入子例程时会发生异常(可能在序言中,所有局部变量的堆栈空间都被保留)。您可以指示 Intel Fortran 在项目设置中启用堆数组,看看是否有帮助(不确定 Windows 版本是否默认启用堆数组)。

甚至没有显示一行代码,就很难猜测问题的根源并解决它。

于 2013-01-25T17:59:53.310 回答