对称矩阵的快速索引

时间:2019-04-02 08:57:10

标签: fortran

在用Fortran08编写的用于计算热力学平衡和相图的大型代码中,我使用了许多对称矩阵,这些矩阵存储为一维数组,并使用一个小函数进行索引

.blog-entry{display: in-line-block}

这很好用,但是在提高了代码其他各个部分的速度之后,此例程现在占用了15-20%的计算时间(它经常使用)。我认为有多种方法可以加快此速度,但是我不知道用C或其他方式可以替代此功能,因此我正在寻求帮助。我使用gfortran,但替换必须是可移植的。

Bo Sundman

2 个答案:

答案 0 :(得分:1)

过去,Fortran编译器的性能达到了同等水平或优于C编译器。 因此,我不希望仅通过切换语言而将注意力集中在算法改进上。

  • 如何用查找表替换索引转换的计算? 您是否有存储能力来存储给定的ixsymi索引的j值?
    是的,它可以抵消您对cpu权衡的记忆,但是,如果您有很多矩阵,这可能会有所帮助。

  • 是否真的需要始终计算转换?例如。如果您遍历元素:ixsym(i, j+1) = ixsym(i, j) + 1,如果i < j

  • 尽管特定于硬件,但另一个想法可能是对数据进行不同的排序,以使其保留在CPU的缓存区域内。 (Link

关于索引转换:

我最初以为您使用了Cantor pairing function的一些变体来枚举对称的2D数组。我问我的朋友露比(Ruby)画几对,她告诉我:

(0, 0) ->  0  (0, 1) ->  0  (0, 2) ->  1  (0, 3) ->  3  (0, 4) ->  6
(1, 0) ->  0  (1, 1) ->  1  (1, 2) ->  2  (1, 3) ->  4  (1, 4) ->  7
(2, 0) ->  1  (2, 1) ->  2  (2, 2) ->  3  (2, 3) ->  5  (2, 4) ->  8
(3, 0) ->  3  (3, 1) ->  4  (3, 2) ->  5  (3, 3) ->  6  (3, 4) ->  9
(4, 0) ->  6  (4, 1) ->  7  (4, 2) ->  8  (4, 3) ->  9  (4, 4) -> 10

我本来应该只计算索引的两次出现,但是我发现某些对出现了三个。这是故意的吗?

更新

正如其他用户Jean-Claude Arbaut在评论中指出的那样,这是索引的开始。 这是Ruby从1开始的答案:

(1, 1) ->  1  (1, 2) ->  2  (1, 3) ->  4  (1, 4) ->  7  (1, 5) -> 11
(2, 1) ->  2  (2, 2) ->  3  (2, 3) ->  5  (2, 4) ->  8  (2, 5) -> 12
(3, 1) ->  4  (3, 2) ->  5  (3, 3) ->  6  (3, 4) ->  9  (3, 5) -> 13
(4, 1) ->  7  (4, 2) ->  8  (4, 3) ->  9  (4, 4) -> 10  (4, 5) -> 14
(5, 1) -> 11  (5, 2) -> 12  (5, 3) -> 13  (5, 4) -> 14  (5, 5) -> 15

答案 1 :(得分:1)

您可能要考虑的唯一事情就是摆脱该函数中的分支:

两个数字的最小值和最大值可计算为:

max = (a+b + abs(a-b))/2
min = (a+b - abs(a-b))/2 = a+b - max

因此您可以将其用作

integer function ixsym(i,j)
   integer :: p, q
   q = i+j; p = (q + abs(i-j))/2; q = q - p
   ixsym = q + (p*(p-1))/2        
   return
end

您可以将其进一步简化为

integer function ixsym(i,j)
   integer :: p
   ixsym = i+j; p = (ixsym + abs(i-j))/2;
   ixsym = ixsym + (p*(p-3))/2        
   return
end