-
Notifications
You must be signed in to change notification settings - Fork 5k
[wasm] Add AppStart task to the bench Sample #61481
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Text.Json; | ||
using System.Threading.Tasks; | ||
|
||
namespace Sample | ||
{ | ||
public class AppStartTask : BenchTask | ||
{ | ||
public override string Name => "AppStart"; | ||
public override bool BrowserOnly => true; | ||
|
||
[DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, "System.Runtime.InteropServices.JavaScript.Runtime", "System.Private.Runtime.InteropServices.JavaScript")] | ||
static Type jsRuntimeType = System.Type.GetType("System.Runtime.InteropServices.JavaScript.Runtime, System.Private.Runtime.InteropServices.JavaScript", true); | ||
static Type jsFunctionType = System.Type.GetType("System.Runtime.InteropServices.JavaScript.Function, System.Private.Runtime.InteropServices.JavaScript", true); | ||
[DynamicDependency("InvokeJS(System.String)", "System.Runtime.InteropServices.JavaScript.Runtime", "System.Private.Runtime.InteropServices.JavaScript")] | ||
static MethodInfo invokeJSMethod = jsRuntimeType.GetMethod("InvokeJS", new Type[] { typeof(string) }); | ||
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, "System.Runtime.InteropServices.JavaScript.Function", "System.Private.Runtime.InteropServices.JavaScript")] | ||
static ConstructorInfo functionConstructor = jsRuntimeType.GetConstructor(new Type[] { typeof(object[]) }); | ||
[DynamicDependency("Call()", "System.Runtime.InteropServices.JavaScript.Function", "System.Private.Runtime.InteropServices.JavaScript")] | ||
static MethodInfo functionCall = jsFunctionType.GetMethod("Call", BindingFlags.Instance | BindingFlags.Public, new Type[] { }); | ||
|
||
public AppStartTask() | ||
{ | ||
measurements = new Measurement[] { | ||
new PageShow(), | ||
new ReachManaged(), | ||
}; | ||
} | ||
|
||
Measurement[] measurements; | ||
public override Measurement[] Measurements => measurements; | ||
|
||
static string InvokeJS(string js) | ||
{ | ||
return (string)invokeJSMethod.Invoke(null, new object[] { js }); | ||
} | ||
|
||
class PageShow : BenchTask.Measurement | ||
{ | ||
public override string Name => "Page show"; | ||
|
||
public override int InitialSamples => 3; | ||
|
||
async Task RunAsyncStep() | ||
{ | ||
var function = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return App.StartAppUI();" } }); | ||
var task = (Task<object>)functionCall.Invoke(function, new object[] { }); | ||
|
||
await task; | ||
} | ||
|
||
public override bool HasRunStepAsync => true; | ||
|
||
public override async Task RunStepAsync() | ||
{ | ||
var function = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return App.PageShow();" } }); | ||
await (Task<object>)functionCall.Invoke(function, null); | ||
} | ||
} | ||
|
||
class ReachManaged : BenchTask.Measurement | ||
{ | ||
public override string Name => "Reach managed"; | ||
public override int InitialSamples => 3; | ||
public override bool HasRunStepAsync => true; | ||
|
||
static object jsUIReachedManagedFunction = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return App.ReachedManaged();" } }); | ||
static object jsReached = Activator.CreateInstance(jsFunctionType, new object[] { new object[] { @"return App.reached();" } }); | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
public static void Reached() | ||
{ | ||
functionCall.Invoke(jsReached, null); | ||
} | ||
|
||
public override async Task RunStepAsync() | ||
{ | ||
await (Task<object>)functionCall.Invoke(jsUIReachedManagedFunction, null); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
using System.Text.RegularExpressions; | ||
using System.Threading.Tasks; | ||
|
||
abstract class BenchTask | ||
public abstract class BenchTask | ||
{ | ||
public abstract string Name { get; } | ||
readonly List<Result> results = new(); | ||
|
@@ -18,7 +18,7 @@ public async Task<string> RunBatch(List<Result> results, int measurementIdx, int | |
{ | ||
var measurement = Measurements[measurementIdx]; | ||
await measurement.BeforeBatch(); | ||
var result = measurement.RunBatch(this, milliseconds); | ||
var result = await measurement.RunBatch(this, milliseconds); | ||
results.Add(result); | ||
await measurement.AfterBatch(); | ||
|
||
|
@@ -50,27 +50,37 @@ public abstract class Measurement | |
|
||
public virtual Task AfterBatch() { return Task.CompletedTask; } | ||
|
||
public abstract void RunStep(); | ||
public virtual void RunStep() { } | ||
public virtual async Task RunStepAsync() { await Task.CompletedTask; } | ||
|
||
public virtual bool HasRunStepAsync => false; | ||
|
||
protected virtual int CalculateSteps(int milliseconds, TimeSpan initTs) | ||
{ | ||
return (int)(milliseconds * InitialSamples / Math.Max(1.0, initTs.TotalMilliseconds)); | ||
} | ||
|
||
public Result RunBatch(BenchTask task, int milliseconds) | ||
public async Task<Result> RunBatch(BenchTask task, int milliseconds) | ||
{ | ||
DateTime start = DateTime.Now; | ||
DateTime end; | ||
int i = 0; | ||
try | ||
{ | ||
// run one to eliminate possible startup overhead and do GC collection | ||
RunStep(); | ||
if (HasRunStepAsync) | ||
await RunStepAsync(); | ||
else | ||
RunStep(); | ||
|
||
GC.Collect(); | ||
|
||
start = DateTime.Now; | ||
for (i = 0; i < InitialSamples; i++) | ||
RunStep(); | ||
if (HasRunStepAsync) | ||
await RunStepAsync(); | ||
else | ||
RunStep(); | ||
end = DateTime.Now; | ||
|
||
var initTs = end - start; | ||
|
@@ -79,7 +89,10 @@ public Result RunBatch(BenchTask task, int milliseconds) | |
start = DateTime.Now; | ||
for (i = 0; i < steps; i++) | ||
{ | ||
RunStep(); | ||
if (HasRunStepAsync) | ||
await RunStepAsync(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that the async continuation is probably executed from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. I think there should not be much else happening at that time, besides things which are part of the startup. Not sure whether we can improve that. |
||
else | ||
RunStep(); | ||
} | ||
end = DateTime.Now; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<!DOCTYPE html> | ||
<!-- Licensed to the .NET Foundation under one or more agreements. --> | ||
<!-- The .NET Foundation licenses this file to you under the MIT license. --> | ||
<html> | ||
<head> | ||
<title>App task</title> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
</head> | ||
<body> | ||
<h3 id="header">Wasm Browser Sample - App task frame</h3> | ||
<span id="out"></span> | ||
<script type='text/javascript'> | ||
var test_exit = function(exit_code) | ||
{ | ||
/* Set result in a tests_done element, to be read by xharness */ | ||
var tests_done_elem = document.createElement("label"); | ||
tests_done_elem.id = "tests_done"; | ||
tests_done_elem.innerHTML = exit_code.toString(); | ||
document.body.appendChild(tests_done_elem); | ||
|
||
console.log(`WASM EXIT ${exit_code}`); | ||
}; | ||
|
||
window.addEventListener("pageshow", event => { window.parent.resolveAppStartEvent(event); }) | ||
|
||
var App = { | ||
init: function () { | ||
INTERNAL.call_static_method("[Wasm.Browser.Bench.Sample] Sample.AppStartTask/ReachManaged:Reached"); | ||
}, | ||
|
||
reached: function() { | ||
window.parent.resolveAppStartEvent("reached"); | ||
} | ||
}; | ||
|
||
</script> | ||
<script type="text/javascript" src="runtime.js"></script> | ||
<script defer src="dotnet.js"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
var AppStart = { | ||
Construct: function() { | ||
this._frame = document.createElement('iframe'); | ||
document.body.appendChild(this._frame); | ||
}, | ||
|
||
WaitForPageShow: async function() { | ||
let promise; | ||
let promiseResolve; | ||
this._frame.src = 'appstart-frame.html'; | ||
promise = new Promise(resolve => { promiseResolve = resolve; }) | ||
window.resolveAppStartEvent = function(event) { promiseResolve(); } | ||
await promise; | ||
}, | ||
|
||
WaitForReached: async function() { | ||
let promise; | ||
let promiseResolve; | ||
this._frame.src = 'appstart-frame.html'; | ||
promise = new Promise(resolve => { promiseResolve = resolve; }) | ||
window.resolveAppStartEvent = function(event) { | ||
if (event == "reached") | ||
promiseResolve(); | ||
} | ||
await promise; | ||
}, | ||
|
||
RemoveFrame: function () { | ||
document.body.removeChild(this._frame); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
iframe { | ||
display:none; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.