一般调试
Contents
一般调试#
分布式应用程序比非分布式应用程序更强大但更复杂。 Ray 的某些行为可能会让用户措手不及,而这些设计选择可能有合理的论据。
此页面列出了用户可能遇到的一些常见问题。特别是,用户认为 Ray 在本地计算机上运行,虽然有时确实如此,但这会导致很多问题。
环境变量不会从Driver进程传递到Worker进程#
问题: 如果您在命令行(运行驱动程序的位置)设置环境变量,并且集群之前已启动,则该环境变量不会传递给集群中运行的所有工作线程。
示例: 如果运行 Ray 的目录中有一个文件 baz.py ,则运行以下命令:
import ray
import os
ray.init()
@ray.remote
def myfunc():
myenv = os.environ.get("FOO")
print(f"myenv is {myenv}")
return 1
ray.get(myfunc.remote())
# this prints: "myenv is None"
预期行为: 大多数人会期望(就好像它是一台机器上的单个进程一样)所有 Worker 中的环境变量都是相同的。不会是这样的。
修复: 使用运行时环境显式传递环境变量。
如果您调用 ray.init(runtime_env=...),
那么 Workers 将设置环境变量。
ray.init(runtime_env={"env_vars": {"FOO": "bar"}})
@ray.remote
def myfunc():
myenv = os.environ.get("FOO")
print(f"myenv is {myenv}")
return 1
ray.get(myfunc.remote())
# this prints: "myenv is bar"
文件名有时有效,有时无效#
问题: 如果您在任务或参与者中按名称引用文件,它有时会起作用,有时会失败。 这是因为如果任务或 actor 运行在集群的头节点上,它就会工作, 但如果任务或 actor 运行在另一台机器上,它就不会工作。
示例: 假设我们执行以下命令:
% touch /tmp/foo.txt
我有这个代码:
import os
import ray
@ray.remote
def check_file():
foo_exists = os.path.exists("/tmp/foo.txt")
return foo_exists
futures = []
for _ in range(1000):
futures.append(check_file.remote())
print(ray.get(futures))
那么你会得到 True 和 False 的混合结果。 如果
check_file() 在头节点上运行,或者我们在本地运行,它就可以工作。
但如果它在工作节点上运行,则会返回 False。
预期行为: M大多数人会期望这要么失败要么成功。毕竟是相同的代码。
修复
仅对此类应用程序使用共享路径 - 例如,如果您使用网络文件系统,则可以使用该系统,或者文件可以位于 S3 上。
不要依赖文件路径一致性。
占位组不可组合#
问题: 若有一个从占位组进行调用的任务,那么资源永远不会被分配并会挂起。
示例: 你正在使用 Ray Tune 创建占位组,并希望将其应用于目标函数,但是该目标函数使用了 Ray Task 本身,例如:
import ray
from ray import tune
def create_task_that_uses_resources():
@ray.remote(num_cpus=10)
def sample_task():
print("Hello")
return
return ray.get([sample_task.remote() for i in range(10)])
def objective(config):
create_task_that_uses_resources()
tuner = tune.Tuner(objective, param_space={"a": 1})
tuner.fit()
这将出错并显示信息:
ValueError: Cannot schedule create_task_that_uses_resources.<locals>.sample_task with the placement group
because the resource request {'CPU': 10} cannot fit into any bundles for the placement group, [{'CPU': 1.0}].
预期行为: 以上会进行执行。
修复: 在 create_task_that_uses_resources() 调用的 @ray.remote 声明中,包含
scheduling_strategy=PlacementGroupSchedulingStrategy(placement_group=None)。
def create_task_that_uses_resources():
+ @ray.remote(num_cpus=10, scheduling_strategy=PlacementGroupSchedulingStrategy(placement_group=None))
- @ray.remote(num_cpus=10)
过期的函数定义#
由于 Python 的微妙,如果你重新定义远程函数,你可能并不总能获得预期的行为。在这种情况下, Ray 可能没有运行该函数的最新版本。
假设你定义了一个远程函数 f ,然后重新定义它。Ray 应该运行最新的版本。
import ray
@ray.remote
def f():
return 1
@ray.remote
def f():
return 2
print(ray.get(f.remote())) # This should be 2.
2
但是,以下情况 Ray 不会将远程函数修改为最新版本(至少在不停止并重新启动 Ray 的情况下)。
该函数作为外部文件导入: 本例中,
f在外部文件file.py定义。如果你import file, 在file.py修改f定义,然后重新import file, 函数f不会更新。因为第二个导入被是为无操作并被忽略,
f仍由第一个文件导入定义。解决方案是使用
reload(file)来代替import file。重新加载会重新执行新的定义,并将其 导出到其他机器。请注意,在 Python 3 中,你需要执行from importlib import reload。该函数依赖于外部文件中的辅助函数: 在这种情况下,
f可以在 Ray 应用程序中定义,但依赖某些外部文件file.py定义的辅助 函数h。如果file.py中的h定义发生了变化,重新定义的f不会更新 Ray 使用最新版本的h。因为
f最初定义之后,它的定义会被发送到所有工作进程,并不会被 unpickled。在 unpick 期间,file.py被导入到 worker 。然后当f被重新定义,定义重新被所有 worker 并 unpickled。 但是file.py已经被导入,会作为第二次导入并忽略来对待。不幸的是,重新加载 driver 不会更新
h,重新加载需要在 worker 上进行。A solution to this problem is to redefine
fto reloadfile.pybefore it callsh. For example, if insidefile.pyyou have 解决这个问题的方式是在f调用h之前重新加载file.py。例如,在file.py中def h(): return 1
远程方法
f定义@ray.remote def f(): return file.h()
按照如下重新定义
f。@ray.remote def f(): reload(file) return file.h()
这会强制 worker 按照需要机型重新加载。注意,在 Python 3 在,你需要
from importlib import reload。
本文档讨论了人们在使用 Ray 时遇到的一些常见问题以及一些已知问题。如果您遇到其他问题, `请告知我们`_ 。