Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions eng/pipelines/runtime-wasm-perf-jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,28 @@ jobs:
performanceRepoAlias: ${{ parameters.performanceRepoAlias }}
${{ each parameter in parameters.jobParameters }}:
${{ parameter.key }}: ${{ parameter.value }}

# Run CoreCLR WASM R2R microbenchmarks using the same runtime payload.
# Browser WASM supports per-assembly R2R rather than composite R2R.
- ${{ if not(startswith(variables['Build.SourceBranch'], 'refs/heads/release')) }}:
- template: /eng/pipelines/common/platform-matrix.yml@${{ parameters.runtimeRepoAlias }}
parameters:
jobTemplate: /eng/pipelines/templates/runtime-perf-job.yml@${{ parameters.performanceRepoAlias }}
buildConfig: release
runtimeFlavor: coreclr
platforms:
- linux_x64
jobParameters:
liveLibrariesBuildConfig: Release
runtimeType: wasm_coreclr
codeGenType: 'wasm'
r2rRunType: 'r2r'
runKind: micro
logicalMachine: 'perfviper'
javascriptEngine: 'v8'
additionalJobIdentifier: coreclr_r2r_v8
downloadSpecificBuild: ${{ parameters.downloadSpecificBuild }}
runtimeRepoAlias: ${{ parameters.runtimeRepoAlias }}
performanceRepoAlias: ${{ parameters.performanceRepoAlias }}
${{ each parameter in parameters.jobParameters }}:
${{ parameter.key }}: ${{ parameter.value }}
45 changes: 44 additions & 1 deletion scripts/build_runtime_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,13 +325,16 @@ def build_wasm_payload(
def build_wasm_coreclr_payload(
browser_wasm_coreclr_archive_or_dir: str,
payload_parent_dir: str,
) -> None:
) -> str:
"""Create a WASM CoreCLR-only payload (dotnet).

This is a self-contained payload for running CoreCLR WASM benchmarks without
requiring Mono artifacts. The archive/directory layout is expected to contain
a `staging/` folder with `dotnet-none` (SDK) and
`microsoft.netcore.app.runtime.browser-wasm` (CoreCLR runtime pack) subfolders.

Returns:
The shared version of the locally built WebAssembly SDK and Crossgen2 packages.
"""

