Files
paddlepaddle--paddle/python/paddle/distributed/auto_parallel/static/planner_v2.py
T
2026-07-13 12:40:42 +08:00

181 lines
7.0 KiB
Python
Executable File

# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import os
import sys
import numpy as np
from paddle.distributed.auto_parallel.process_mesh import ProcessMesh
from paddle.distributed.auto_parallel.static.dist_attribute import (
OperatorDistAttr,
TensorDistAttr,
)
from paddle.distributed.auto_parallel.static.dist_op import DistributedOperator
from paddle.distributed.auto_parallel.static.dist_tensor import (
DistributedTensor,
)
from ...utils.log_utils import get_logger
from .completion import Completer
from .dist_context import get_default_distributed_context
from .tuner.parallel_tuner import ParallelTuner
from .tuner.rule_based_tuner import RuleBasedTuner
from .utils import is_naive_data_parallel
class Planner:
def __init__(self, mode, dist_context):
self._mode = mode
self._dist_context = dist_context
self._load = False # load dist_attr from file
# NOTE: [HighOrderGrad]. There are grad ops in forward phase, and it need
# dependency of backward-forward ops in forward completion.
default_ctx = get_default_distributed_context()
self._dist_context._dist_op_context = default_ctx.dist_op_context
self._dist_context.data_parallel = default_ctx.data_parallel
if not is_naive_data_parallel(self._dist_context):
# Use SSA graph for complex parallelism
self._dist_context.initialize(with_graph=True)
else:
# Use program for data parallel parallelism
self._dist_context.initialize(with_graph=False)
self._completer = Completer(self._dist_context)
self._strategy = dist_context.strategy
# set parallel tuner for auto search
if self._strategy.auto_mode == "full_random":
self._parallel_tuner = ParallelTuner(
self._dist_context, mode=self._mode
)
elif self._strategy.auto_mode == "full_rule_based":
self._parallel_tuner = RuleBasedTuner(
self._dist_context, mode=self._mode
)
@property
def completer(self):
return self._completer
def plan(self):
logger = get_logger(logging.INFO)
path = None
if self._dist_context._json_config:
try:
path = self._dist_context._json_config["tuner_load_path"]
except:
path = None
if path and os.path.exists(path):
try:
with open(path, "rb") as f:
from paddle.framework.restricted_unpickler import (
safe_load_pickle,
)
dist_attrs = safe_load_pickle(f)
tensor_dist_attrs = dist_attrs["tensor"]
op_dist_attrs = dist_attrs["op"]
process_meshes = dist_attrs["process_meshes"]
cluster = dist_attrs["cluster"]
last_gpu_model = cluster.machines[0].devices[0].model
last_gpu_memory = cluster.machines[0].devices[0].memory
last_node_count = len(cluster.machines)
last_device_count = len(cluster.get_all_devices("GPU"))
gpu_model = (
self._dist_context.cluster.machines[0].devices[0].model
)
gpu_memory = (
self._dist_context.cluster.machines[0].devices[0].memory
)
node_count = len(self._dist_context.cluster.machines)
device_count = len(
self._dist_context.cluster.get_all_devices("GPU")
)
if (
gpu_model != last_gpu_model
or gpu_memory != last_gpu_memory
or last_node_count != node_count
or device_count != last_device_count
):
logger.info(
f"The cluster {node_count} nodes {device_count} {gpu_model} devices is different from the saved last cluster {last_node_count} nodes {last_device_count} {last_gpu_model} devices, so we run the planner again."
)
need_set_dist_attr = False
else:
need_set_dist_attr = True
except:
need_set_dist_attr = False
if need_set_dist_attr:
for key in op_dist_attrs:
serial_op = self._dist_context._dist_ops_for_program[
key
].serial_op
# clear dist attr
serial_op.dist_attr = OperatorDistAttr(serial_op.desc)
serial_op.dist_attr.parse_from_string(op_dist_attrs[key])
self._dist_context._dist_ops_for_program[key] = (
DistributedOperator(serial_op)
)
for key in tensor_dist_attrs:
serial_tensor = (
self._dist_context._dist_tensors_for_program[
key
].serial_tensor
)
# clear dist attr
serial_tensor.dist_attr = TensorDistAttr(serial_tensor.desc)
serial_tensor.dist_attr.parse_from_string(
tensor_dist_attrs[key]
)
self._dist_context._dist_tensors_for_program[key] = (
DistributedTensor(serial_tensor)
)
process_meshes = []
for item in dist_attrs["process_meshes"]:
process_ids = item[0]
shape = item[1]
process_meshes.append(
ProcessMesh(
np.array(process_ids).reshape(shape).tolist()
)
)
self._dist_context.process_meshes = process_meshes
self._load = True
logger.info(
f"The parallel strategy has been loaded from {path}"
)
if not self._load:
if self._strategy.auto_mode != "semi":
self._parallel_tuner.tune()
else:
self._completer.complete_forward_annotation()
if os.getenv("PADDLE_AUTO_PARALLEL_STAGE", "run") != "run":
sys.exit()
# parse forward sub block
self._dist_context.block_state.parse_forward_blocks(
self._dist_context.serial_main_program
)