diff --git a/eng/Versions.props b/eng/Versions.props
index da50f10c2bb..a4413c71b41 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -11,7 +11,7 @@
10.0.0-rc.1.25555.107
10.0.0-rc.1.25555.107
10.0.0-rc.1.25555.107
- 0.16.0-custom.20260127.101
+ 0.16.0-custom.20260223.100
10.0.0-rc.1.25555.107
11.0.0-prerelease.26064.3
diff --git a/eng/pipelines/runtime-wasm-perf-jobs.yml b/eng/pipelines/runtime-wasm-perf-jobs.yml
index 9fc2e46e690..b7b32a9ed97 100644
--- a/eng/pipelines/runtime-wasm-perf-jobs.yml
+++ b/eng/pipelines/runtime-wasm-perf-jobs.yml
@@ -130,3 +130,25 @@ jobs:
performanceRepoAlias: ${{ parameters.performanceRepoAlias }}
${{ each parameter in parameters.jobParameters }}:
${{ parameter.key }}: ${{ parameter.value }}
+
+ # Run CoreCLR WASM microbenchmarks perf job
+ - 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'
+ runKind: micro
+ logicalMachine: 'perfviper'
+ javascriptEngine: 'v8'
+ additionalJobIdentifier: coreclr_v8
+ downloadSpecificBuild: ${{ parameters.downloadSpecificBuild }}
+ runtimeRepoAlias: ${{ parameters.runtimeRepoAlias }}
+ performanceRepoAlias: ${{ parameters.performanceRepoAlias }}
+ ${{ each parameter in parameters.jobParameters }}:
+ ${{ parameter.key }}: ${{ parameter.value }}
diff --git a/eng/pipelines/templates/runtime-perf-job.yml b/eng/pipelines/templates/runtime-perf-job.yml
index 99c63d8e159..6c6d1d5040e 100644
--- a/eng/pipelines/templates/runtime-perf-job.yml
+++ b/eng/pipelines/templates/runtime-perf-job.yml
@@ -90,7 +90,7 @@ jobs:
displayName: Add Properties To Pipeline Env
- ${{ if eq(parameters.runtimeType, 'wasm') }}:
- # Download wasm
+ # Download wasm (Mono)
- ${{ if eq(parameters.downloadSpecificBuild.buildId, '') }}:
- template: /eng/pipelines/templates/download-artifact-step.yml
parameters:
@@ -106,6 +106,25 @@ jobs:
artifactName: BrowserWasm
displayName: BrowserWasm
${{ insert }}: ${{ parameters.downloadSpecificBuild }}
+
+ - ${{ if eq(parameters.runtimeType, 'wasm_coreclr') }}:
+ # Download wasm (CoreCLR)
+ - ${{ if eq(parameters.downloadSpecificBuild.buildId, '') }}:
+ - template: /eng/pipelines/templates/download-artifact-step.yml
+ parameters:
+ unpackFolder: $(librariesDownloadDir)/BrowserWasmCoreCLR
+ artifactFileName: BrowserWasmCoreCLR.tar.gz
+ artifactName: BrowserWasmCoreCLR
+ displayName: BrowserWasmCoreCLR
+ - ${{ if ne(parameters.downloadSpecificBuild.buildId, '') }}:
+ - template: /eng/pipelines/templates/download-specific-artifact-step.yml
+ parameters:
+ unpackFolder: $(librariesDownloadDir)/BrowserWasmCoreCLR
+ artifactFileName: BrowserWasmCoreCLR.tar.gz
+ artifactName: BrowserWasmCoreCLR
+ displayName: BrowserWasmCoreCLR
+ ${{ insert }}: ${{ parameters.downloadSpecificBuild }}
+
- ${{ elseif and(eq(parameters.codeGenType, 'AOT'), not(eq(parameters.runtimeType, 'AndroidMono'))) }}:
- template: /eng/pipelines/templates/download-artifact-step.yml
parameters:
diff --git a/scripts/benchmarks_local.py b/scripts/benchmarks_local.py
index 61a860a556c..ae0be0a01c2 100644
--- a/scripts/benchmarks_local.py
+++ b/scripts/benchmarks_local.py
@@ -245,14 +245,6 @@ def generate_all_runtype_dependencies(parsed_args: Namespace, repo_path: str, co
copy_directory_contents(src_dir_dotnet_latest, dest_dir_wasm_dotnet)
src_dir_built_nugets = os.path.join(repo_path, "artifacts", "packages", "Release", "Shipping") # Goal is to copy Microsoft.NET.Sdk.WebAssembly.Pack*, Microsoft.NETCore.App.Ref*, either need to do the shipping folder or glob
copy_directory_contents(src_dir_built_nugets, dir_bin_wasm)
- # browser folder was extracted from wasm folder here: https://github.com/dotnet/runtime/pull/95940, so we need to check both locations for which to use (Dec, 2023)
- src_file_test_main = glob.glob(os.path.join(repo_path, "src", "mono", "*", "test-main.js"))[0]
- dest_dir_wasm_data = os.path.join(dir_bin_wasm, "wasm-data")
- dest_file_test_main = os.path.join(dest_dir_wasm_data, "test-main.js")
- if not os.path.exists(dest_dir_wasm_data):
- os.makedirs(dest_dir_wasm_data)
- shutil.copy2(src_file_test_main, dest_file_test_main)
-
# Store the artifact in the artifact storage path
shutil.rmtree(artifact_wasm_wasm, ignore_errors=True)
copy_directory_contents(dir_bin_wasm, artifact_wasm_wasm)
@@ -402,7 +394,6 @@ def generate_single_benchmark_ci_args(parsed_args: Namespace, specific_run_type:
'--anyCategories', 'Libraries', 'Runtime',
'--category-exclusion-filter', 'NoInterpreter', 'NoWASM', 'NoMono',
'--cli', os.path.join(get_run_artifact_path(parsed_args, RunType.WasmInterpreter, commit), "wasm_bundle", "dotnet", "dotnet"),
- '--wasmDataDir', os.path.join(get_run_artifact_path(parsed_args, RunType.WasmInterpreter, commit), "wasm_bundle", "wasm-data"),
'--wasmEngine', parsed_args.wasm_engine_path,
'--wasmArgs', '\" --expose_wasm --module\"',
'--logBuildOutput',
@@ -418,7 +409,6 @@ def generate_single_benchmark_ci_args(parsed_args: Namespace, specific_run_type:
'--anyCategories', 'Libraries', 'Runtime',
'--category-exclusion-filter', 'NoInterpreter', 'NoWASM', 'NoMono',
'--cli', os.path.join(get_run_artifact_path(parsed_args, RunType.WasmAOT, commit), "wasm_bundle", "dotnet", "dotnet"),
- '--wasmDataDir', os.path.join(get_run_artifact_path(parsed_args, RunType.WasmAOT, commit), "wasm_bundle", "wasm-data"),
'--wasmEngine', parsed_args.wasm_engine_path,
'--wasmArgs', '\" --expose_wasm --module\"',
'--aotcompilermode', 'wasm',
diff --git a/scripts/build_runtime_payload.py b/scripts/build_runtime_payload.py
index 1217d6deb57..a7218bca7da 100644
--- a/scripts/build_runtime_payload.py
+++ b/scripts/build_runtime_payload.py
@@ -20,6 +20,7 @@
"build_mono_payload",
"build_monoaot_payload",
"build_wasm_payload",
+ "build_wasm_coreclr_payload",
]
@@ -299,27 +300,15 @@ def build_monoaot_payload(
def build_wasm_payload(
browser_wasm_archive_or_dir: str,
- payload_parent_dir: str, # wasm creates three payload directories
- test_main_js_path: Optional[str] = None,
- runtime_repo_dir: Optional[str] = None,
+ payload_parent_dir: str, # wasm creates two payload directories
) -> None:
- """Create the WASM payload directories (dotnet, built-nugets, wasm-data).
+ """Create the WASM payload directories (dotnet, built-nugets).
The archive/directory layout is expected to contain a `staging/` folder with
- `dotnet-latest` and `built-nugets` subfolders. We also copy the harness
- `test-main.js` into `wasm-data/`.
+ `dotnet-latest` and `built-nugets` subfolders.
"""
- if test_main_js_path is None:
- if runtime_repo_dir is None:
- raise Exception("Please provide a path to the test-main.js or runtime repository")
- test_main_js_path = os.path.join(runtime_repo_dir, "src", "mono", "browser", "test-main.js")
-
- if not os.path.exists(test_main_js_path):
- raise Exception(f"test-main.js not found in expected location: {test_main_js_path}")
-
wasm_dotnet_dir = os.path.join(payload_parent_dir, "dotnet")
wasm_built_nugets_dir = os.path.join(payload_parent_dir, "built-nugets")
- wasm_data_dir = os.path.join(payload_parent_dir, "wasm-data")
extract_archive_or_copy(
browser_wasm_archive_or_dir, wasm_dotnet_dir, prefix="staging/dotnet-latest/"
@@ -329,7 +318,57 @@ def build_wasm_payload(
browser_wasm_archive_or_dir, wasm_built_nugets_dir, prefix="staging/built-nugets/"
)
- os.makedirs(wasm_data_dir, exist_ok=True)
- shutil.copy(test_main_js_path, os.path.join(wasm_data_dir, "test-main.js"))
+ _set_permissions_recursive([wasm_dotnet_dir, wasm_built_nugets_dir], mode=0o664) # rw-rw-r--
+
- _set_permissions_recursive([wasm_dotnet_dir, wasm_built_nugets_dir, wasm_data_dir], mode=0o664) # rw-rw-r--
\ No newline at end of file
+def build_wasm_coreclr_payload(
+ browser_wasm_coreclr_archive_or_dir: str,
+ payload_parent_dir: str,
+) -> None:
+ """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.
+ """
+
+ wasm_dotnet_dir = os.path.join(payload_parent_dir, "dotnet")
+ wasm_built_nugets_dir = os.path.join(payload_parent_dir, "built-nugets")
+
+ # Extract the SDK from dotnet-none
+ extract_archive_or_copy(
+ browser_wasm_coreclr_archive_or_dir, wasm_dotnet_dir, prefix="staging/dotnet-none/"
+ )
+
+ # Extract built NuGet packages (WebAssembly SDK pack, ref pack)
+ extract_archive_or_copy(
+ browser_wasm_coreclr_archive_or_dir, wasm_built_nugets_dir, prefix="staging/built-nugets/"
+ )
+
+ # Determine version from the runtime pack directory structure
+ runtime_pack_src = os.path.join(
+ browser_wasm_coreclr_archive_or_dir if os.path.isdir(browser_wasm_coreclr_archive_or_dir) else "",
+ "staging", "microsoft.netcore.app.runtime.browser-wasm", "Release"
+ )
+ if os.path.isdir(runtime_pack_src):
+ # Get version from NuGet.config or infer from directory
+ # For now, read version from the SDK's Microsoft.NETCore.App.Ref pack
+ ref_pack_parent = os.path.join(wasm_dotnet_dir, "packs", "Microsoft.NETCore.App.Ref")
+ if os.path.isdir(ref_pack_parent):
+ versions = os.listdir(ref_pack_parent)
+ if versions:
+ pack_version = versions[0]
+ coreclr_pack_dest = os.path.join(
+ wasm_dotnet_dir, "packs", "Microsoft.NETCore.App.Runtime.browser-wasm", pack_version
+ )
+ extract_archive_or_copy(
+ browser_wasm_coreclr_archive_or_dir,
+ coreclr_pack_dest,
+ prefix="staging/microsoft.netcore.app.runtime.browser-wasm/Release/",
+ )
+ getLogger().info("Installed CoreCLR browser-wasm runtime pack version %s", pack_version)
+ else:
+ getLogger().warning("Microsoft.NETCore.App.Ref pack not found – cannot determine version")
+
+ _set_permissions_recursive([wasm_dotnet_dir, wasm_built_nugets_dir], mode=0o664)
\ No newline at end of file
diff --git a/scripts/micro_benchmarks.py b/scripts/micro_benchmarks.py
index a92fde9261e..8dee94e11cd 100755
--- a/scripts/micro_benchmarks.py
+++ b/scripts/micro_benchmarks.py
@@ -140,6 +140,15 @@ def __get_bdn_arguments(user_input: str) -> list[str]:
help='Tests should be run with the wasm runtime'
)
+ parser.add_argument(
+ '--wasm-coreclr',
+ dest='wasm_coreclr',
+ required=False,
+ default=False,
+ action='store_true',
+ help='Use CoreCLR runtime pack instead of Mono for WASM benchmarks'
+ )
+
parser.add_argument(
'--bdn-arguments',
dest='bdn_arguments',
@@ -273,6 +282,8 @@ def __get_benchmarkdotnet_arguments(framework: str, args: Any) -> list[str]:
run_args += ['--runtimes', 'wasmnet11_0']
else:
raise ArgumentTypeError('Framework {} is not supported for wasm'.format(framework))
+ if args.wasm_coreclr:
+ run_args += ['--wasmCoreCLR']
# Increase default 2 min build timeout to accommodate slow (or even very slow) hardware
if not args.bdn_arguments or '--buildTimeout' not in args.bdn_arguments:
diff --git a/scripts/run_performance_job.py b/scripts/run_performance_job.py
index 3fad795a973..d548b26765d 100644
--- a/scripts/run_performance_job.py
+++ b/scripts/run_performance_job.py
@@ -200,8 +200,8 @@ def get_pre_commands(
"sudo apt -y install curl dirmngr apt-transport-https lsb-release ca-certificates"
]
- # Set up everything needed for WASM runs
- if runtime_type == "wasm":
+ # Set up everything needed for WASM runs (both Mono and CoreCLR)
+ if runtime_type in ("wasm", "wasm_coreclr"):
if os_distro == "azurelinux":
# Azure Linux uses tdnf package manager
install_prerequisites += [
@@ -442,7 +442,6 @@ def get_bdn_arguments(
"--wasmEngine", javascript_engine_path,
f"\\\"--wasmArgs={' '.join(wasm_args)}\\\"",
"--cli", "$HELIX_CORRELATION_PAYLOAD/dotnet/dotnet",
- "--wasmDataDir", "$HELIX_CORRELATION_PAYLOAD/wasm-data"
]
if is_aot:
@@ -451,6 +450,21 @@ def get_bdn_arguments(
"--buildTimeout", "3600"
]
+ if runtime_type == "wasm_coreclr":
+ category_exclusions += ["NoWASM", "NoMono"]
+
+ wasm_args = ["--expose_wasm"]
+ if javascript_engine == "v8":
+ wasm_args += ["--module"]
+
+ assert javascript_engine_path is not None
+ bdn_arguments += [
+ "--wasmEngine", javascript_engine_path,
+ f"\\\"--wasmArgs={' '.join(wasm_args)}\\\"",
+ "--cli", "$HELIX_CORRELATION_PAYLOAD/dotnet/dotnet",
+ "--buildTimeout", "1200"
+ ]
+
if category_exclusions:
bdn_arguments += ["--category-exclusion-filter", *set(category_exclusions)]
@@ -560,7 +574,7 @@ 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):
+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):
if os_group == "windows":
work_item_command = [
"python",
@@ -588,6 +602,8 @@ def get_work_item_command(os_group: str, target_csproj: str, architecture: str,
if wasm:
work_item_command += ["--run-isolated", "--wasm", "--dotnet-path", "$HELIX_CORRELATION_PAYLOAD/dotnet/"]
+ if wasm_coreclr:
+ work_item_command += ["--wasm-coreclr"]
work_item_command += ["--bdn-artifacts", bdn_artifacts_dir]
@@ -646,8 +662,9 @@ def run_performance_job(args: RunPerformanceJobArgs):
is_mono = args.runtime_type == "mono"
mono_aot = is_mono and is_aot
mono_dotnet = is_mono and not is_aot
- wasm = args.runtime_type == "wasm"
- wasm_aot = wasm and is_aot
+ wasm_coreclr = args.runtime_type == "wasm_coreclr"
+ wasm = args.runtime_type == "wasm" or wasm_coreclr # wasm_coreclr also uses wasm infrastructure
+ wasm_aot = wasm and is_aot and not wasm_coreclr
working_dir = os.path.join(args.performance_repo_dir, "CorrelationStaging") # folder in which the payload and workitem directories will be made
work_item_dir = os.path.join(working_dir, "workitem", "") # Folder in which the work item commands will be run in
@@ -779,14 +796,29 @@ def run_performance_job(args: RunPerformanceJobArgs):
shutil.copytree(args.mono_dotnet_dir, mono_dotnet_path, dirs_exist_ok=True)
v8_version = ""
- if wasm:
+ 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(
+ browser_wasm_coreclr_dir,
+ payload_dir,
+ )
+
+ elif wasm:
if args.libraries_download_dir is None:
raise Exception("Libraries not downloaded for wasm runs")
getLogger().info("Copying wasm bundle directory to payload directory")
browser_wasm_dir = os.path.join(args.libraries_download_dir, "BrowserWasm")
- build_wasm_payload(browser_wasm_dir, payload_dir, runtime_repo_dir=args.runtime_repo_dir)
+ build_wasm_payload(
+ browser_wasm_dir,
+ payload_dir,
+ )
+ if wasm:
if args.javascript_engine == "v8":
if args.browser_versions_props_path is None:
if args.runtime_repo_dir is None:
@@ -802,7 +834,7 @@ def run_performance_job(args: RunPerformanceJobArgs):
break
else:
raise Exception("Unable to find v8 version in BrowserVersions.props")
-
+
if args.javascript_engine_path is None:
args.javascript_engine_path = f"/home/helixbot/.jsvu/bin/v8-{v8_version}"
@@ -961,6 +993,11 @@ def run_performance_job(args: RunPerformanceJobArgs):
ci_setup_arguments.output_file = os.path.join(root_payload_dir, "machine-setup")
if args.is_scenario:
ci_setup_arguments.install_dir = os.path.join(payload_dir, "dotnet")
+ elif wasm_coreclr:
+ # For wasm_coreclr, we already have the SDK in the payload - skip downloading
+ wasm_dotnet_path = os.path.join(payload_dir, "dotnet")
+ ci_setup_arguments.dotnet_path = wasm_dotnet_path
+ ci_setup_arguments.install_dir = wasm_dotnet_path
else:
tools_dir = os.path.join(performance_payload_dir, "tools")
ci_setup_arguments.install_dir = os.path.join(tools_dir, "dotnet", args.architecture)
@@ -1157,7 +1194,7 @@ 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)
+ return get_work_item_command(args.os_group, args.target_csproj, args.architecture, perf_lab_framework, args.internal, wasm, artifact_dir, wasm_coreclr)
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)
diff --git a/src/benchmarks/micro/MicroBenchmarks.Wasm.props b/src/benchmarks/micro/MicroBenchmarks.Wasm.props
index bffdc16baed..d5730feae77 100644
--- a/src/benchmarks/micro/MicroBenchmarks.Wasm.props
+++ b/src/benchmarks/micro/MicroBenchmarks.Wasm.props
@@ -1,7 +1,5 @@
- true
- $(WasmDataDir)\test-main.js
partial
true
true
diff --git a/src/benchmarks/micro/MicroBenchmarks.Wasm.targets b/src/benchmarks/micro/MicroBenchmarks.Wasm.targets
index cf9978d836f..6489b14732f 100644
--- a/src/benchmarks/micro/MicroBenchmarks.Wasm.targets
+++ b/src/benchmarks/micro/MicroBenchmarks.Wasm.targets
@@ -8,7 +8,6 @@
it can get into a situation where the emsdk version doesn't match
between runtime, and the workload pack. -->
<_WasmStrictVersionMatch>false
- true
@@ -25,9 +24,5 @@
-
-
-
-