chore: import upstream snapshot with attribution
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,500 @@
|
||||
# Copyright (c) 2026 LightSeek Foundation
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import torch
|
||||
import zmq
|
||||
from viztracer import VizTracer
|
||||
|
||||
from tokenspeed.runtime.distributed.process_group_manager import (
|
||||
process_group_manager as pg_manager,
|
||||
)
|
||||
from tokenspeed.runtime.engine.generation_output_processor import RequestState
|
||||
from tokenspeed.runtime.engine.io_struct import (
|
||||
AbortReq,
|
||||
FlushCacheReqInput,
|
||||
FlushCacheReqOutput,
|
||||
GetInternalStateReq,
|
||||
GetInternalStateReqOutput,
|
||||
GetLoadReqInput,
|
||||
GetLoadReqOutput,
|
||||
IsSchedulerPausedReqInput,
|
||||
IsSleepingReqInput,
|
||||
PauseSchedulerReqInput,
|
||||
ProfileReq,
|
||||
ProfileReqOutput,
|
||||
ProfileReqType,
|
||||
ReleaseMemoryOccupationReqInput,
|
||||
ResumeMemoryOccupationReqInput,
|
||||
ResumeSchedulerReqInput,
|
||||
SetInternalStateReq,
|
||||
SetInternalStateReqOutput,
|
||||
TokenizedGenerateReqInput,
|
||||
)
|
||||
from tokenspeed.runtime.engine.request_types import FINISH_ABORT
|
||||
from tokenspeed.runtime.engine.scheduler_utils import make_spec
|
||||
from tokenspeed.runtime.execution.forward_batch_info import ForwardMode
|
||||
from tokenspeed.runtime.grammar.grammar_manager import GrammarManager
|
||||
from tokenspeed.runtime.multimodal.shm_transport import sync_shm_features
|
||||
from tokenspeed.runtime.pd.base.bootstrap import BootstrapInfo
|
||||
from tokenspeed.runtime.utils import broadcast_pyobj
|
||||
from tokenspeed.runtime.utils.dispatch import TypeBasedDispatcher
|
||||
from tokenspeed.runtime.utils.env import envs
|
||||
from tokenspeed.runtime.utils.hf_transformers_utils import get_tokenizer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from tokenspeed.runtime.utils.server_args import ServerArgs
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RequestHandler:
|
||||
"""
|
||||
1. Recv Reqs from ZMQ
|
||||
2. manage sessions
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
server_args: ServerArgs,
|
||||
hf_eos_token_id,
|
||||
max_req_len: int,
|
||||
vocab_size: int,
|
||||
recv_func,
|
||||
send_func,
|
||||
get_load_fn=None,
|
||||
architectures: list[str] | None = None,
|
||||
pause_controller=None,
|
||||
memory_controller=None,
|
||||
) -> None:
|
||||
|
||||
self.forward_ct = 0
|
||||
self.server_args = server_args
|
||||
# Owns pause/resume state; shared with the event loop. See pause.py.
|
||||
self.pause_controller = pause_controller
|
||||
# Owns release/resume_memory_occupation (data plane). See
|
||||
# memory_occupation.py. Shares the pause controller's drain machinery.
|
||||
self.memory_controller = memory_controller
|
||||
|
||||
mapping = server_args.mapping
|
||||
self.attn_tp_size = mapping.attn.tp_size
|
||||
self.attn_tp_rank = mapping.attn.tp_rank
|
||||
self.attn_tp_cpu_group = pg_manager.get_process_group(
|
||||
"gloo", mapping.attn.tp_group
|
||||
)
|
||||
self.attn_tp_src_rank = mapping.attn.tp_group[0]
|
||||
|
||||
self.hf_eos_token_id = hf_eos_token_id
|
||||
self.max_req_len = max_req_len
|
||||
self.vocab_size = vocab_size
|
||||
self.get_load_fn = get_load_fn
|
||||
|
||||
self.tokenizer = get_tokenizer(
|
||||
server_args.tokenizer,
|
||||
tokenizer_mode=server_args.tokenizer_mode,
|
||||
trust_remote_code=server_args.trust_remote_code,
|
||||
revision=server_args.revision,
|
||||
architectures=architectures,
|
||||
)
|
||||
|
||||
self.recv_func = recv_func
|
||||
self.send_func = send_func
|
||||
|
||||
self.control_request_dispatcher = TypeBasedDispatcher(
|
||||
[(ProfileReq, self.profile)]
|
||||
)
|
||||
|
||||
self.grammar_manager = GrammarManager(
|
||||
self.server_args, self.tokenizer, self.vocab_size
|
||||
)
|
||||
|
||||
self.init_profiler()
|
||||
|
||||
def recv_reqs(self) -> list:
|
||||
"""Receive results at attn_tp_rank = 0 and broadcast it to all other TP ranks."""
|
||||
if self.attn_tp_rank == 0:
|
||||
recv_reqs = []
|
||||
|
||||
while True:
|
||||
try:
|
||||
recv_req = self.recv_func.recv_pyobj(zmq.NOBLOCK)
|
||||
except zmq.ZMQError:
|
||||
break
|
||||
recv_reqs.append(recv_req)
|
||||
else:
|
||||
recv_reqs = None
|
||||
|
||||
if self.attn_tp_size != 1:
|
||||
recv_reqs = broadcast_pyobj(
|
||||
recv_reqs,
|
||||
self.attn_tp_rank,
|
||||
self.attn_tp_cpu_group,
|
||||
src=self.attn_tp_src_rank,
|
||||
)
|
||||
|
||||
if recv_reqs:
|
||||
sync_shm_features(recv_reqs, self.attn_tp_cpu_group, self.attn_tp_size)
|
||||
|
||||
return recv_reqs
|
||||
|
||||
def process_requests(self, recv_reqs: list):
|
||||
"""Dispatch control requests and return new generate request specs and states."""
|
||||
new_req_specs, req_states, bootstrap_infos, abort_rids = [], [], [], []
|
||||
for recv_req in recv_reqs:
|
||||
if isinstance(recv_req, TokenizedGenerateReqInput):
|
||||
req_spec, req_state, bootstrap_info = self.handle_generate_request(
|
||||
recv_req
|
||||
)
|
||||
|
||||
new_req_specs.append(req_spec)
|
||||
req_states.append(req_state)
|
||||
bootstrap_infos.append(bootstrap_info)
|
||||
elif isinstance(recv_req, ProfileReq):
|
||||
output = self.control_request_dispatcher(recv_req)
|
||||
if output is not None:
|
||||
self.send_func.send_pyobj(output)
|
||||
elif isinstance(recv_req, AbortReq):
|
||||
logger.debug("AbortReq for rid=%s", recv_req.rid)
|
||||
abort_rids.append(recv_req.rid)
|
||||
elif isinstance(recv_req, FlushCacheReqInput):
|
||||
# Prefix cache is owned by the scheduler path; acknowledge the
|
||||
# control request here so API callers still get a typed reply.
|
||||
self.send_func.send_pyobj(FlushCacheReqOutput(success=True))
|
||||
elif isinstance(recv_req, PauseSchedulerReqInput):
|
||||
# State change + reply (abort/wait replies are deferred by the
|
||||
# controller until the event loop observes a drained scheduler).
|
||||
self.pause_controller.handle_pause(recv_req)
|
||||
elif isinstance(recv_req, ResumeSchedulerReqInput):
|
||||
self.pause_controller.handle_resume(recv_req)
|
||||
elif isinstance(recv_req, IsSchedulerPausedReqInput):
|
||||
self.pause_controller.handle_is_paused(recv_req)
|
||||
elif isinstance(recv_req, ReleaseMemoryOccupationReqInput):
|
||||
# Deferred: pauses + drains, then frees GPU memory and replies.
|
||||
self.memory_controller.handle_release(recv_req)
|
||||
elif isinstance(recv_req, ResumeMemoryOccupationReqInput):
|
||||
self.memory_controller.handle_resume(recv_req)
|
||||
elif isinstance(recv_req, IsSleepingReqInput):
|
||||
self.memory_controller.handle_is_sleeping(recv_req)
|
||||
elif isinstance(recv_req, GetInternalStateReq):
|
||||
self.send_func.send_pyobj(GetInternalStateReqOutput(internal_state={}))
|
||||
elif isinstance(recv_req, SetInternalStateReq):
|
||||
self.send_func.send_pyobj(
|
||||
SetInternalStateReqOutput(updated=False, server_args={})
|
||||
)
|
||||
elif isinstance(recv_req, GetLoadReqInput):
|
||||
if self.get_load_fn is not None:
|
||||
self.send_func.send_pyobj(self.get_load_fn())
|
||||
else:
|
||||
self.send_func.send_pyobj(GetLoadReqOutput())
|
||||
else:
|
||||
raise NotImplementedError(f"Unsupported request type: {type(recv_req)}")
|
||||
return new_req_specs, req_states, bootstrap_infos, abort_rids
|
||||
|
||||
def handle_generate_request(
|
||||
self,
|
||||
recv_req: TokenizedGenerateReqInput,
|
||||
):
|
||||
if recv_req.bootstrap_port is None:
|
||||
recv_req.bootstrap_port = self.server_args.disaggregation_bootstrap_port
|
||||
|
||||
req_spec = make_spec(
|
||||
rid=recv_req.rid,
|
||||
tokens=recv_req.input_ids,
|
||||
)
|
||||
req_state = RequestState.from_recv_req(
|
||||
recv_req,
|
||||
tokenizer=self.tokenizer,
|
||||
eos_token_ids=self.hf_eos_token_id,
|
||||
)
|
||||
|
||||
if (
|
||||
recv_req.session_params is not None
|
||||
and recv_req.session_params.id is not None
|
||||
):
|
||||
req_state.finished_reason = FINISH_ABORT(
|
||||
f"Invalid request: session id {recv_req.session_params.id} does not exist"
|
||||
)
|
||||
return (
|
||||
req_spec,
|
||||
req_state,
|
||||
BootstrapInfo(
|
||||
recv_req.bootstrap_host,
|
||||
recv_req.bootstrap_port,
|
||||
recv_req.bootstrap_room,
|
||||
),
|
||||
)
|
||||
|
||||
req_state.sampling_params.max_new_tokens = min(
|
||||
(
|
||||
req_state.sampling_params.max_new_tokens
|
||||
if req_state.sampling_params.max_new_tokens is not None
|
||||
else 1 << 30
|
||||
),
|
||||
self.max_req_len - len(req_state.prompt_input_ids) - 1,
|
||||
)
|
||||
return (
|
||||
req_spec,
|
||||
req_state,
|
||||
BootstrapInfo(
|
||||
recv_req.bootstrap_host,
|
||||
recv_req.bootstrap_port,
|
||||
recv_req.bootstrap_room,
|
||||
),
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Profiling: torch / cuda / viztracer / mem-snapshot, driven by
|
||||
# /start_profile and /stop_profile control requests.
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def init_profiler(self):
|
||||
self.torch_profiler = None
|
||||
self.profiler_output_dir: str | None = None
|
||||
self.profiler_activities: list[str] | None = None
|
||||
self.profile_id: str | None = None
|
||||
self.profiler_start_forward_ct: int | None = None
|
||||
self.profiler_target_forward_ct: int | None = None
|
||||
self.profiler_target_prefill_ct: int | None = None
|
||||
self.profiler_target_decode_ct: int | None = None
|
||||
self.profiler_prefill_ct: int | None = None
|
||||
self.profiler_decode_ct: int | None = None
|
||||
self.profile_by_stage: bool = False
|
||||
self.profile_in_progress: bool = False
|
||||
self.viztracer = None
|
||||
|
||||
def init_profile(
|
||||
self,
|
||||
output_dir: str | None,
|
||||
start_step: int | None,
|
||||
num_steps: int | None,
|
||||
activities: list[str] | None,
|
||||
with_stack: bool | None,
|
||||
record_shapes: bool | None,
|
||||
profile_by_stage: bool,
|
||||
profile_id: str,
|
||||
) -> ProfileReqOutput:
|
||||
if self.profile_in_progress:
|
||||
return ProfileReqOutput(
|
||||
success=False,
|
||||
message="Profiling is already in progress. Call /stop_profile first.",
|
||||
)
|
||||
|
||||
self.profile_by_stage = profile_by_stage
|
||||
|
||||
if output_dir is None:
|
||||
output_dir = envs.TOKENSPEED_PROFILER_DIR.get()
|
||||
if activities is None:
|
||||
activities = ["CPU", "GPU"]
|
||||
|
||||
self.profiler_output_dir = output_dir
|
||||
self.torch_profiler_with_stack = with_stack
|
||||
self.torch_profiler_record_shapes = record_shapes
|
||||
self.profiler_activities = activities
|
||||
self.profile_id = profile_id
|
||||
|
||||
if start_step:
|
||||
self.profiler_start_forward_ct = max(start_step, self.forward_ct + 1)
|
||||
|
||||
if num_steps:
|
||||
if self.profile_by_stage:
|
||||
self.profiler_target_prefill_ct = num_steps
|
||||
self.profiler_target_decode_ct = num_steps
|
||||
self.profiler_prefill_ct = 0
|
||||
self.profiler_decode_ct = 0
|
||||
elif start_step:
|
||||
self.profiler_target_forward_ct = (
|
||||
self.profiler_start_forward_ct + num_steps
|
||||
)
|
||||
else:
|
||||
self.profiler_target_forward_ct = self.forward_ct + num_steps
|
||||
# The caller will be notified when reaching profiler_target_forward_ct
|
||||
else:
|
||||
self.profiler_target_forward_ct = None
|
||||
|
||||
return ProfileReqOutput(success=True, message="Succeeded")
|
||||
|
||||
def start_profile(
|
||||
self, stage: ForwardMode | None = None
|
||||
) -> ProfileReqOutput | None:
|
||||
stage_str = f" for {stage.name}" if stage else ""
|
||||
stage_suffix = f"-{stage.name}" if stage else ""
|
||||
|
||||
activities = self.profiler_activities
|
||||
with_stack = self.torch_profiler_with_stack
|
||||
record_shapes = self.torch_profiler_record_shapes
|
||||
|
||||
activity_map = {
|
||||
"CPU": torch.profiler.ProfilerActivity.CPU,
|
||||
"GPU": torch.profiler.ProfilerActivity.CUDA,
|
||||
}
|
||||
torchprof_activities = [
|
||||
activity_map[a] for a in activities if a in activity_map
|
||||
]
|
||||
|
||||
if torchprof_activities:
|
||||
self.torch_profiler = torch.profiler.profile(
|
||||
activities=torchprof_activities,
|
||||
with_stack=with_stack if with_stack is not None else True,
|
||||
record_shapes=record_shapes if record_shapes is not None else False,
|
||||
)
|
||||
self.torch_profiler.start()
|
||||
|
||||
if "MEM" in activities:
|
||||
torch.cuda.memory._record_memory_history(max_entries=100000)
|
||||
|
||||
if "CUDA_PROFILER" in activities:
|
||||
torch.cuda.cudart().cudaProfilerStart()
|
||||
|
||||
if "VIZTRACER" in activities:
|
||||
Path(self.profiler_output_dir).mkdir(parents=True, exist_ok=True)
|
||||
self.viztracer = VizTracer(
|
||||
output_file=os.path.join(
|
||||
self.profiler_output_dir,
|
||||
f"{self.profile_id}-TP-{self.attn_tp_rank}{stage_suffix}.viztracer.json",
|
||||
),
|
||||
min_duration=int(
|
||||
os.environ.get("TOKENSPEED_VIZTRACER_MIN_DURATION_US", "100")
|
||||
),
|
||||
log_async=True,
|
||||
)
|
||||
self.viztracer.start()
|
||||
|
||||
if activities:
|
||||
if activities != ["CUDA_PROFILER"]:
|
||||
logger.info(
|
||||
"Profiling starts%s. Traces will be saved to: %s (with profile id: %s)",
|
||||
stage_str,
|
||||
self.profiler_output_dir,
|
||||
self.profile_id,
|
||||
)
|
||||
self.profile_in_progress = True
|
||||
|
||||
return ProfileReqOutput(success=True, message="Succeeded")
|
||||
|
||||
def stop_profile(self, stage: ForwardMode | None = None) -> ProfileReqOutput | None:
|
||||
if not self.profile_in_progress:
|
||||
return ProfileReqOutput(
|
||||
success=False,
|
||||
message="Profiling is not in progress. Call /start_profile first.",
|
||||
)
|
||||
|
||||
Path(self.profiler_output_dir).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
stage_suffix = f"-{stage.name}" if stage else ""
|
||||
logger.info("Stop profiling%s...", stage_suffix)
|
||||
|
||||
if self.torch_profiler is not None:
|
||||
self.torch_profiler.stop()
|
||||
self.torch_profiler.export_chrome_trace(
|
||||
os.path.join(
|
||||
self.profiler_output_dir,
|
||||
f"{self.profile_id}-TP-{self.attn_tp_rank}{stage_suffix}.trace.json.gz",
|
||||
)
|
||||
)
|
||||
torch.distributed.barrier(self.attn_tp_cpu_group)
|
||||
|
||||
if self.profiler_activities is not None and "MEM" in self.profiler_activities:
|
||||
memory_profile_path = os.path.join(
|
||||
self.profiler_output_dir,
|
||||
f"{self.profile_id}-TP-{self.attn_tp_rank}-memory{stage_suffix}.pickle",
|
||||
)
|
||||
torch.cuda.memory._dump_snapshot(memory_profile_path)
|
||||
torch.cuda.memory._record_memory_history(enabled=None)
|
||||
|
||||
if "CUDA_PROFILER" in self.profiler_activities:
|
||||
torch.cuda.cudart().cudaProfilerStop()
|
||||
|
||||
if "VIZTRACER" in self.profiler_activities and self.viztracer is not None:
|
||||
self.viztracer.stop()
|
||||
self.viztracer.save()
|
||||
self.viztracer = None
|
||||
|
||||
if self.profiler_activities and self.profiler_activities != ["CUDA_PROFILER"]:
|
||||
logger.info(
|
||||
"Profiling done. Traces are saved to: %s", self.profiler_output_dir
|
||||
)
|
||||
|
||||
self.torch_profiler = None
|
||||
self.profile_in_progress = False
|
||||
self.profiler_start_forward_ct = None
|
||||
|
||||
return ProfileReqOutput(success=True, message="Succeeded.")
|
||||
|
||||
def _profile_batch_predicate(self, forward_mode=None):
|
||||
"""Check and toggle profiling based on forward step count.
|
||||
|
||||
Args:
|
||||
forward_mode: Optional ForwardMode for stage-based profiling.
|
||||
Not needed for step-count-based profiling.
|
||||
"""
|
||||
if self.profile_by_stage and forward_mode is not None:
|
||||
if forward_mode.is_extend_or_mixed():
|
||||
if self.profiler_prefill_ct == 0:
|
||||
self.start_profile(forward_mode)
|
||||
self.profiler_prefill_ct += 1
|
||||
if self.profiler_prefill_ct > self.profiler_target_prefill_ct:
|
||||
if self.profile_in_progress:
|
||||
self.stop_profile(stage=ForwardMode.EXTEND)
|
||||
elif forward_mode.is_decode():
|
||||
if self.profiler_decode_ct == 0:
|
||||
if self.profile_in_progress:
|
||||
self.stop_profile(ForwardMode.EXTEND)
|
||||
self.start_profile(forward_mode)
|
||||
self.profiler_decode_ct += 1
|
||||
if self.profiler_decode_ct > self.profiler_target_decode_ct:
|
||||
if self.profile_in_progress:
|
||||
self.stop_profile(stage=ForwardMode.DECODE)
|
||||
elif forward_mode.is_idle():
|
||||
pass
|
||||
else:
|
||||
if (
|
||||
self.profiler_target_forward_ct
|
||||
and self.profiler_target_forward_ct <= self.forward_ct
|
||||
):
|
||||
self.stop_profile()
|
||||
if (
|
||||
self.profiler_start_forward_ct
|
||||
and self.profiler_start_forward_ct == self.forward_ct
|
||||
):
|
||||
self.start_profile()
|
||||
|
||||
def profile(self, recv_req: ProfileReq):
|
||||
if recv_req.type == ProfileReqType.START_PROFILE:
|
||||
res = self.init_profile(
|
||||
recv_req.output_dir,
|
||||
recv_req.start_step,
|
||||
recv_req.num_steps,
|
||||
recv_req.activities,
|
||||
recv_req.with_stack,
|
||||
recv_req.record_shapes,
|
||||
recv_req.profile_by_stage,
|
||||
recv_req.profile_id,
|
||||
)
|
||||
if not res.success or recv_req.profile_by_stage or recv_req.start_step:
|
||||
return res
|
||||
return self.start_profile()
|
||||
else:
|
||||
return self.stop_profile()
|
||||
Reference in New Issue
Block a user