CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
166 lines
5.6 KiB
Python
166 lines
5.6 KiB
Python
# Copyright (c) Advanced Micro Devices, Inc.
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""
|
|
Thread limit tests.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
import pytest
|
|
from conftest import RocprofsysTest, get_rocprof_config
|
|
|
|
pytestmark = [pytest.mark.thread_limit]
|
|
|
|
# rocprof-sys may initialize internal/offset threads (sampling, ROCm, etc.)
|
|
# that consume thread slots without appearing as workload thread rows. Expect the
|
|
# highest profiled thread index to be at most thread_limit - INTERNAL_THREAD_OFFSET.
|
|
# The half-thread case is far below the limit, so use the exact highest launched index.
|
|
INTERNAL_THREAD_OFFSET = 20
|
|
OVERFLOW_THREAD_LOAD_MULTIPLIER = 8
|
|
|
|
# ============================================================================
|
|
# Thread Limit Fixtures
|
|
# ============================================================================
|
|
|
|
|
|
@pytest.fixture
|
|
def thread_limit_env() -> dict[str, str]:
|
|
"""Environment variables for thread limit tests."""
|
|
return {
|
|
"ROCPROFSYS_PROFILE": "ON",
|
|
"ROCPROFSYS_COUT_OUTPUT": "ON",
|
|
"ROCPROFSYS_USE_SAMPLING": "ON",
|
|
"ROCPROFSYS_SAMPLING_FREQ": "250",
|
|
"ROCPROFSYS_TIMEMORY_COMPONENTS": "wall_clock,peak_rss,page_rss",
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# Helper Function
|
|
# ============================================================================
|
|
|
|
|
|
def get_thread_limit() -> int:
|
|
"""Get the thread limit values for the test."""
|
|
# ROCPROFSYS_MAX_THREADS may have been explicitly set, so use that if it exists
|
|
import os
|
|
|
|
max_threads = os.getenv("ROCPROFSYS_MAX_THREADS")
|
|
if max_threads:
|
|
return int(max_threads)
|
|
|
|
rocprof_config = get_rocprof_config()
|
|
num_procs = rocprof_config.capabilities.num_procs
|
|
if num_procs < 128:
|
|
thread_count = 2048
|
|
else:
|
|
# Round up to nearest power of 2
|
|
n = 16 * num_procs - 1
|
|
n |= n >> 1
|
|
n |= n >> 2
|
|
n |= n >> 4
|
|
n |= n >> 8
|
|
n |= n >> 16
|
|
n |= n >> 32
|
|
thread_count = n + 1
|
|
return thread_count
|
|
|
|
|
|
def get_overflow_thread_load_count() -> int:
|
|
"""Thread count for load test — scales with the configured thread limit."""
|
|
return get_thread_limit() * OVERFLOW_THREAD_LOAD_MULTIPLIER
|
|
|
|
|
|
def get_expected_pass_value(thread_count: int, thread_limit: int) -> int:
|
|
"""Highest profiled thread index expected after internal/offset overhead."""
|
|
max_profiled = min(thread_count - 1, thread_limit - 1)
|
|
if thread_count == thread_limit // 2:
|
|
return max_profiled
|
|
return max_profiled - INTERNAL_THREAD_OFFSET
|
|
|
|
|
|
def get_expected_fail_value(thread_count: int, thread_limit: int) -> int:
|
|
"""Thread index that must not appear in profile output."""
|
|
if thread_count >= thread_limit:
|
|
return thread_limit + 1
|
|
return thread_count + 1
|
|
|
|
|
|
def get_thread_limit_warning_regex(thread_limit: int) -> str:
|
|
"""Regex for pthread_create_gotcha thread-limit warning in runner logs."""
|
|
return (
|
|
rf"\[warning\] Maximum allowed thread limit \({thread_limit}\) reached\. "
|
|
r"Further profiling will be disabled to prevent resource exhaustion\. "
|
|
r"Consider increasing the limit at compile time using the "
|
|
r"ROCPROFSYS_MAX_THREADS CMake option\."
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Thread Limit Tests
|
|
# ============================================================================
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"mode", ["sampling", "binary_rewrite", "runtime_instrument", "sys_run"]
|
|
)
|
|
@pytest.mark.parametrize(
|
|
"thread_count",
|
|
[
|
|
get_thread_limit() // 2,
|
|
get_thread_limit(),
|
|
get_thread_limit() * 2,
|
|
],
|
|
ids=["half", "at", "double"],
|
|
)
|
|
@pytest.mark.timeout(480)
|
|
@pytest.mark.class_name("thread-limit")
|
|
class TestThreadLimit(RocprofsysTest):
|
|
BINARY_REWRITE_ARGS = ["-e", "-v", "2", "-i", "1024", "--label", "return", "args"]
|
|
RUNTIME_INSTRUMENT_ARGS = ["-e", "-v", "1", "-i", "1024", "--label", "return", "args"]
|
|
|
|
def test(self, mode, thread_count, thread_limit_env):
|
|
result = self.run_test(
|
|
mode,
|
|
"thread-limit",
|
|
env=thread_limit_env,
|
|
run_args=["35", "2", str(thread_count)],
|
|
binary_rewrite_args=self.BINARY_REWRITE_ARGS,
|
|
runtime_instrument_args=self.RUNTIME_INSTRUMENT_ARGS,
|
|
)
|
|
thread_limit = get_thread_limit()
|
|
pass_value = get_expected_pass_value(thread_count, thread_limit)
|
|
fail_value = get_expected_fail_value(thread_count, thread_limit)
|
|
|
|
self.assert_regex(
|
|
result,
|
|
mode,
|
|
pass_regex=[f"\\|{pass_value}>>>"],
|
|
fail_regex=[f"\\|{fail_value}>>>"],
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("mode", ["sampling", "sys_run"])
|
|
@pytest.mark.timeout(600)
|
|
@pytest.mark.class_name("thread-limit-load-test")
|
|
class TestThreadLimitLoadTest(RocprofsysTest):
|
|
def test(self, mode, thread_limit_env, rocprof_config):
|
|
concurrency = min(rocprof_config.capabilities.num_procs, 16)
|
|
thread_count = get_overflow_thread_load_count()
|
|
result = self.run_test(
|
|
mode,
|
|
"thread-limit",
|
|
env=thread_limit_env,
|
|
run_args=["30", str(concurrency), str(thread_count)],
|
|
)
|
|
thread_limit = get_thread_limit()
|
|
pass_value = get_expected_pass_value(thread_count, thread_limit)
|
|
fail_value = get_expected_fail_value(thread_count, thread_limit)
|
|
warning_re = get_thread_limit_warning_regex(thread_limit)
|
|
self.assert_regex(
|
|
result,
|
|
mode,
|
|
pass_regex=[f"\\|{pass_value}>>>", warning_re],
|
|
fail_regex=[f"\\|{fail_value}>>>"],
|
|
)
|