我正在尝试构建一个Process
子类,以便在我的桌面上使用多个GPU。
class GPUProcess(mp.Process):
used_ids: list[int] = []
next_id: int = 0
def __init__(self, *, target: Callable[[Any], Any], kwargs: Any):
gpu_id = GPUProcess.next_id
if gpu_id in GPUProcess.used_ids:
raise RuntimeError(
f"Attempt to reserve reserved processor {gpu_id} {self.used_ids=}"
)
GPUProcess.next_id += 1
GPUProcess.used_ids.append(gpu_id)
self._gpu_id = gpu_id
# Define target process func with contant gpu_id
def _target(**_target_kwargs):
target(
**_target_kwargs,
gpu_id=self.gpu_id,
)
super(GPUProcess, self).__init__(target=_target, kwargs=kwargs)
@property
def gpu_id(self):
return self._gpu_id
def __del__(self):
GPUProcess.used_ids.remove(self.gpu_id)
def __repr__(self) -> str:
return f"<{type(self)} gpu_id={self.gpu_id} hash={hash(self)}>"
# Test creation
def test_process_creation():
# Expect two gpus
def dummy_func(*args):
return args
processes = []
for _ in range(2):
p = GPUProcess(
target=dummy_func,
kwargs=dict(a=("a", "b", "c")),
)
processes.append(p)
for p in processes:
p.start()
for p in processes:
p.join()
del processes
assert GPUProcess.used_ids == [], f"{GPUProcess.used_ids=}!=[]"
if __name__ == "__main__":
test_process_creation()
__del__
不会被第二个进程调用。AssertionError: GPUProcess.used_ids=[1]!=[]
为什么第二个__del__
没有被调用?
后来,我使用这个类和mp.Pool
来运行一个大的有效负载集,每个GPU使用一个GPUProcess
和一个使用gpu_id
关键字来决定使用的设备的函数。这在Python中是明智的方法吗?
1条答案
按热度按时间z4iuyo4d1#
简短的回答是,
__del__
没有被调用,因为上一个for循环中的变量p
仍在引用第二个process对象根据官方文档,当调用
del object
时,不保证调用object.__del__
。当对象的引用计数为0时,调用
object.__del__
。del
关键字将对象的引用计数减少1。因此,您可以通过在调用Assert以删除该引用之前设置
p = None
或del p
来解决这个问题,或者在退出test_process_creation()
函数之后调用Assert,因为这将删除该堆栈级别的所有引用计数。我发现this video from the mCoding channel是关于
__del__
以及如何不使用它的非常有用的信息。顺便说一句,我会注意到,在测试你的代码时,我发现了一些你需要解决的问题:
1.在传递Assert之后,我遇到了错误
ValueError: list.remove(x): x not in list
。您的__del__()
方法应该使用try/except,或者在删除它之前检查self.gpu_id是否在列表中。1.在
test_process_creation()
中,您将伪函数定义为dummy_func(*args)
,但您专门创建的测试进程具有使用kwargs=dict(a=("a", "b", "c"))
定义的关键字参数。这将导致异常TypeError: test_process_creation.<locals>.dummy_func() got an unexpected keyword argument 'a'
。最简单的解决方案是将定义更改为dummy_func(**kwargs)