wasm_dotnet_dir = os.path.join(payload_parent_dir, "dotnet")
Expand All @@ -346,6 +349,7 @@ def build_wasm_coreclr_payload(
extract_archive_or_copy(
browser_wasm_coreclr_archive_or_dir, wasm_built_nugets_dir, prefix="staging/built-nugets/"
)
local_package_version = _get_wasm_local_package_version(wasm_built_nugets_dir)

# Determine version from the runtime pack directory structure
runtime_pack_src = os.path.join(
Expand Down Expand Up @@ -373,6 +377,45 @@ def build_wasm_coreclr_payload(
getLogger().warning("Microsoft.NETCore.App.Ref pack not found – cannot determine version")

_set_permissions_recursive([wasm_dotnet_dir, wasm_built_nugets_dir], mode=0o664)
return local_package_version


def _get_wasm_local_package_version(built_nugets_dir: str) -> str:
package_prefix = "Microsoft.NET.Sdk.WebAssembly.Pack."
wasm_sdk_packages = [
package for package in Path(built_nugets_dir).glob(f"{package_prefix}*.nupkg")
if not package.name.endswith(".symbols.nupkg")
]
if len(wasm_sdk_packages) != 1:
raise ValueError(
f"Expected one WebAssembly SDK package in {built_nugets_dir}, found {len(wasm_sdk_packages)}")

package_version = wasm_sdk_packages[0].name[len(package_prefix):-len(".nupkg")]
crossgen2_packages = [
package for package in Path(built_nugets_dir).glob("Microsoft.NETCore.App.Crossgen2.*.nupkg")
if not package.name.endswith(".symbols.nupkg")
]
if len(crossgen2_packages) != 1:
raise ValueError(
f"Expected one Crossgen2 package in {built_nugets_dir}, found {len(crossgen2_packages)}")
Comment thread
lewing marked this conversation as resolved.
if not crossgen2_packages[0].name.endswith(f".{package_version}.nupkg"):
raise ValueError(
f"WebAssembly SDK and Crossgen2 package versions do not match: "
f"{wasm_sdk_packages[0].name}, {crossgen2_packages[0].name}")

illink_packages = [
package for package in Path(built_nugets_dir).glob("Microsoft.NET.ILLink.Tasks.*.nupkg")
if not package.name.endswith(".symbols.nupkg")
]
if len(illink_packages) != 1:
raise ValueError(
f"Expected one ILLink package in {built_nugets_dir}, found {len(illink_packages)}")
Comment thread
lewing marked this conversation as resolved.
if illink_packages[0].name != f"Microsoft.NET.ILLink.Tasks.{package_version}.nupkg":
raise ValueError(
f"WebAssembly SDK and ILLink package versions do not match: "
f"{wasm_sdk_packages[0].name}, {illink_packages[0].name}")

return package_version


def build_r2r_interpreter_payload(
Expand Down
36 changes: 34 additions & 2 deletions scripts/micro_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from argparse import SUPPRESS
from io import StringIO
from logging import getLogger
from os import path
from os import environ, path
from subprocess import CalledProcessError
from traceback import format_exc
from typing import Any
Expand Down Expand Up @@ -149,6 +149,15 @@ def __get_bdn_arguments(user_input: str) -> list[str]:
help='Runtime flavor for WASM benchmarks: Mono (default) or CoreCLR'
)

parser.add_argument(
'--wasm-ready-to-run',
dest='wasm_ready_to_run',
required=False,
default=False,
action='store_true',
help='Publish CoreCLR WASM benchmarks as ReadyToRun'
)

parser.add_argument(
'--bdn-arguments',
dest='bdn_arguments',
Expand Down Expand Up @@ -233,7 +242,28 @@ def __process_arguments(args: list[str]):
)

parser = add_arguments(parser)
return parser.parse_args(args)
parsed_args = parser.parse_args(args)

try:
validate_wasm_ready_to_run(parsed_args)
except ArgumentTypeError as error:
parser.error(str(error))

return parsed_args


def validate_wasm_ready_to_run(args: Any) -> None:
if args.wasm_ready_to_run and (not args.wasm or args.wasm_runtime_flavor != 'CoreCLR'):
raise ArgumentTypeError('--wasm-ready-to-run requires --wasm --wasm-runtime-flavor CoreCLR')


def configure_wasm_ready_to_run(args: Any) -> None:
validate_wasm_ready_to_run(args)

# BenchmarkDotNet builds generated projects in child processes. MSBuild
# imports environment variables as properties, which lets the generated
# WASM project opt into R2R without requiring a new BDN command-line option.
environ['PERFLAB_WASM_READY_TO_RUN'] = str(args.wasm_ready_to_run).lower()


def __get_benchmarkdotnet_arguments(framework: str, args: Any) -> list[str]:
Expand Down Expand Up @@ -363,6 +393,8 @@ def run(
framework
))

configure_wasm_ready_to_run(args)

# dotnet exec
run_args = __get_benchmarkdotnet_arguments(framework, args)
target_framework_moniker = dotnet.get_target_framework_moniker(
Expand Down
53 changes: 47 additions & 6 deletions scripts/run_performance_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ def get_pre_commands(
runtime_type: str,
codegen_type: str,
build_config: str,
v8_version: str):
v8_version: str,
wasm_local_package_version: Optional[str] = None):
helix_pre_commands: list[str] = []

# Remember the previous PYTHONPATH that was set so it can be restored in the post commands
Expand Down Expand Up @@ -284,7 +285,14 @@ def get_pre_commands(
]

# Set up everything needed for WASM runs (both Mono and CoreCLR)
if runtime_type in ("wasm", "wasm_coreclr"):
if runtime_type in ("wasm", "wasm_coreclr"):
if runtime_type == "wasm_coreclr":
if not wasm_local_package_version:
raise ValueError("CoreCLR WASM requires a local WebAssembly toolchain package version")
Comment thread
lewing marked this conversation as resolved.
install_prerequisites += [
f"export PERFLAB_WASM_PACKAGE_VERSION={wasm_local_package_version}"
]

if os_distro == "azurelinux":
# Azure Linux uses tdnf package manager
install_prerequisites += [
Expand Down Expand Up @@ -644,6 +652,8 @@ def get_run_configurations(

if r2r_run_type == "nor2r":
configurations["R2RType"] = "nor2r"
elif r2r_run_type == "r2r":
configurations["R2RType"] = "r2r"

if runtime_type == "coreclr_r2r_interpreter":
configurations["R2RType"] = "r2r_interpreter"
Expand Down Expand Up @@ -690,7 +700,17 @@ def get_run_configurations(

return configurations

def get_work_item_command(os_group: str, target_csproj: str, architecture: str, perf_lab_framework: str, internal: bool, wasm: bool, bdn_artifacts_dir: str, wasm_coreclr: bool = False, only_sanity_check: bool = False):
def get_work_item_command(
os_group: str,
target_csproj: str,
architecture: str,
perf_lab_framework: str,
internal: bool,
wasm: bool,
bdn_artifacts_dir: str,
wasm_coreclr: bool = False,
wasm_ready_to_run: bool = False,
only_sanity_check: bool = False):
if os_group == "windows":
work_item_command = [
"python",
Expand Down Expand Up @@ -720,6 +740,8 @@ def get_work_item_command(os_group: str, target_csproj: str, architecture: str,
work_item_command += ["--run-isolated", "--wasm", "--dotnet-path", "$HELIX_CORRELATION_PAYLOAD/dotnet/"]
if wasm_coreclr:
work_item_command += ["--wasm-runtime-flavor", "CoreCLR"]
if wasm_ready_to_run:
work_item_command += ["--wasm-ready-to-run"]

work_item_command += ["--bdn-artifacts", bdn_artifacts_dir]

Expand Down Expand Up @@ -940,13 +962,14 @@ def run_performance_job(args: RunPerformanceJobArgs):
shutil.copytree(args.mono_dotnet_dir, mono_dotnet_path, dirs_exist_ok=True)

v8_version = ""
wasm_local_package_version = None
if wasm_coreclr:
if args.libraries_download_dir is None:
raise Exception("Libraries not downloaded for wasm_coreclr runs")

getLogger().info("Building wasm_coreclr payload directory")
browser_wasm_coreclr_dir = os.path.join(args.libraries_download_dir, "BrowserWasmCoreCLR")
build_wasm_coreclr_payload(
wasm_local_package_version = build_wasm_coreclr_payload(
browser_wasm_coreclr_dir,
payload_dir,
)
Expand Down Expand Up @@ -1133,7 +1156,15 @@ def run_performance_job(args: RunPerformanceJobArgs):
else:
agent_python = "python3"

helix_pre_commands = get_pre_commands(args.os_group, args.os_distro, args.internal, args.runtime_type, args.codegen_type, args.build_config, v8_version)
helix_pre_commands = get_pre_commands(
args.os_group,
args.os_distro,
args.internal,
args.runtime_type,
args.codegen_type,
args.build_config,
v8_version,
wasm_local_package_version)
helix_post_commands = get_post_commands(args.os_group, args.internal, args.runtime_type)

# Point ML.NET at the SSWE model that was pre-downloaded into the correlation payload above, so it
Expand Down Expand Up @@ -1372,7 +1403,17 @@ def get_bdn_args_for_coreroot_dir(coreroot_dir: Optional[str]):

def get_work_item_command_for_artifact_dir(artifact_dir: str):
assert args.target_csproj is not None
return get_work_item_command(args.os_group, args.target_csproj, args.architecture, perf_lab_framework, args.internal, wasm, artifact_dir, wasm_coreclr, args.only_sanity_check)
return get_work_item_command(
args.os_group,
args.target_csproj,
args.architecture,
perf_lab_framework,
args.internal,
wasm,
artifact_dir,
wasm_coreclr,
wasm_coreclr and args.r2r_run_type == "r2r",
args.only_sanity_check)

work_item_command = get_work_item_command_for_artifact_dir(bdn_artifacts_directory)
baseline_work_item_command = get_work_item_command_for_artifact_dir(bdn_baseline_artifacts_dir)
Expand Down
1 change: 1 addition & 0 deletions scripts/tests/test_run_performance_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def get_generated_apt_commands(*, internal: bool, runtime_type: str) -> list[str
codegen_type="jit",
build_config="Release",
v8_version="12.0.0",
wasm_local_package_version="11.0.0-ci" if runtime_type == "wasm_coreclr" else None,
Comment thread
lewing marked this conversation as resolved.
)

return [
Expand Down
Loading