94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
559 lines
18 KiB
Python
559 lines
18 KiB
Python
from contextlib import nullcontext
|
|
from types import SimpleNamespace
|
|
|
|
import torch
|
|
|
|
from sglang.multimodal_gen.runtime.layers.quantization.fp8 import Fp8Config
|
|
from sglang.multimodal_gen.runtime.layers.quantization.modelopt_quant import (
|
|
ModelOptFp8Config,
|
|
)
|
|
from sglang.multimodal_gen.runtime.loader.transformer_load_utils import (
|
|
_ModelOptFp8OffloadAdapter,
|
|
)
|
|
from sglang.multimodal_gen.runtime.managers.memory_managers import (
|
|
component_resident_strategies as component_resident_strategies_mod,
|
|
)
|
|
from sglang.multimodal_gen.runtime.managers.memory_managers import (
|
|
layerwise_offload as layerwise_offload_mod,
|
|
)
|
|
from sglang.multimodal_gen.runtime.managers.memory_managers.component_manager import (
|
|
ComponentUse,
|
|
build_component_residency_strategy,
|
|
)
|
|
from sglang.multimodal_gen.runtime.managers.memory_managers.component_resident_strategies import (
|
|
LayerwiseOffloadStrategy,
|
|
ResidentStrategy,
|
|
VanillaD2HStrategy,
|
|
)
|
|
from sglang.multimodal_gen.runtime.managers.memory_managers.layerwise_offload import (
|
|
LayerwiseOffloadableModuleMixin,
|
|
LayerwiseOffloadManager,
|
|
configure_layerwise_offload_modules,
|
|
get_layerwise_offload_component_names_for_pipeline,
|
|
is_layerwise_offloaded_module,
|
|
)
|
|
|
|
|
|
class _FakeStream:
|
|
def wait_stream(self, _stream) -> None:
|
|
return None
|
|
|
|
def wait_event(self, _event) -> None:
|
|
return None
|
|
|
|
|
|
class _FakeEvent:
|
|
def record(self, _stream) -> None:
|
|
return None
|
|
|
|
|
|
class _FakeDeviceModule:
|
|
Stream = _FakeStream
|
|
Event = _FakeEvent
|
|
|
|
@staticmethod
|
|
def is_available() -> bool:
|
|
return True
|
|
|
|
@staticmethod
|
|
def current_device() -> int:
|
|
return 0
|
|
|
|
@staticmethod
|
|
def current_stream() -> _FakeStream:
|
|
return _FakeStream()
|
|
|
|
@staticmethod
|
|
def stream(_stream):
|
|
return nullcontext()
|
|
|
|
|
|
class _DummyBlock(torch.nn.Module):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
base = torch.arange(12, dtype=torch.float32).reshape(3, 4)
|
|
self.weight = torch.nn.Parameter(base.t())
|
|
self.bias = torch.nn.Parameter(torch.arange(3, dtype=torch.float32))
|
|
|
|
|
|
class _DummyModel(torch.nn.Module):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.blocks = torch.nn.ModuleList([_DummyBlock()])
|
|
|
|
|
|
class _NestedDummyModel(torch.nn.Module, LayerwiseOffloadableModuleMixin):
|
|
layer_names = ["encoder.blocks"]
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.encoder = _DummyModel()
|
|
|
|
|
|
class _SharedBuffer(torch.nn.Module):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.register_buffer(
|
|
"cache", torch.arange(12, dtype=torch.float32).reshape(6, 2)
|
|
)
|
|
|
|
|
|
class _SharedBufferLayer(torch.nn.Module):
|
|
def __init__(self, shared: _SharedBuffer) -> None:
|
|
super().__init__()
|
|
self.shared = shared
|
|
self.weight = torch.nn.Parameter(torch.ones(2, 2, dtype=torch.float32))
|
|
|
|
|
|
class _SharedBufferModel(torch.nn.Module):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
shared = _SharedBuffer()
|
|
self.blocks = torch.nn.ModuleList(
|
|
[_SharedBufferLayer(shared), _SharedBufferLayer(shared)]
|
|
)
|
|
|
|
|
|
class _OrderedLinearLayer(torch.nn.Module):
|
|
def __init__(self, scale: float) -> None:
|
|
super().__init__()
|
|
self.weight = torch.nn.Parameter(torch.eye(2, dtype=torch.float32) * scale)
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
return x @ self.weight
|
|
|
|
|
|
class _ReverseLayerwiseModel(torch.nn.Module):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.blocks = torch.nn.ModuleList(
|
|
[
|
|
_OrderedLinearLayer(2.0),
|
|
_OrderedLinearLayer(3.0),
|
|
_OrderedLinearLayer(5.0),
|
|
]
|
|
)
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
for block in reversed(self.blocks):
|
|
x = block(x)
|
|
return x
|
|
|
|
|
|
class _NestedEncoderDummyModel(_NestedDummyModel):
|
|
layerwise_offload_dit_group_enabled = False
|
|
|
|
|
|
class _LayerwiseComponent(torch.nn.Module, LayerwiseOffloadableModuleMixin):
|
|
layer_names = ["blocks"]
|
|
|
|
def __init__(self, enabled: bool) -> None:
|
|
super().__init__()
|
|
self.blocks = torch.nn.ModuleList([_DummyBlock()])
|
|
self.layerwise_offload_managers = [SimpleNamespace(enabled=enabled)]
|
|
|
|
|
|
def _server_args(**kwargs):
|
|
defaults = dict(
|
|
use_fsdp_inference=False,
|
|
dit_cpu_offload=False,
|
|
text_encoder_cpu_offload=False,
|
|
image_encoder_cpu_offload=False,
|
|
vae_cpu_offload=False,
|
|
dit_offload_prefetch_size=1,
|
|
pin_cpu_memory=False,
|
|
)
|
|
defaults.update(kwargs)
|
|
return SimpleNamespace(**defaults)
|
|
|
|
|
|
def test_layerwise_offload_preserves_non_contiguous_stride(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
|
|
model = _DummyModel()
|
|
original_weight = model.blocks[0].weight.detach().clone()
|
|
original_stride = model.blocks[0].weight.stride()
|
|
assert not model.blocks[0].weight.is_contiguous()
|
|
|
|
manager = LayerwiseOffloadManager(
|
|
model=model,
|
|
layers_attr_str="blocks",
|
|
num_layers=1,
|
|
enabled=True,
|
|
pin_cpu_memory=False,
|
|
prefetch_size=1,
|
|
)
|
|
|
|
meta = manager._weight_metadata[0]["blocks.0.weight"]
|
|
assert meta["preserve_strides"] is True
|
|
|
|
restored_weight = model.blocks[0].weight.data
|
|
assert restored_weight.shape == original_weight.shape
|
|
assert restored_weight.stride() == original_stride
|
|
assert not restored_weight.is_contiguous()
|
|
assert torch.equal(restored_weight, original_weight)
|
|
|
|
manager.release_layer(0)
|
|
manager.prefetch_layer(0, non_blocking=False)
|
|
|
|
reloaded_weight = model.blocks[0].weight.data
|
|
assert reloaded_weight.stride() == original_stride
|
|
assert not reloaded_weight.is_contiguous()
|
|
assert torch.equal(reloaded_weight, original_weight)
|
|
|
|
|
|
def test_layerwise_offload_uses_normal_tensors_under_inference_mode(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
|
|
model = _DummyModel()
|
|
manager = LayerwiseOffloadManager(
|
|
model=model,
|
|
layers_attr_str="blocks",
|
|
num_layers=1,
|
|
enabled=True,
|
|
pin_cpu_memory=False,
|
|
prefetch_size=1,
|
|
)
|
|
|
|
with torch.inference_mode():
|
|
manager.release_layer(0)
|
|
manager.prefetch_layer(0, non_blocking=False)
|
|
|
|
assert model.blocks[0].weight._version >= 0
|
|
assert model.blocks[0].bias._version >= 0
|
|
|
|
|
|
def test_layerwise_offload_keeps_shared_buffers_resident(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
|
|
model = _SharedBufferModel()
|
|
original_cache = model.blocks[0].shared.cache.detach().clone()
|
|
|
|
manager = LayerwiseOffloadManager(
|
|
model=model,
|
|
layers_attr_str="blocks",
|
|
num_layers=2,
|
|
enabled=True,
|
|
pin_cpu_memory=False,
|
|
prefetch_size=1,
|
|
)
|
|
|
|
assert not any(
|
|
"cache" in name
|
|
for metadata in manager._weight_metadata.values()
|
|
for name in metadata
|
|
)
|
|
manager.release_layer(0)
|
|
|
|
cache = model.blocks[1].shared.cache
|
|
assert torch.equal(cache, original_cache)
|
|
assert torch.equal(cache.index_select(0, torch.tensor([2])), original_cache[2:3])
|
|
|
|
|
|
def test_layerwise_offload_loads_current_layer_for_reverse_execution(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
|
|
model = _ReverseLayerwiseModel()
|
|
x = torch.ones(1, 2, dtype=torch.float32)
|
|
expected = model(x)
|
|
|
|
LayerwiseOffloadManager(
|
|
model=model,
|
|
layers_attr_str="blocks",
|
|
num_layers=3,
|
|
enabled=True,
|
|
pin_cpu_memory=False,
|
|
prefetch_size=1,
|
|
)
|
|
|
|
assert torch.equal(model(x), expected)
|
|
|
|
|
|
def test_modelopt_fp8_adapter_keeps_layerwise_offload_enabled():
|
|
server_args = SimpleNamespace(
|
|
dit_cpu_offload=True,
|
|
dit_layerwise_offload=True,
|
|
)
|
|
quant_config = ModelOptFp8Config(is_checkpoint_fp8_serialized=True)
|
|
|
|
_ModelOptFp8OffloadAdapter._maybe_disable_incompatible_dit_offload_modes(
|
|
server_args=server_args,
|
|
quant_config=quant_config,
|
|
)
|
|
|
|
assert server_args.dit_cpu_offload is False
|
|
assert server_args.dit_layerwise_offload is True
|
|
|
|
|
|
def test_modelopt_fp8_adapter_does_not_change_online_fp8_offload():
|
|
server_args = SimpleNamespace(
|
|
dit_cpu_offload=True,
|
|
dit_layerwise_offload=False,
|
|
quantization="fp8",
|
|
)
|
|
|
|
_ModelOptFp8OffloadAdapter._maybe_disable_incompatible_dit_offload_modes(
|
|
server_args=server_args,
|
|
quant_config=Fp8Config(),
|
|
)
|
|
|
|
assert server_args.dit_cpu_offload is True
|
|
|
|
|
|
def test_layerwise_capability_selects_layerwise_strategy_for_any_component():
|
|
module = _LayerwiseComponent(enabled=True)
|
|
|
|
assert is_layerwise_offloaded_module(module)
|
|
strategy = build_component_residency_strategy(
|
|
"text_encoder", module, _server_args(text_encoder_cpu_offload=True)
|
|
)
|
|
|
|
assert isinstance(strategy, LayerwiseOffloadStrategy)
|
|
|
|
|
|
def test_layerwise_pipeline_selection_uses_dit_group(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
layerwise_module = _NestedDummyModel()
|
|
modules = {
|
|
"text_encoder": layerwise_module,
|
|
"text_encoder_alias": layerwise_module,
|
|
"scheduler": object(),
|
|
}
|
|
|
|
selected = get_layerwise_offload_component_names_for_pipeline(modules)
|
|
configured = configure_layerwise_offload_modules(modules, _server_args())
|
|
|
|
assert selected == ["text_encoder", "text_encoder_alias"]
|
|
assert configured == ["text_encoder"]
|
|
assert is_layerwise_offloaded_module(layerwise_module)
|
|
|
|
|
|
def test_layerwise_configuration_filters_by_component_name(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
text_encoder = _NestedEncoderDummyModel()
|
|
transformer = _NestedDummyModel()
|
|
vae = _NestedDummyModel()
|
|
modules = {
|
|
"custom_encoder_name": text_encoder,
|
|
"custom_transformer_name": transformer,
|
|
"custom_vae_name": vae,
|
|
}
|
|
|
|
configured = configure_layerwise_offload_modules(
|
|
modules, _server_args(), component_names=["custom_encoder_name"]
|
|
)
|
|
|
|
assert configured == ["custom_encoder_name"]
|
|
assert is_layerwise_offloaded_module(text_encoder)
|
|
assert not is_layerwise_offloaded_module(transformer)
|
|
assert not is_layerwise_offloaded_module(vae)
|
|
|
|
|
|
def test_layerwise_configuration_default_group_selects_non_dit_defaults(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
text_encoder = _NestedEncoderDummyModel()
|
|
text_encoder_2 = _NestedEncoderDummyModel()
|
|
transformer = _NestedDummyModel()
|
|
image_encoder = _NestedEncoderDummyModel()
|
|
vae = _NestedEncoderDummyModel()
|
|
audio_vae = _NestedEncoderDummyModel()
|
|
vocoder = _NestedEncoderDummyModel()
|
|
spatial_upsampler = _NestedEncoderDummyModel()
|
|
condition_image_encoder = _NestedEncoderDummyModel()
|
|
modules = {
|
|
"text_encoder": text_encoder,
|
|
"text_encoder_2": text_encoder_2,
|
|
"transformer": transformer,
|
|
"image_encoder": image_encoder,
|
|
"vae": vae,
|
|
"audio_vae": audio_vae,
|
|
"vocoder": vocoder,
|
|
"spatial_upsampler": spatial_upsampler,
|
|
"condition_image_encoder": condition_image_encoder,
|
|
}
|
|
|
|
configured = configure_layerwise_offload_modules(
|
|
modules, _server_args(), component_names=["default"]
|
|
)
|
|
|
|
assert get_layerwise_offload_component_names_for_pipeline(modules, ["default"]) == [
|
|
"text_encoder",
|
|
"text_encoder_2",
|
|
"image_encoder",
|
|
"vae",
|
|
"condition_image_encoder",
|
|
]
|
|
assert configured == [
|
|
"text_encoder",
|
|
"text_encoder_2",
|
|
"image_encoder",
|
|
"vae",
|
|
"condition_image_encoder",
|
|
]
|
|
assert is_layerwise_offloaded_module(text_encoder)
|
|
assert is_layerwise_offloaded_module(text_encoder_2)
|
|
assert not is_layerwise_offloaded_module(transformer)
|
|
assert is_layerwise_offloaded_module(image_encoder)
|
|
assert is_layerwise_offloaded_module(vae)
|
|
assert not is_layerwise_offloaded_module(audio_vae)
|
|
assert not is_layerwise_offloaded_module(vocoder)
|
|
assert not is_layerwise_offloaded_module(spatial_upsampler)
|
|
assert is_layerwise_offloaded_module(condition_image_encoder)
|
|
|
|
for component_name, module in (
|
|
("audio_vae", audio_vae),
|
|
("vocoder", vocoder),
|
|
("spatial_upsampler", spatial_upsampler),
|
|
):
|
|
configured = configure_layerwise_offload_modules(
|
|
modules, _server_args(), component_names=[component_name]
|
|
)
|
|
assert configured == [component_name]
|
|
assert is_layerwise_offloaded_module(module)
|
|
|
|
|
|
def test_layerwise_configuration_all_selects_every_capable_component(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
text_encoder = _NestedEncoderDummyModel()
|
|
transformer = _NestedDummyModel()
|
|
modules = {
|
|
"custom_encoder_name": text_encoder,
|
|
"custom_transformer_name": transformer,
|
|
"scheduler": object(),
|
|
}
|
|
|
|
configured = configure_layerwise_offload_modules(
|
|
modules, _server_args(), component_names=["all"]
|
|
)
|
|
|
|
assert configured == ["custom_encoder_name", "custom_transformer_name"]
|
|
assert is_layerwise_offloaded_module(text_encoder)
|
|
assert is_layerwise_offloaded_module(transformer)
|
|
|
|
|
|
def test_component_cpu_offload_strategy_remains_flag_driven():
|
|
strategy = build_component_residency_strategy(
|
|
"text_encoder", _DummyModel(), _server_args(text_encoder_cpu_offload=True)
|
|
)
|
|
assert isinstance(strategy, VanillaD2HStrategy)
|
|
|
|
strategy = build_component_residency_strategy(
|
|
"unknown_component", _DummyModel(), _server_args(text_encoder_cpu_offload=True)
|
|
)
|
|
assert isinstance(strategy, ResidentStrategy)
|
|
|
|
|
|
def test_resident_strategy_prepares_local_device_without_dtype(monkeypatch):
|
|
calls = []
|
|
|
|
def fake_module_to_local_device(module, *, dtype=None):
|
|
calls.append((module, dtype))
|
|
|
|
monkeypatch.setattr(
|
|
component_resident_strategies_mod,
|
|
"_module_to_local_device",
|
|
fake_module_to_local_device,
|
|
)
|
|
module = _DummyModel()
|
|
|
|
ResidentStrategy().prepare_for_use(
|
|
module,
|
|
ComponentUse(stage_name="DenoisingStage", component_name="transformer"),
|
|
SimpleNamespace(),
|
|
)
|
|
|
|
assert calls == [(module, None)]
|
|
|
|
|
|
def test_resident_strategy_keeps_fsdp_managed_module_owned_by_fsdp(monkeypatch):
|
|
calls = []
|
|
|
|
def fake_module_to_local_device(module, *, dtype=None):
|
|
calls.append((module, dtype))
|
|
|
|
monkeypatch.setattr(
|
|
component_resident_strategies_mod,
|
|
"_module_to_local_device",
|
|
fake_module_to_local_device,
|
|
)
|
|
module = type("FSDPDummyModel", (_DummyModel,), {})()
|
|
|
|
ResidentStrategy().prepare_for_use(
|
|
module,
|
|
ComponentUse(stage_name="TextEncodingStage", component_name="text_encoder"),
|
|
SimpleNamespace(),
|
|
)
|
|
|
|
assert calls == []
|
|
|
|
|
|
def test_layerwise_offload_aligns_contiguous_tensor_offsets(monkeypatch):
|
|
monkeypatch.setattr(
|
|
layerwise_offload_mod.torch, "get_device_module", lambda: _FakeDeviceModule
|
|
)
|
|
monkeypatch.setattr(layerwise_offload_mod.current_platform, "device_type", "cpu")
|
|
|
|
class _AlignedDummyBlock(torch.nn.Module):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.weight = torch.nn.Parameter(
|
|
torch.arange(9, dtype=torch.float32).reshape(3, 3)
|
|
)
|
|
self.bias = torch.nn.Parameter(torch.arange(3, dtype=torch.float32))
|
|
|
|
class _AlignedDummyModel(torch.nn.Module):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
self.blocks = torch.nn.ModuleList([_AlignedDummyBlock()])
|
|
|
|
model = _AlignedDummyModel()
|
|
original_weight = model.blocks[0].weight.detach().clone()
|
|
original_bias = model.blocks[0].bias.detach().clone()
|
|
|
|
manager = LayerwiseOffloadManager(
|
|
model=model,
|
|
layers_attr_str="blocks",
|
|
num_layers=1,
|
|
enabled=True,
|
|
pin_cpu_memory=False,
|
|
prefetch_size=1,
|
|
)
|
|
|
|
weight_meta = manager._weight_metadata[0]["blocks.0.weight"]
|
|
bias_meta = manager._weight_metadata[0]["blocks.0.bias"]
|
|
assert weight_meta["preserve_strides"] is False
|
|
assert bias_meta["preserve_strides"] is False
|
|
assert weight_meta["offset"] == 0
|
|
assert bias_meta["offset"] % 8 == 0
|
|
|
|
restored_weight = model.blocks[0].weight.data
|
|
restored_bias = model.blocks[0].bias.data
|
|
assert restored_weight.data_ptr() % 32 == 0
|
|
assert restored_bias.data_ptr() % 32 == 0
|
|
assert torch.equal(restored_weight, original_weight)
|
|
assert torch.equal(restored_bias, original_bias)
|