Skip to content
Open
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
22 changes: 22 additions & 0 deletions eng/pipelines/runtime-wasm-perf-jobs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
21 changes: 20 additions & 1 deletion eng/pipelines/templates/runtime-perf-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
63 changes: 62 additions & 1 deletion scripts/build_runtime_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"build_mono_payload",
"build_monoaot_payload",
"build_wasm_payload",
"build_wasm_coreclr_payload",
]


Expand Down Expand Up @@ -332,4 +333,64 @@ def build_wasm_payload(
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, wasm_data_dir], mode=0o664) # rw-rw-r--
_set_permissions_recursive([wasm_dotnet_dir, wasm_built_nugets_dir, wasm_data_dir], mode=0o664) # rw-rw-r--


def build_wasm_coreclr_payload(
browser_wasm_coreclr_archive_or_dir: str,
payload_parent_dir: str,
test_main_js_path: Optional[str] = None,
runtime_repo_dir: Optional[str] = None,
) -> None:
"""Create a WASM CoreCLR-only payload (dotnet, wasm-data).

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.
"""
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_data_dir = os.path.join(payload_parent_dir, "wasm-data")

# Extract the SDK from dotnet-none
extract_archive_or_copy(
browser_wasm_coreclr_archive_or_dir, wasm_dotnet_dir, prefix="staging/dotnet-none/"
)

# 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")

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_data_dir], mode=0o664)
11 changes: 11 additions & 0 deletions scripts/micro_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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:
Expand Down
71 changes: 62 additions & 9 deletions scripts/run_performance_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 += [
Expand Down Expand Up @@ -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:
Expand All @@ -451,6 +450,22 @@ 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",
"--wasmCoreCLR",
"--buildTimeout", "1200"
]

if category_exclusions:
bdn_arguments += ["--category-exclusion-filter", *set(category_exclusions)]

Expand Down Expand Up @@ -560,7 +575,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",
Expand Down Expand Up @@ -588,6 +603,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]

Expand Down Expand Up @@ -646,8 +663,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
Expand Down Expand Up @@ -779,13 +797,43 @@ 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,
runtime_repo_dir=args.runtime_repo_dir,
)

if args.javascript_engine == "v8":
if args.browser_versions_props_path is None:
if args.runtime_repo_dir is None:
raise Exception("BrowserVersions.props must be present for wasm runs")
args.browser_versions_props_path = os.path.join(args.runtime_repo_dir, "eng", "testing", "BrowserVersions.props")

with open(args.browser_versions_props_path) as f:
for line in f:
match = re.search(r"linux_V8Version>([^<]*)<", line)
if match:
v8_version = match.group(1)
v8_version = ".".join(v8_version.split(".")[:3])
break

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,
runtime_repo_dir=args.runtime_repo_dir,
)

if args.javascript_engine == "v8":
if args.browser_versions_props_path is None:
Expand Down Expand Up @@ -961,6 +1009,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)
Expand Down Expand Up @@ -1157,7 +1210,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)
Expand Down
2 changes: 0 additions & 2 deletions src/benchmarks/micro/MicroBenchmarks.Wasm.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<Project>
<PropertyGroup>
<WasmGenerateRunV8Script>true</WasmGenerateRunV8Script>
<WasmMainJSPath>$(WasmDataDir)\test-main.js</WasmMainJSPath>
<TrimMode>partial</TrimMode>
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
<WasmBuildNative>true</WasmBuildNative>
Expand Down
5 changes: 0 additions & 5 deletions src/benchmarks/micro/MicroBenchmarks.Wasm.targets
Original file line number Diff line number Diff line change
Expand Up @@ -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</_WasmStrictVersionMatch>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>

<ItemGroup>
Expand All @@ -25,9 +24,5 @@
</ItemGroup>
</Target>

<Target Name="Validate" BeforeTargets="Build">
<Error Text="Cannot find WasmMainJSPath: $(WasmMainJSPath)" Condition="'$(WasmMainJSPath)' == '' or !Exists('$(WasmMainJSPath)')" />
</Target>

<Import Project="$(MSBuildThisFileDirectory)..\..\scenarios\build-common\WasmOverridePacks.targets" />
</Project>