【问题标题】:How to use multi CPUs to make the computation faster in Linux如何在 Linux 中使用多 CPU 使计算更快
【发布时间】:2015-10-19 21:05:48
【问题描述】:

我想加快我在 linux 中的计算速度?例如,我有以下脚本:example.sh

#!/bin/bash
n1=1; n2=1000
while [ $n1 -le $n2 ]
do
cat << EOF > test.f 
       open (1,file='$n1.txt',form='formatted', status='unknown')
       write (1, *) "Hello World!"
       stop;end
EOF
gfortran test.f
./a.out
(( n1++ ))
done

如果我执行上面的example.sh,那么它将执行1000次。我原来的 fortran 程序很小,执行一次大约需要 3 分钟。所以完成我的脚本需要大约 2 天的时间。那么如何使用多 CPU 来加快速度呢?我的系统显示 CPU:8。

【问题讨论】:

  • 你正在通过脚本编写一个fortran程序,编译这个程序,然后运行它?这必须是你这样做的最糟糕的方式。将您的循环和代码全部放在一个 fortran 程序中,该程序采用输入参数并编译/运行一次。然后我们可以谈谈如何实现 MPI。
  • 谢谢@NoseKnowsAll。是的,我的脚本编写了一个 fortran 程序,编译然后运行。因为相同的 fortran 程序将针对许多不同的文本文件执行。所以你的意思是我应该用fortran代码写所有东西?
  • 是的。至少,编写一个接受文件名的 fortran 程序。编译一次。运行您的脚本循环,它只会更改您输入到程序中的文件名。你应该只编译一次程序。
  • 之后的下一步是编写一个 fortran 程序,该程序接受一个文件名,该文件名对应于包含您要运行的所有文件名的 ASCII 文件。然后,您的循环将位于 fortran 程序的内部,理论上您可以使用 MPI 或 OpenMP 实现该循环。
  • @NoseKnowsAll Unix 的方式是让程序将文件作为参数处理,而不是读取文件列表。这允许“自然”使用带有 globbing 的 shell。而且 Fortran 现在有了正确的读取参数的方法,因此非常适合。

标签: linux shell unix parallel-processing mpi


【解决方案1】:

GNU Parallel 做得很好。

GNU Parallel 可用于使用一台或多台计算机并行执行作业。这消除了管理进程的需要,并为我们提供了一种并行执行二进制文件的方法。

所以,你的脚本(让我们称之为 test.sh)变成了

#!/bin/bash
n1=$1
cat << EOF > test.f 
   open (1,file='$n1.txt',form='formatted', status='unknown')
   write (1, *) "Hello World!"
   stop;end
EOF
gfortran test.f
./a.out

您现在可以使用“并行”调用脚本 test.sh,如下所示。

seq 1000 | parallel -j 8 --workdir $PWD ./test.sh {}

-j 8 指定要运行的作业数。这将启动 ./test.sh 1, ./test.sh 2, ./test.sh 3 ... ./test.sh 1000 并确保其中 8 个并行运行。

【讨论】:

  • 这似乎是一个很好的方法。除了第一行 n1=$1 外,我理解了整个过程。我知道 n1 被分配了 $1,但我不知道 $1 是什么意思。你会这么好心解释一下吗?非常感谢@Sidharth.N.Kashyap
  • $1 捕获命令行中提供的第一个参数。例如:./test.sh abc abc 将在 $1 内提供给您
【解决方案2】:

这段代码将自己执行八次(每次在后台),然后等待它们全部退出:

#!/bin/bash

if [ "$#" = 0 ]; then

  trap 'kill -9 $job_list' 0  # clean up if given CTRL+C

  i=0; CPUs=8; total=1000
  while [ $i -lt $CPUs ]; do
    from=$((i*total/CPUs+1))
    i=$((i+1))
    to=$((i*total/CPUs))
    bash $0 $from $to &
    job_list="$job_list $!"
  done

  wait
  trap - 0  # done, remove cleanup code
  exit

fi

n1=$1; n2=$2
while [ $n1 -le $n2 ]; do
  cat << EOF > test$n1.f 
         open (1,file='$n1.txt',form='formatted', status='unknown')
         write (1, *) "Hello World!"
         stop;end
EOF
  trap "rm -f test$n1.f a$n1.out" 0
  gfortran test$n1.f -o a$n1.out
  ./a$n1.out
  rm test$n1.f a$n1.out
  (( n1++ ))
done

这将运行给定 Fortran 代码的 125 次运行的 8 个循环。所有八个循环同时运行。

这使用waitjob control:在命令末尾使用&amp; 将其作为后台作业运行(并继续运行而不等待它完成),$! 是最近的后台作业(我们将其保存在 $job_list 中,以便我们可以在需要时将其终止),而 wait 在没有参数时将等待 所有 后台作业,然后再继续。

【讨论】:

  • 非常感谢@Adam Katz。在我开始执行这个脚本之前,我可以确认以下内容。我是远程服务器的用户。那么是否会影响其他人的工作,因为我们一开始就使用了kill -9命令。
  • 我不是 Fortran 专家(我也不知道您会运行该特定代码),但如果运行成本很高,您将敲打所有八个内核,而其他内核可能不喜欢。 kill 命令是安全的,因为它只会杀死我们创建和记录的进程(请参阅$job_list)。 trap 仅在程序 退出 时触发,如果它干净地退出,此代码将删除 trap 并且永远不会杀死任何东西。 kill 真的只是为了如果你需要点击^C
【解决方案3】:

正如其他人所提到的,重写程序以接受命令行参数是值得的。我在下面的示例中扩展了代码:

runfile.f90

program runfile
  character(len=128) :: arg
  integer :: i

  do i = 1,command_argument_count()
     call get_command_argument(i, arg)

     open (1, file=trim(arg) // ".txt", form='formatted', status='unknown')
     write (1, *) "Hello world!"
  end do
end program runfile

然后您可以编译该程序一次(如果您对速度感兴趣,可能还使用一些更激进的编译标志)并与另一个答案中提到的 GNU 并行并行化:

seq -w 1000 | parallel -j 8 ./runfile {}

我完成命令行处理的方式与许多 Unix 工具一样,因此即使您不进行并行化,您现在也可以像 ./runfile {01..10} 这样调用您的程序来执行 10 次运行。此外,如果文本文件实际上是要处理的东西,您将能够稍微修改程序以允许像./runfile *.txt 这样的调用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    • 2011-07-27
    • 2014-09-23
    • 1970-01-01
    相关资源
    最近更新 更多