求教各位大神一个程序结构问题,内容更新,多谢!

f
ftmit
楼主 (未名空间)

以前的帖子我没有表达清楚,把问题太过于简化了,更新一下。恳请各路大神出手指点迷津了。没法子,灌水paper用。先谢谢了!

就是有两个二维数组,一个是tt,另一个是uu。然后根据tt中的每一列的最大值和最小值,找出uu中相对应列的在该最大值和最小值之间的数值.

使用fortran,这是一个两层循环。具体结构,以及数据之间的关联,如下:

do i = 1, N1 !N1大小在10^9数量级

b = maxval(tt(:,i)) !tt为二维数组,列的长度为其他数值,且与i无关
c = minval(tt(:,i))
A(:) = uu(:,i) !uu为二维数组,列的长度为N2,且与i无关

do j = 1, N2 !N2大小在10^7数量级

if ((A(j).lt.b).and.(A(j).gt.c) then
.......
....... !then之后的事,与tt,uu,A,c,b都没有关系,但与j有关

else
endif

enddo
enddo

这种循环结构太浪费时间了,一个月能算出一个结果就不错了,太慢了。有啥好办法能够节约计算时间?

先多谢各路大神指点迷津!

d
dakedo

A(j)还是A(i,j)?

【 在 ftmit (八戒) 的大作中提到: 】
: 以前的帖子我没有表达清楚,把问题太过于简化了,更新一下。恳请各路大神出手指点
: 迷津了。没法子,灌水paper用。先谢谢了!
: 使用fortran,这是一个两层循环。具体结构和大小如下:
: do i = 1, N1 !N1大小在10^9数量级
: b(i) = ...... !这里也可以不使用数组b(i),直接用数值b,b值与i有关
: c(i) = ...... !这里也可以不使用数组c(i),直接用数值c,c值与i有关
: do j = 1, N2 !N2大小在10^7数量级
: if ((A(j).gt.b(i)).and.(A(j).lt.c(i))) then
: .......
: .......
: ...................

f
ftmit

多谢大蝌蚪,我犯了个错误,马上更新一下,a,b,c的数值关系。

【 在 dakedo (大蝌蚪) 的大作中提到: 】
: A(j)还是A(i,j)?

B
BroPingtou

这数据是不是应该在生成的时候处理好啊?

现在你要处理就按照黄总说的,切片放到不同机器上跑好了
f
ftmit

大蝌蚪,我把数据之间的关系更新了一下,不知道表达清楚了没有?

其实,就是有两个二维数组,一个是qq,另一个是pp。然后根据pp中的每一列的最大值和最小值,找出qq中相应列的数值.

那您有什么更好的办法吗?先谢谢了!

【 在 dakedo (大蝌蚪) 的大作中提到: 】
: A(j)还是A(i,j)?

d
dakedo

then之后干的事情河pp qq的值有关系吗

【 在 ftmit (八戒) 的大作中提到: 】
: 大蝌蚪,我把数据之间的关系更新了一下,不知道表达清楚了没有?
: 其实,就是有两个二维数组,一个是qq,另一个是pp。然后根据pp中的每一列的最大值
: 和最小值,找出qq中相应列的数值.
: 那您有什么更好的办法吗?先谢谢了!

f
ftmit

then之后的事,与那两个数组无关,与A,c,b也都没有关系,但与j有关

【 在 dakedo (大蝌蚪) 的大作中提到: 】
: then之后干的事情河pp qq的值有关系吗

d
dakedo

那应该没啥好办法
FORTRAN 不懂
但是如果可以vectorize的话应该可以比for loop快些吧

【 在 ftmit (八戒) 的大作中提到: 】
: then之后的事,与那两个数组无关,与A(j),a,b也都没有关系,但与j有关

H
Huangchong


可能的原因是你企图把10^9 个数字一下存在内存里 (导致内存不够 开始用硬盘做扩展) 还不断从硬盘读新的数据 这样导致大量的硬盘io 本来可以很快的操作变成
龟速

军版的一个回帖说得有道理 你可以考虑把两个循环分开 第一步只循环tt 并把每列的极值存在文件里 第二步再循环uu 并且把uu合理分组 每次从硬盘上读10k左右的
极值

【 在 ftmit (八戒) 的大作中提到: 】
: 以前的帖子我没有表达清楚,把问题太过于简化了,更新一下。恳请各路大神出手指点
: 迷津了。没法子,灌水paper用。先谢谢了!
: 就是有两个二维数组,一个是tt,另一个是uu。然后根据tt中的每一列的最大值和最小
: 值,找出uu中相对应列的在该最大值和最小值之间的数值.
: 使用fortran,这是一个两层循环。具体结构,以及数据之间的关联,如下:
: do i = 1, N1 !N1大小在10^9数量级
: b = maxval(tt(:,i)) !tt为二维数组,列的长度为其他数值,且与i无关
: c = minval(tt(:,i))
: A(:) = uu(:,i) !uu为二维数组,列的长度为N2,且与i无关
: do j = 1, N2 !N2大小在10^7数量级
: ...................

d
dakedo

他这个太大了
10^16如果8byte一个得1000T内存
肯定得分开弄

另外tt 不知每列多长
也得分

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 可能的原因是你企图把10^9 个数字一下存在内存里 还不断从硬盘读信的数据 导致
: 大量的硬盘io 本来可以很快的操作变成龟速
: 军版的一个回帖说得有道理 你可以考虑把两个循环分开 第一步只循环tt 并把每列
: 的极值存在文件里 第二步再循环uu 并且把uu合理分组 每次从硬盘上读10k左右的
: 极值

H
Huangchong

想起一件事 Fortran的二维数组 在内存的存储方向和c是不同的 如果某个维度特别长 需要考虑把数组转置 另外不要一开始就把tt全读进内存 一次读100M的数据点 而且保证所有操作在这100M里可以完成 可能问题就解决了

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 可能的原因是你企图把10^9 个数字一下存在内存里 (导致内存不够 开始用硬盘做扩
: 展) 还不断从硬盘读新的数据 这样导致大量的硬盘io 本来可以很快的操作变成
: 龟速
: 军版的一个回帖说得有道理 你可以考虑把两个循环分开 第一步只循环tt 并把每列
: 的极值存在文件里 第二步再循环uu 并且把uu合理分组 每次从硬盘上读10k左右的
: 极值

H
Huangchong

https://web.stanford.edu/class/me200c/tutorial_77/10_arrays.html

(1,1) (1,2) (1,3) (1,4) (1,5)
(2,1) (2,2) (2,3) (2,4) (2,5)
(3,1) (3,2) (3,3) (3,4) (3,5)

Fortran stores higher dimensional arrays as a contiguous sequence of
elements. It is important to know that 2-dimensional arrays are stored by
column. So in the above example, array element (1,2) will follow element (3,1). Then follows the rest of the second column, thereafter the third column, and so on.

八戒如果只需要每次处理tt一列的话 似乎刚好是在处理连续的内存 不过还是要仔
细看看程序 是不是真的这样 不是的话 光读1列就够内存和硬盘折腾半天了

还要看一下 原始数据在硬盘上是不是也是以列为单位存储的 不是的话,值得先写个小程序把数据转置一下

此外 把tt先处理完 得到极大极小值 写进文件 再处理uu这也会大大改善io操作速
度 不然的话 系统可能需要在tt和uu源文件里跳来跳去 而两步分开的话 就只需
要分别顺序读取两个文件了

分段读数据 (最好在硬盘上也是连续的) 每次处理一块 这个总体方法 是没错的
我猜慢的主要原因就是低效io操作太多

此外取极大和极小值可以自己写个函数一步完成 或者查查有没有现成的函数 如果数组够大 把可以一步完成的事做两次也是相当可观的

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 想起一件事 Fortran的二维数组 在内存的存储方向和c是不同的 如果某个维度特别
: 长 需要考虑把数组转置 另外不要一开始就把tt全读进内存 一次读100M的数据

: 而且保证所有操作在这100M里可以完成 可能问题就解决了

B
BroPingtou

不是吧。

10^9 space而已

【 在 dakedo(大蝌蚪) 的大作中提到: 】

: 他这个太大了

: 10^16如果8byte一个得1000T内存

: 肯定得分开弄

: 另外tt 不知每列多长

: 也得分

H
Huangchong

简单说 当二维数组的一个维度特别长 而且不需要同时分析这个维度的话 程序要注
意在这个维度上切割数据 减少io操作

如果数据要经过几道计算的话 把一起处理的数据块限制到几兆大(cpu缓存大小以内
)还可以减少内存到cpu的io运动 可以明显提速计算

【 在 Huangchong (净坛使者) 的大作中提到: 】
: https://web.stanford.edu/class/me200c/tutorial_77/10_arrays.html
: (1,1) (1,2) (1,3) (1,4) (1,5)
: (2,1) (2,2) (2,3) (2,4) (2,5)
: (3,1) (3,2) (3,3) (3,4) (3,5)
: Fortran stores higher dimensional arrays as a contiguous sequence of
: elements. It is important to know that 2-dimensional arrays are stored by : column. So in the above example, array element (1,2) will follow element (3,
: 1). Then follows the rest of the second column, thereafter the third
column,
: and so on.
: 八戒如果只需要每次处理tt一列的话 似乎刚好是在处理连续的内存 不过还是要仔
: ...................

f
ftmit

好的,多谢蝗虫。

另外,军版还有建议使用GPU,我没有折腾过GPU,您搞过没?如果有例子,麻烦给我一
个。再次感谢出手相助。

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 可能的原因是你企图把10^9 个数字一下存在内存里 (导致内存不够 开始用硬盘做扩
: 展) 还不断从硬盘读新的数据 这样导致大量的硬盘io 本来可以很快的操作变成
: 龟速
: 军版的一个回帖说得有道理 你可以考虑把两个循环分开 第一步只循环tt 并把每列
: 的极值存在文件里 第二步再循环uu 并且把uu合理分组 每次从硬盘上读10k左右的
: 极值

H
Huangchong

根本不必要 再说你也不是做矩阵乘法 GPU似乎不会帮你什么 我觉得你的程序只要
优化对数据的管理就能提速了

【 在 ftmit (八戒) 的大作中提到: 】
: 好的,多谢蝗虫。
: 另外,军版还有建议使用GPU,我没有折腾过GPU,您搞过没?如果有例子,麻烦给我一
: 个。再次感谢出手相助。

f
ftmit

好的,明白了。多谢蝗虫!
【 在 Huangchong (净坛使者) 的大作中提到: 】
: 根本不必要 再说你也不是做矩阵乘法 GPU似乎不会帮你什么 我觉得你的程序只要
: 优化对数据的管理就能提速了

H
Huangchong

你的tt一列多长? tt uu各是多大的文件? 如果数据本身就是非常大 那还是只有
手动分块 在几个机器上分别跑 数据量特别大的话你得考虑限速的不只是内存和
CPU 硬盘IO可能是大头了

把数据和任务分成小块一个个地搞掉是基本思想

【 在 ftmit (八戒) 的大作中提到: 】
: 好的,多谢蝗虫。
: 另外,军版还有建议使用GPU,我没有折腾过GPU,您搞过没?如果有例子,麻烦给我一
: 个。再次感谢出手相助。

H
Huangchong

军版说 找老板要大型机时也是好办法 实际就是 “放在几百个计算机上跑”
有很多对科研单位不要钱的大型机资源

当然你也得考虑你原始数据到底多大 如果大到上传都要一个月 那还是本地解决吧

【 在 ftmit (八戒) 的大作中提到: 】
: 好的,多谢蝗虫。
: 另外,军版还有建议使用GPU,我没有折腾过GPU,您搞过没?如果有例子,麻烦给我一
: 个。再次感谢出手相助。

f
ftmit

tt列长度很小,在1000左右,问题是那个uu太大了

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 你的tt一列多长? tt uu各是多大的文件? 如果数据本身就是非常大 那还是只有
: 手动分块 在几个机器上分别跑 数据量特别大的话你得考虑限速的不只是内存和: CPU 硬盘IO可能是大头了
: 把数据和任务分成小块一个个地搞掉是基本思想

H
Huangchong

1000x1Gx4 所以你的tt是可以装在一个硬盘里的? 那就先把tt处理了呗 得到一个
10G大的极值列表

然后把uu一切 放到10个普通电脑上 几天就分析完了 反正uu你也不是存在一个硬盘上的吧?

【 在 ftmit (八戒) 的大作中提到: 】
: tt列长度很小,在1000左右,问题是那个uu太大了

f
ftmit

实际上的程序是这样的

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 1000x1Gx4 所以你的tt是可以装在一个硬盘里的? 那就先把tt处理了呗 得到一个: 10G大的极值列表
: 然后把uu一切 放到10个普通电脑上 几天就分析完了 反正uu你也不是存在一个硬盘
: 上的吧?

d
dakedo

vectorize也很重要
比如按vector的做法,5秒就能弄1e3列
写成loop,现在十几分钟了还没出来结果

当然c++/fortran这些编译的时候自动会做一些vectorization
编译里这些优化的选项要选

import numpy as np
import time

l = int(1e3)
m = int(1e3)
n = int(1e5)#生成随机数费时间,底下重复100次充数

tt = np.random.rand(l, m)
uu = np.random.rand(l, n)*3-1.5

# vector
start = time.time()

ttmax = np.max(tt, axis=1).reshape(-1, 1)
ttmin = np.min(tt, axis=1).reshape(-1, 1)

for i in range(100):

lt = np.less(uu, ttmax)
gt = np.greater(uu, ttmin)
check = np.logical_and(lt, gt)

end = time.time()
print(end-start)

# loop
start = time.time()

for i in range(l):
tmax = max(tt[i,:])
tmin = min(tt[i,:])

for j in range(100):
for k in range(n):
check = uu[i, k] < tmax and uu[i, k] > tmin

end = time.time()
print(end-start)

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 1000x1Gx4 所以你的tt是可以装在一个硬盘里的? 那就先把tt处理了呗 得到一个: 10G大的极值列表
: 然后把uu一切 放到10个普通电脑上 几天就分析完了 反正uu你也不是存在一个硬盘
: 上的吧?

H
Huangchong

那就切uu吧

这种计算似乎比从硬盘读uu也慢不了多少 那么多数据你拷贝一次不也要好多天吗 然后还得把抽出的结果再拷贝

我觉得 你可以看一下怎么优化存储 是不是可以用并行存储

【 在 ftmit (八戒) 的大作中提到: 】
: 实际上的程序是这样的

f
ftmit

tt和uu都不是从外部的硬盘上读取的,这两个都是这个程序本身产生的数组,如果根据最后的总数据定义一个二维数组,那size就应该是(N2,N1),这个大小已经远远超出硬盘大小了,根本没法编译。我只有一列一列做。

帖子中的code,为了说明问题,我做了简化,实际的code是这样的
do i1 = 1, 70
do i2 = i1+1, 71
do i3 = i2+1, 72
do i4 = i3+1, 73
.................... ! 这里都是do循环,模式和上边一样,如果把这些循环都完成,次数在10^9数量级

b = maxval(tt(:,i1)+tt(:,i2)+tt(:,i3)+tt(:,i4)......)
c = minval(tt(:,i1)+tt(:,i2)+tt(:,i3)+tt(:,i4)......)
A(:) = uu(:,i1)+uu(:,i2)+uu(:,i3)+uu(:,i4)......

do j = 1, N2 !这个是uu列长度,在10^7数量级

if ((A(j).lt.b).and.(A(j).gt.c) then
.......
.......
else
endif

enddo
enddo
enddo
enddo
【 在 Huangchong (净坛使者) 的大作中提到: 】
: 1000x1Gx4 所以你的tt是可以装在一个硬盘里的? 那就先把tt处理了呗 得到一个: 10G大的极值列表
: 然后把uu一切 放到10个普通电脑上 几天就分析完了 反正uu你也不是存在一个硬盘
: 上的吧?

f
ftmit

我把具体的code贴出来了,麻烦您看一下怎么切比较好,先谢谢您了。

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 那就切uu吧
: 这种计算似乎比从硬盘读uu也慢不了多少 那么多数据你拷贝一次不也要好多天吗
: 然后还得把抽出的结果再拷贝
: 我觉得 你可以看一下怎么优化存储 是不是可以用并行存储

H
Huangchong


只要uu列是互相独立的 你这个程序似乎非常容易并行

不会写多线程的话 可以把uu列的生成做成用命令行参数控制 比如 compute —tt
24 34 这样 告诉这个程序只处理 24 到34 列

然后用 gnu parallel 程序 (很简单 往上有例子https://www.gnu.org/software/
parallel/parallel_tutorial.html) 直接跑很多个fortran程序的副本

这种情况 很适合在小型机到大型机那种环境里跑 有多少逻辑CPU 你需要的总时间就缩小多少倍 放到好几个小电脑上跑也是一样的

【 在 ftmit (八戒) 的大作中提到: 】
: tt和uu都不是从外部的硬盘上读取的,这两个都是这个程序本身产生的数组,如果根据
: 最后的总数据定义一个二维数组,那size就应该是(N2,N1),这个大小已经远远超出硬
: 盘大小了,根本没法编译。我只有一列一列做。
: 帖子中的code,为了说明问题,我做了简化,实际的code是这样的
: do i1 = 1, 70
: do i2 = i1+1, 71
: do i3 = i2+1, 72
: do i4 = i3+1, 73
: .................... ! 这里都是do循环,模式和上边一样,如果把这些循环
: 都完成,次数在10^9数量级
: ...................

z
zhetian

你这个循环加上这个看看。应该可以加速,取决于你CPU有几个core。你这个数据完全
独立不冲突
!$OMP PARALLEL

你的循环

!$OMP END PARALLEL

【 在 ftmit (八戒) 的大作中提到: 】
: tt和uu都不是从外部的硬盘上读取的,这两个都是这个程序本身产生的数组,如果根据
: 最后的总数据定义一个二维数组,那size就应该是(N2,N1),这个大小已经远远超出硬
: 盘大小了,根本没法编译。我只有一列一列做。
: 帖子中的code,为了说明问题,我做了简化,实际的code是这样的
: do i1 = 1, 70
: do i2 = i1+1, 71
: do i3 = i2+1, 72
: do i4 = i3+1, 73
: .................... ! 这里都是do循环,模式和上边一样,如果把这些循环
: 都完成,次数在10^9数量级
: ...................

o
oOOo

楼主基本上狗屁不通。你们这样瞎支招是浪费时间

s
skl

楼主本来就是来请教的,不要这么命

【 在 oOOo(\_/o!o\_/) 的大作中提到: 】

: 楼主基本上狗屁不通。你们这样瞎支招是浪费时间

f
ftmit

好的,我马上加上这两句并行命令。谢谢大神指点迷津!

【 在 zhetian(叶凡) 的大作中提到: 】

: 你这个循环加上这个看看。应该可以加速,取决于你CPU有几个core。你这个数
据完全

: 独立不冲突

: !$OMP PARALLEL

: 你的循环

: !$OMP END PARALLEL

m
mitbbs847

不确定是否误解。可以用串循环取代套循环:

for i=1, N1 do max(i)=maxval(tt(:,i)), min(i)=minval(tt(:,i))

for j=1, N2 do if min(i) <= A(j) <= max(i) then ...

【 在 ftmit (八戒) 的大作中提到: 】
: 以前的帖子我没有表达清楚,把问题太过于简化了,更新一下。恳请各路大神出手指点
: 迷津了。没法子,灌水paper用。先谢谢了!
: 就是有两个二维数组,一个是tt,另一个是uu。然后根据tt中的每一列的最大值和最小
: 值,找出uu中相对应列的在该最大值和最小值之间的数值.
: 使用fortran,这是一个两层循环。具体结构,以及数据之间的关联,如下:
: do i = 1, N1 !N1大小在10^9数量级
: b = maxval(tt(:,i)) !tt为二维数组,列的长度为其他数值,且与i无关
: c = minval(tt(:,i))
: A(:) = uu(:,i) !uu为二维数组,列的长度为N2,且与i无关
: do j = 1, N2 !N2大小在10^7数量级
: ...................

f
familyguy

话糙理不糙。

本来也想随便给看看,结果没明白他到底想干啥。

【 在 oOOo (_/o!o_/) 的大作中提到: 】
: 楼主基本上狗屁不通。你们这样瞎支招是浪费时间

f
familyguy

如果问题的确像你理解的,确实不用嵌套循环。

不过第一个循环仍然非常costly,整个二维数组要被access 2×N1遍。而用两个N1尺寸
的数组,一个存每列的max value so far, 一个存每列的min value so far, 循序读取数据,边读边比较(属于某一列的数据去跟那两个数组里目前已知那一列的最大最小比较),这样整个数据只要读一遍即可。

【 在 mitbbs847 (迪西科) 的大作中提到: 】
: 不确定是否误解。可以用串循环取代套循环:
: for i=1, N1 do max(i)=maxval(tt(:,i)), min(i)=minval(tt(:,i))
: for j=1, N2 do if min(i) <= A(j) <= max(i) then ...

n
noles

这个是对的。

套循环是最笨的。very expensive.

【 在 mitbbs847 (迪西科) 的大作中提到: 】
: 不确定是否误解。可以用串循环取代套循环:
: for i=1, N1 do max(i)=maxval(tt(:,i)), min(i)=minval(tt(:,i))
: for j=1, N2 do if min(i) <= A(j) <= max(i) then ...

t
tofunao

数据on the fly的话那就跟I/O没关系

b = maxval(tt(:,i1)+tt(:,i2)+tt(:,i3)+tt(:,i4)......)
c = minval(tt(:,i1)+tt(:,i2)+tt(:,i3)+tt(:,i4)......)
显然这里你先算好
sum=tt(:,i1)+tt(:,i2)+tt(:,i3)+tt(:,i4)......
比算两遍要快
b = maxval(sum)
c = minval(sum)

不要用maxval, minval, 把max min放在一个loop里
Do I=1,N
if(max max=sum[i]
if(min>sum[i]
min=sum[i]
enddo
Why, 因为这个loop就是把所有数据遍历一次, 分开来就是遍历两次,主要花销是内存读取的bandwith。 但是如果你把他们放在一起, 数据还在register里, 基本没有额
外花销。
另外实现多线程, 用openmp也好, 自己写也好。
考虑 SIMD/vectorization 比如AVX512的intrinsics __m512 _mm512_max_ps(__m512 a, __m512 b), 可以一次处理16个数据。

内嵌循环也可以考虑多线程和SIMD

【 在 ftmit (八戒) 的大作中提到: 】
: tt和uu都不是从外部的硬盘上读取的,这两个都是这个程序本身产生的数组,如果根据
: 最后的总数据定义一个二维数组,那size就应该是(N2,N1),这个大小已经远远超出硬
: 盘大小了,根本没法编译。我只有一列一列做。
: 帖子中的code,为了说明问题,我做了简化,实际的code是这样的
: do i1 = 1, 70
: do i2 = i1+1, 71
: do i3 = i2+1, 72
: do i4 = i3+1, 73
: .................... ! 这里都是do循环,模式和上边一样,如果把这些循环
: 都完成,次数在10^9数量级
: ...................

f
familyguy

她的第二个数组有10^7行,如果也有10^9列,那么第二个数组数据量是10000 TB!能负责处理10000 TB数据的人,写程序不可能这么小白。

如果第二个数组远没有10^9列,那么第一个数组后面的很多列就用不着处理和存储,只用处理跟第二个数组列数相等的那些列。

其次这个的主要问题是超大数组按行存储,但需要按列处理,是数据access的问题,而运算量几乎可以忽略不计。

【 在 Huangchong (净坛使者) 的大作中提到: 】
: 1000x1Gx4 所以你的tt是可以装在一个硬盘里的? 那就先把tt处理了呗 得到一个: 10G大的极值列表
: 然后把uu一切 放到10个普通电脑上 几天就分析完了 反正uu你也不是存在一个硬盘
: 上的吧?