我正在使用一组计算机进行一些并行计算。我的主目录在整个集群中共享。在一台机器上,我有一个 ruby 代码,它创建包含计算命令的 bash 脚本,并将脚本写入 ~/q/目录。这些脚本被命名为 *.worker1.sh、*.worker2.sh 等。
在其他 20 台机器上,我运行了 20 个 python 代码(每台机器一个),它们(不断地)检查 ~/q/目录并查找属于该机器的作业,使用如下 python 代码:
jobs = glob.glob('q/*.worker1.sh')
[os.system('sh ' + job + ' &') for job in jobs]
对于一些额外的控制,在将 bash 脚本写入 q 目录后,ruby 代码将在 q 目录创建一个空文件,如 workeri.start (i = 1..20),python 代码将检查'start ' 文件在运行上面的代码之前。在 bash 脚本中,如果命令成功完成,bash 脚本将创建一个空文件,如“workeri.sccuess”,python 代码在运行上述代码后检查此文件以确保计算成功完成。如果 python 发现计算成功完成,它将删除 q 目录中的 'start' 文件,因此 ruby 代码知道作业成功完成。 20个bash脚本全部完成后,ruby代码会创建新的bash脚本,python会读取并执行新的脚本等等。
我知道这不是协调计算的优雅方式,但我还没有想出更好的方式来在不同机器之间进行通信。
现在的问题是:我预计这 20 个作业将在一定程度上并行运行。完成 20 项工作的总时间不会比完成一项工作的时间长很多。然而,这些作业似乎是顺序运行的,时间比我预期的要长得多。
我怀疑部分原因是多个代码同时读写同一个目录,但linux系统或python锁定了该目录,只允许一个进程操作该目录。这使得代码一次执行一个。
我不确定是不是这样。如果我将bash脚本拆分到不同的目录,让不同机器上的python代码读写不同的目录,是不是就可以解决问题了?还是有其他原因导致的问题?
非常感谢您的任何建议!如果我没有解释清楚,请告诉我。
一些附加信息: 我的主目录在/home/my_group/my_home,这是它的挂载信息 :/vol/my_group on/home/my_group type nfs (rw,nosuid,nodev,noatime,tcp,timeo=600,retrans=2,rsize=65536,wsize=65536,addr=...)
我说不断检查 q 目录,意思是像这样的 python 循环:
While True:
if 'start' file exists:
find the scripts and execute them as I mentioned above
最佳答案
I know this is not a elegant way to coordinate the computation, but I haven't figured out a better to communicate between different machines.
虽然这不是您直接提出的问题,但您真的应该真的考虑在这个级别上解决您的问题,使用某种共享消息队列可能很多 比依赖特定网络文件系统的锁定语义更易于管理和调试。
根据我的经验,最简单的设置和运行解决方案是 redis在当前运行创建作业的 Ruby 脚本的机器上。从字面上看,它应该像下载源代码一样简单,编译它并启动它。一旦 redis 服务器启动并运行,您就可以更改代码以将计算命令附加到一个或多个 Redis 列表。在 ruby 中你会使用 redis-rb像这样的库:
require "redis"
redis = Redis.new
# Your other code to build up command lists...
redis.lpush 'commands', command1, command2...
如果计算需要由某些机器处理,请使用像这样的每台机器列表:
redis.lpush 'jobs:machine1', command1
# etc.
然后在您的 Python 代码中,您可以使用 redis-py连接到 Redis 服务器并将作业从列表中拉出,如下所示:
from redis import Redis
r = Redis(host="hostname-of-machine-running-redis")
while r.llen('jobs:machine1'):
job = r.lpop('commands:machine1')
os.system('sh ' + job + ' &')
当然,您可以轻松地从队列中取出作业并在 Ruby 中执行它们:
require 'redis'
redis = Redis.new(:host => 'hostname-of-machine-running-redis')
while redis.llen('jobs:machine1')
job = redis.lpop('commands:machine1')
`sh #{job} &`
end
有了关于计算需求及其运行环境的更多详细信息,就可以推荐更简单的方法来管理它。
关于Python(或一般的 linux)文件操作流控制或文件锁定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8958939/