未验证 提交 dddf0d0f 编写于 作者: P Pavel Savara 提交者: GitHub

backport 90388 to Net8 (#91201)

上级 776a8451
......@@ -13,10 +13,7 @@
<link rel="preload" href="./blazor.boot.json" as="fetch" crossorigin="use-credentials">
<link rel="prefetch" href="./dotnet.native.js" as="fetch" crossorigin="anonymous">
<link rel="prefetch" href="./dotnet.runtime.js" as="fetch" crossorigin="anonymous">
<link rel="prefetch" href="./dotnet.native.wasm" as="fetch" crossorigin="anonymous">
<!-- users should consider if they optimize for the first load or subsequent load from memory snapshot -->
<link rel="prefetch" href="./icudt.dat" as="fetch" crossorigin="anonymous">
<link rel="prefetch" href="./System.Private.CoreLib.wasm" as="fetch" crossorigin="anonymous">
<link rel="prefetch" href="./advanced-sample.lib.module.js" as="fetch" crossorigin="anonymous">
</head>
<body>
......
......@@ -31,6 +31,7 @@ try {
// here we show how emscripten could be further configured
// It is preferred to use specific 'with***' methods instead in all other cases.
.withConfig({
startupMemoryCache: true,
resources: {
modulesAfterConfigLoaded: {
"advanced-sample.lib.module.js": ""
......
......@@ -12,9 +12,6 @@
<link rel="preload" href="./_framework/blazor.boot.json" as="fetch" crossorigin="use-credentials">
<link rel="prefetch" href="./_framework/dotnet.native.js" as="fetch" crossorigin="anonymous">
<link rel="prefetch" href="./_framework/dotnet.runtime.js" as="fetch" crossorigin="anonymous">
<link rel="prefetch" href="./_framework/dotnet.native.wasm" as="fetch" crossorigin="anonymous">
<!-- users should consider if they optimize for the first load or subsequent load from memory snapshot -->
<link rel="prefetch" href="./_framework/System.Private.CoreLib.dll" as="fetch" crossorigin="anonymous">
</head>
<body>
......
......@@ -31,6 +31,10 @@ try {
}
const runtime = await dotnet
.withConfig({
maxParallelDownloads: 10000,
// diagnosticTracing:true,
})
.withModuleConfig({
printErr: () => undefined,
print: () => undefined,
......@@ -38,7 +42,6 @@ try {
if (window.parent != window) {
window.parent.resolveAppStartEvent("onConfigLoaded");
}
// config.diagnosticTracing = true;
}
})
.create();
......
......@@ -193,12 +193,13 @@ See also [fetch integrity on MDN](https://developer.mozilla.org/en-US/docs/Web/A
### Pre-fetching
In order to start downloading application resources as soon as possible you can add HTML elements to `<head>` of your page similar to:
Adding too many files into prefetch could be counterproductive.
Please benchmark your startup performance on real target devices and with realistic network conditions.
```html
<link rel="preload" href="./_framework/blazor.boot.json" as="fetch" crossorigin="use-credentials">
<link rel="prefetch" href="./_framework/dotnet.native.js" as="fetch" crossorigin="anonymous">
<link rel="prefetch" href="./_framework/dotnet.runtime.js" as="fetch" crossorigin="anonymous">
<link rel="prefetch" href="./_framework/dotnet.native.wasm" as="fetch" crossorigin="anonymous">
```
See also [link rel prefetch on MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/prefetch)
......
......@@ -8,7 +8,7 @@ import type { RuntimeAPI } from "./types";
import { Module, linkerDisableLegacyJsInterop, exportedRuntimeAPI, passEmscriptenInternals, runtimeHelpers, setRuntimeGlobals, } from "./globals";
import { GlobalObjects, is_nullish } from "./types/internal";
import { configureEmscriptenStartup, configureWorkerStartup } from "./startup";
import { configureEmscriptenStartup, configureRuntimeStartup, configureWorkerStartup } from "./startup";
import { create_weak_ref } from "./weak-ref";
import { export_internal } from "./exports-internal";
......@@ -143,5 +143,5 @@ class RuntimeList {
// export external API
export {
passEmscriptenInternals, initializeExports, initializeReplacements, configureEmscriptenStartup, configureWorkerStartup, setRuntimeGlobals
passEmscriptenInternals, initializeExports, initializeReplacements, configureRuntimeStartup, configureEmscriptenStartup, configureWorkerStartup, setRuntimeGlobals
};
\ No newline at end of file
......@@ -59,7 +59,6 @@ export function setRuntimeGlobals(globalObjects: GlobalObjects) {
gitHash,
allAssetsInMemory: createPromiseController<void>(),
dotnetReady: createPromiseController<any>(),
memorySnapshotSkippedOrDone: createPromiseController<void>(),
afterInstantiateWasm: createPromiseController<void>(),
beforePreInit: createPromiseController<void>(),
afterPreInit: createPromiseController<void>(),
......
......@@ -78,8 +78,6 @@ const containedInSnapshotByAssetTypes: {
"pdb": true,
"heap": true,
"icu": true,
...jsModulesAssetTypes,
"dotnetwasm": true,
};
// these assets are instantiated differently than the main flow
......@@ -95,7 +93,7 @@ export function shouldLoadIcuAsset(asset: AssetEntryInternal): boolean {
return !(asset.behavior == "icu" && asset.name != loaderHelpers.preferredIcuAsset);
}
function convert_single_asset(modulesAssets: AssetEntryInternal[], resource: ResourceList | undefined, behavior: SingleAssetBehaviors): AssetEntryInternal {
function convert_single_asset(assetsCollection: AssetEntryInternal[], resource: ResourceList | undefined, behavior: SingleAssetBehaviors): AssetEntryInternal {
const keys = Object.keys(resource || {});
mono_assert(keys.length == 1, `Expect to have one ${behavior} asset in resources`);
......@@ -110,7 +108,7 @@ function convert_single_asset(modulesAssets: AssetEntryInternal[], resource: Res
set_single_asset(asset);
// so that we can use it on the worker too
modulesAssets.push(asset);
assetsCollection.push(asset);
return asset;
}
......@@ -168,15 +166,12 @@ export async function mono_download_assets(): Promise<void> {
countAndStartDownload(asset);
}
// continue after the dotnet.runtime.js was loaded
await loaderHelpers.runtimeModuleLoaded.promise;
// continue after we know if memory snapshot is available or not
await runtimeHelpers.memorySnapshotSkippedOrDone.promise;
await loaderHelpers.memorySnapshotSkippedOrDone.promise;
// start fetching assets in parallel, only if memory snapshot is not available.
for (const asset of containedInSnapshotAssets) {
if (!runtimeHelpers.loadedMemorySnapshot) {
if (!runtimeHelpers.loadedMemorySnapshotSize) {
countAndStartDownload(asset);
} else {
// Otherwise cleanup in case we were given pending download. It would be even better if we could abort the download.
......@@ -193,6 +188,8 @@ export async function mono_download_assets(): Promise<void> {
}
loaderHelpers.allDownloadsQueued.promise_control.resolve();
// continue after the dotnet.runtime.js was loaded
await loaderHelpers.runtimeModuleLoaded.promise;
const promises_of_asset_instantiation: Promise<void>[] = [];
......@@ -211,7 +208,6 @@ export async function mono_download_assets(): Promise<void> {
// wait till after onRuntimeInitialized and after memory snapshot is loaded or skipped
await runtimeHelpers.beforeOnRuntimeInitialized.promise;
await runtimeHelpers.memorySnapshotSkippedOrDone.promise;
runtimeHelpers.instantiate_asset(asset, url, data);
}
} else {
......@@ -284,7 +280,7 @@ export function prepareAssets() {
mono_assert(resources.jsModuleNative, "resources.jsModuleNative must be defined");
mono_assert(resources.jsModuleRuntime, "resources.jsModuleRuntime must be defined");
mono_assert(!MonoWasmThreads || resources.jsModuleWorker, "resources.jsModuleWorker must be defined");
convert_single_asset(modulesAssets, resources.wasmNative, "dotnetwasm");
convert_single_asset(alwaysLoadedAssets, resources.wasmNative, "dotnetwasm");
convert_single_asset(modulesAssets, resources.jsModuleNative, "js-module-native");
convert_single_asset(modulesAssets, resources.jsModuleRuntime, "js-module-runtime");
if (MonoWasmThreads) {
......
......@@ -10,6 +10,7 @@ import { importLibraryInitializers, invokeLibraryInitializers } from "./libraryI
import { mono_exit } from "./exit";
import { makeURLAbsoluteWithApplicationBase } from "./polyfills";
import { appendUniqueQuery } from "./assets";
import { mono_assert } from "./globals";
export function deep_merge_config(target: MonoConfigInternal, source: MonoConfigInternal): MonoConfigInternal {
// no need to merge the same object
......@@ -220,15 +221,12 @@ export async function mono_wasm_load_config(module: DotnetModuleInternal): Promi
await loaderHelpers.afterConfigLoaded.promise;
return;
}
configLoaded = true;
if (!configFilePath) {
normalizeConfig();
loaderHelpers.afterConfigLoaded.promise_control.resolve(loaderHelpers.config);
return;
}
mono_log_debug("mono_wasm_load_config");
try {
await loadBootConfig(module);
configLoaded = true;
if (configFilePath) {
mono_log_debug("mono_wasm_load_config");
await loadBootConfig(module);
}
normalizeConfig();
......@@ -249,7 +247,12 @@ export async function mono_wasm_load_config(module: DotnetModuleInternal): Promi
normalizeConfig();
mono_assert(!loaderHelpers.config.startupMemoryCache || !module.instantiateWasm, "startupMemoryCache is not supported with Module.instantiateWasm");
loaderHelpers.afterConfigLoaded.promise_control.resolve(loaderHelpers.config);
if (!loaderHelpers.config.startupMemoryCache) {
loaderHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
}
} catch (err) {
const errMessage = `Failed to load config file ${configFilePath} ${err} ${(err as Error)?.stack}`;
loaderHelpers.config = module.config = Object.assign(loaderHelpers.config, { message: errMessage, error: err, isError: true });
......
......@@ -122,9 +122,9 @@ function abort_promises(reason: any) {
loaderHelpers.afterConfigLoaded.promise_control.reject(reason);
loaderHelpers.wasmDownloadPromise.promise_control.reject(reason);
loaderHelpers.runtimeModuleLoaded.promise_control.reject(reason);
loaderHelpers.memorySnapshotSkippedOrDone.promise_control.reject(reason);
if (runtimeHelpers.dotnetReady) {
runtimeHelpers.dotnetReady.promise_control.reject(reason);
runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.reject(reason);
runtimeHelpers.afterInstantiateWasm.promise_control.reject(reason);
runtimeHelpers.beforePreInit.promise_control.reject(reason);
runtimeHelpers.afterPreInit.promise_control.reject(reason);
......
......@@ -87,6 +87,7 @@ export function setLoaderGlobals(
allDownloadsQueued: createPromiseController<void>(),
wasmDownloadPromise: createPromiseController<AssetEntryInternal>(),
runtimeModuleLoaded: createPromiseController<void>(),
memorySnapshotSkippedOrDone: createPromiseController<void>(),
is_exited,
is_runtime_running,
......
......@@ -454,10 +454,11 @@ function importModules() {
}
async function initializeModules(es6Modules: [RuntimeModuleExportsInternal, NativeModuleExportsInternal]) {
const { initializeExports, initializeReplacements, configureEmscriptenStartup, configureWorkerStartup, setRuntimeGlobals, passEmscriptenInternals } = es6Modules[0];
const { initializeExports, initializeReplacements, configureRuntimeStartup, configureEmscriptenStartup, configureWorkerStartup, setRuntimeGlobals, passEmscriptenInternals } = es6Modules[0];
const { default: emscriptenFactory } = es6Modules[1];
setRuntimeGlobals(globalObjectsRoot);
initializeExports(globalObjectsRoot);
await configureRuntimeStartup();
loaderHelpers.runtimeModuleLoaded.promise_control.resolve();
emscriptenFactory((originalModule: EmscriptenModuleInternal) => {
......@@ -494,9 +495,8 @@ async function createEmscriptenMain(): Promise<RuntimeAPI> {
mono_exit(1, err);
});
init_globalization();
setTimeout(() => {
init_globalization();
mono_download_assets(); // intentionally not awaited
}, 0);
......
......@@ -44,22 +44,35 @@ async function openCache(): Promise<Cache | null> {
}
}
export async function getMemorySnapshotSize(): Promise<number | undefined> {
export async function checkMemorySnapshotSize(): Promise<void> {
try {
if (!runtimeHelpers.config.startupMemoryCache) {
// we could start downloading DLLs because snapshot is disabled
return;
}
const cacheKey = await getCacheKey();
if (!cacheKey) {
return undefined;
return;
}
const cache = await openCache();
if (!cache) {
return undefined;
return;
}
const res = await cache.match(cacheKey);
const contentLength = res?.headers.get("content-length");
return contentLength ? parseInt(contentLength) : undefined;
const memorySize = contentLength ? parseInt(contentLength) : undefined;
runtimeHelpers.loadedMemorySnapshotSize = memorySize;
runtimeHelpers.storeMemorySnapshotPending = !memorySize;
} catch (ex) {
mono_log_warn("Failed find memory snapshot in the cache", ex);
return undefined;
}
finally {
if (!runtimeHelpers.loadedMemorySnapshotSize) {
// we could start downloading DLLs because there is no snapshot yet
loaderHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
}
}
}
......
......@@ -21,7 +21,7 @@ import { instantiate_wasm_asset, wait_for_all_assets } from "./assets";
import { mono_wasm_init_diagnostics } from "./diagnostics";
import { replace_linker_placeholders } from "./exports-binding";
import { endMeasure, MeasuredBlock, startMeasure } from "./profiler";
import { getMemorySnapshot, storeMemorySnapshot, getMemorySnapshotSize } from "./snapshot";
import { checkMemorySnapshotSize, getMemorySnapshot, storeMemorySnapshot } from "./snapshot";
import { mono_log_debug, mono_log_error, mono_log_warn, mono_set_thread_id } from "./logging";
// threads
......@@ -39,6 +39,19 @@ import { assertNoProxies } from "./gc-handles";
// default size if MonoConfig.pthreadPoolSize is undefined
const MONO_PTHREAD_POOL_SIZE = 4;
export async function configureRuntimeStartup(): Promise<void> {
if (linkerWasmEnableSIMD) {
mono_assert(await loaderHelpers.simd(), "This browser/engine doesn't support WASM SIMD. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
}
if (linkerWasmEnableEH) {
mono_assert(await loaderHelpers.exceptions(), "This browser/engine doesn't support WASM exception handling. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
}
await init_polyfills_async();
await checkMemorySnapshotSize();
}
// we are making emscripten startup async friendly
// emscripten is executing the events without awaiting it and so we need to block progress via PromiseControllers above
export function configureEmscriptenStartup(module: DotnetModuleInternal): void {
......@@ -117,8 +130,6 @@ function instantiateWasm(
const mark = startMeasure();
if (userInstantiateWasm) {
// user wasm instantiation doesn't support memory snapshots
runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
const exports = userInstantiateWasm(imports, (instance: WebAssembly.Instance, module: WebAssembly.Module | undefined) => {
endMeasure(mark, MeasuredBlock.instantiateWasm);
runtimeHelpers.afterInstantiateWasm.promise_control.resolve();
......@@ -375,14 +386,6 @@ async function mono_wasm_pre_init_essential_async(): Promise<void> {
mono_log_debug("mono_wasm_pre_init_essential_async");
Module.addRunDependency("mono_wasm_pre_init_essential_async");
if (linkerWasmEnableSIMD) {
mono_assert(await loaderHelpers.simd(), "This browser/engine doesn't support WASM SIMD. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
}
if (linkerWasmEnableEH) {
mono_assert(await loaderHelpers.exceptions(), "This browser/engine doesn't support WASM exception handling. Please use a modern version. See also https://aka.ms/dotnet-wasm-features");
}
await init_polyfills_async();
if (MonoWasmThreads) {
preAllocatePThreadWorkerPool(MONO_PTHREAD_POOL_SIZE, runtimeHelpers.config);
......@@ -457,20 +460,9 @@ async function instantiate_wasm_module(
): Promise<void> {
// this is called so early that even Module exports like addRunDependency don't exist yet
try {
let memorySize: number | undefined = undefined;
await loaderHelpers.afterConfigLoaded;
mono_log_debug("instantiate_wasm_module");
if (runtimeHelpers.config.startupMemoryCache) {
memorySize = await getMemorySnapshotSize();
runtimeHelpers.loadedMemorySnapshot = !!memorySize;
runtimeHelpers.storeMemorySnapshotPending = !runtimeHelpers.loadedMemorySnapshot;
}
if (!runtimeHelpers.loadedMemorySnapshot) {
// we should start downloading DLLs etc as they are not in the snapshot
runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
}
await runtimeHelpers.beforePreInit.promise;
Module.addRunDependency("instantiate_wasm_module");
......@@ -484,19 +476,19 @@ async function instantiate_wasm_module(
mono_log_debug("instantiate_wasm_module done");
if (runtimeHelpers.loadedMemorySnapshot) {
if (runtimeHelpers.loadedMemorySnapshotSize) {
try {
const wasmMemory = (Module.asm?.memory || Module.wasmMemory)!;
// .grow() takes a delta compared to the previous size
wasmMemory.grow((memorySize! - wasmMemory.buffer.byteLength + 65535) >>> 16);
wasmMemory.grow((runtimeHelpers.loadedMemorySnapshotSize! - wasmMemory.buffer.byteLength + 65535) >>> 16);
runtimeHelpers.updateMemoryViews();
} catch (err) {
mono_log_warn("failed to resize memory for the snapshot", err);
runtimeHelpers.loadedMemorySnapshot = false;
runtimeHelpers.loadedMemorySnapshotSize = undefined;
}
// now we know if the loading of memory succeeded or not, we can start loading the rest of the assets
runtimeHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
loaderHelpers.memorySnapshotSkippedOrDone.promise_control.resolve();
}
runtimeHelpers.afterInstantiateWasm.promise_control.resolve();
} catch (err) {
......@@ -509,7 +501,7 @@ async function instantiate_wasm_module(
async function mono_wasm_before_memory_snapshot() {
const mark = startMeasure();
if (runtimeHelpers.loadedMemorySnapshot) {
if (runtimeHelpers.loadedMemorySnapshotSize) {
// get the bytes after we re-sized the memory, so that we don't have too much memory in use at the same time
const memoryBytes = await getMemorySnapshot();
const heapU8 = localHeapViewU8();
......
......@@ -128,6 +128,7 @@ export type LoaderHelpers = {
allDownloadsQueued: PromiseAndController<void>,
wasmDownloadPromise: PromiseAndController<AssetEntryInternal>,
runtimeModuleLoaded: PromiseAndController<void>,
memorySnapshotSkippedOrDone: PromiseAndController<void>,
is_exited: () => boolean,
is_runtime_running: () => boolean,
......@@ -176,7 +177,7 @@ export type RuntimeHelpers = {
mono_wasm_runtime_is_ready: boolean;
mono_wasm_bindings_is_ready: boolean;
loadedMemorySnapshot: boolean,
loadedMemorySnapshotSize?: number,
enablePerfMeasure: boolean;
waitForDebugger?: number;
ExitStatus: ExitStatusError;
......@@ -194,7 +195,6 @@ export type RuntimeHelpers = {
allAssetsInMemory: PromiseAndController<void>,
dotnetReady: PromiseAndController<any>,
memorySnapshotSkippedOrDone: PromiseAndController<void>,
afterInstantiateWasm: PromiseAndController<void>,
beforePreInit: PromiseAndController<void>,
afterPreInit: PromiseAndController<void>,
......@@ -490,6 +490,7 @@ export type setGlobalObjectsType = (globalObjects: GlobalObjects) => void;
export type initializeExportsType = (globalObjects: GlobalObjects) => RuntimeAPI;
export type initializeReplacementsType = (replacements: EmscriptenReplacements) => void;
export type configureEmscriptenStartupType = (module: DotnetModuleInternal) => void;
export type configureRuntimeStartupType = () => Promise<void>;
export type configureWorkerStartupType = (module: DotnetModuleInternal) => Promise<void>
......@@ -497,6 +498,7 @@ export type RuntimeModuleExportsInternal = {
setRuntimeGlobals: setGlobalObjectsType,
initializeExports: initializeExportsType,
initializeReplacements: initializeReplacementsType,
configureRuntimeStartup: configureRuntimeStartupType,
configureEmscriptenStartup: configureEmscriptenStartupType,
configureWorkerStartup: configureWorkerStartupType,
passEmscriptenInternals: passEmscriptenInternalsType,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册