mirror of https://github.com/hpcaitech/ColossalAI
aibig-modeldata-parallelismdeep-learningdistributed-computingfoundation-modelsheterogeneous-traininghpcinferencelarge-scalemodel-parallelismpipeline-parallelism
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
3.9 KiB
127 lines
3.9 KiB
from typing import Any |
|
|
|
import torch |
|
from torch.fx.proxy import Proxy |
|
|
|
from colossalai.fx.tracer.meta_patch import meta_patched_function |
|
|
|
__all__ = ["ColoProxy"] |
|
|
|
|
|
class ColoProxy(Proxy): |
|
""" |
|
ColoProxy is a proxy class which uses meta tensor to handle data-dependent control flow. The original torch.fx proxy |
|
cannot be used to infer the condition statement, with this proxy, torch.fx can still run even with if statements. |
|
|
|
Example:: |
|
|
|
proxy = tracer.create_proxy(...) |
|
proxy.meta_data = torch.empty(4, 2, device='meta') |
|
print(len(proxy)) # expect output 4 |
|
|
|
""" |
|
|
|
def __init__(self, *args, **kwargs): |
|
super().__init__(*args, **kwargs) |
|
self.node._meta_data = None |
|
|
|
@property |
|
def meta_data(self): |
|
return self.node._meta_data |
|
|
|
@meta_data.setter |
|
def meta_data(self, data: Any): |
|
self.node._meta_data = data |
|
|
|
@property |
|
def has_meta_data(self): |
|
return self._meta_data is not None |
|
|
|
def _assert_meta_data_is_tensor(self): |
|
assert ( |
|
torch.is_tensor(self._meta_data) and self._meta_data.is_meta |
|
), f"Meta data is not a meta tensor for {self.node.name}" |
|
|
|
def _assert_has_meta_data(self): |
|
assert self._meta_data is not None, f"Meta data is not set for {self.node.name}" |
|
|
|
def __len__(self): |
|
self._assert_has_meta_data() |
|
return len(self.meta_data) |
|
|
|
def __int__(self): |
|
self._assert_has_meta_data() |
|
return int(self.meta_data) |
|
|
|
def __float__(self): |
|
self._assert_has_meta_data() |
|
return float(self.meta_data) |
|
|
|
def __bool__(self): |
|
self._assert_has_meta_data() |
|
return self.meta_data |
|
|
|
def __getattr__(self, k): |
|
return ColoAttribute(self, k) |
|
|
|
def __contains__(self, key): |
|
if self.node.op == "placeholder": |
|
# this is used to handle like |
|
# if x in kwargs |
|
# we don't handle this case for now |
|
return False |
|
return super().__contains__(key) |
|
|
|
|
|
def extract_meta(*args, **kwargs): |
|
""" |
|
This function is copied from _tracer_utils.py to avoid circular import issue. |
|
""" |
|
|
|
def _convert(val): |
|
if isinstance(val, ColoProxy): |
|
return val.meta_data |
|
elif isinstance(val, (list, tuple)): |
|
return type(val)([_convert(ele) for ele in val]) |
|
return val |
|
|
|
new_args = [_convert(val) for val in args] |
|
new_kwargs = {k: _convert(v) for k, v in kwargs.items()} |
|
return new_args, new_kwargs |
|
|
|
|
|
class ColoAttribute(ColoProxy): |
|
def __init__(self, root, attr: str): |
|
self.root = root |
|
self.attr = attr |
|
self.tracer = root.tracer |
|
self._node = None |
|
|
|
@property |
|
def node(self): |
|
if self._node is None: |
|
proxy = self.tracer.create_proxy("call_function", getattr, (self.root, self.attr), {}) |
|
if not isinstance(proxy, ColoProxy): |
|
meta_args, meta_kwargs = extract_meta(*(self.root, self.attr)) |
|
meta_out = getattr(*meta_args, **meta_kwargs) |
|
proxy = ColoProxy(proxy.node) |
|
proxy.meta_data = meta_out |
|
self._node = proxy.node |
|
|
|
return self._node |
|
|
|
def __call__(self, *args, **kwargs): |
|
proxy = self.tracer.create_proxy("call_method", self.attr, (self.root,) + args, kwargs) |
|
if not isinstance(proxy, ColoProxy): |
|
meta_args, meta_kwargs = extract_meta(*((self.root,) + args), **kwargs) |
|
method = getattr(meta_args[0].__class__, self.attr) |
|
if meta_patched_function.has(method): |
|
meta_target = meta_patched_function.get(method) |
|
elif meta_patched_function.has(method.__name__): |
|
meta_target = meta_patched_function.get(method.__name__) |
|
else: |
|
meta_target = method |
|
meta_out = meta_target(*meta_args, **meta_kwargs) |
|
proxy = ColoProxy(proxy.node) |
|
proxy.meta_data = meta_out |
|
return proxy
|
|
|