Skip to content

Commit ba58785

Browse files
committed
perf: Additional changes to mitigate PG-AOT dotnet/runtime issue #56309
See #7005 for additional details
1 parent 7061278 commit ba58785

File tree

5 files changed

+159
-60
lines changed

5 files changed

+159
-60
lines changed

src/Uno.Foundation.Runtime.WebAssembly/Interop/Runtime.wasm.cs

+66-22
Original file line numberDiff line numberDiff line change
@@ -60,19 +60,30 @@ public static bool InvokeJSUnmarshalled(string functionIdentifier, IntPtr arg0)
6060
{
6161
if (_trace.IsEnabled)
6262
{
63-
using (WritePropertyEventTrace(TraceProvider.UnmarshalledInvokedStart, TraceProvider.UnmarshalledInvokedEnd, functionIdentifier))
64-
{
65-
var ret = InnerInvokeJSUnmarshalled(functionIdentifier, arg0, out var exception);
66-
67-
if(exception != null)
68-
{
69-
throw exception;
70-
}
63+
return InvokeJSUnmarshalledWithTrace(functionIdentifier, arg0);
64+
}
65+
else
66+
{
67+
var ret = InnerInvokeJSUnmarshalled(functionIdentifier, arg0, out var exception);
7168

72-
return ret;
69+
if (exception != null)
70+
{
71+
throw exception;
7372
}
73+
74+
return ret;
7475
}
75-
else
76+
}
77+
78+
/// <remarks>
79+
/// This method contains or is called by a try/catch containing method and
80+
/// can be significantly slower than other methods as a result on WebAssembly.
81+
/// See https://github.com/dotnet/runtime/issues/56309
82+
/// </remarks>
83+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
84+
private static bool InvokeJSUnmarshalledWithTrace(string functionIdentifier, IntPtr arg0)
85+
{
86+
using (WritePropertyEventTrace(TraceProvider.UnmarshalledInvokedStart, TraceProvider.UnmarshalledInvokedEnd, functionIdentifier))
7687
{
7788
var ret = InnerInvokeJSUnmarshalled(functionIdentifier, arg0, out var exception);
7889

@@ -93,17 +104,28 @@ internal static bool InvokeJSUnmarshalled(string functionIdentifier, IntPtr arg0
93104
{
94105
if (_trace.IsEnabled)
95106
{
96-
using (WritePropertyEventTrace(TraceProvider.UnmarshalledInvokedStart, TraceProvider.UnmarshalledInvokedEnd, functionIdentifier))
97-
{
98-
return InnerInvokeJSUnmarshalled(functionIdentifier, arg0, out exception);
99-
}
107+
return InvokeJSUnmarshalledWithTrace(functionIdentifier, arg0, out exception);
100108
}
101109
else
102110
{
103111
return InnerInvokeJSUnmarshalled(functionIdentifier, arg0, out exception);
104112
}
105113
}
106114

115+
/// <remarks>
116+
/// This method contains or is called by a try/catch containing method and
117+
/// can be significantly slower than other methods as a result on WebAssembly.
118+
/// See https://github.com/dotnet/runtime/issues/56309
119+
/// </remarks>
120+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
121+
private static bool InvokeJSUnmarshalledWithTrace(string functionIdentifier, IntPtr arg0, out Exception? exception)
122+
{
123+
using (WritePropertyEventTrace(TraceProvider.UnmarshalledInvokedStart, TraceProvider.UnmarshalledInvokedEnd, functionIdentifier))
124+
{
125+
return InnerInvokeJSUnmarshalled(functionIdentifier, arg0, out exception);
126+
}
127+
}
128+
107129
private static bool InnerInvokeJSUnmarshalled(string functionIdentifier, IntPtr arg0, out Exception? exception)
108130
{
109131
exception = null;
@@ -135,17 +157,28 @@ public static bool InvokeJSUnmarshalled(string functionIdentifier, IntPtr arg0,
135157
{
136158
if (_trace.IsEnabled)
137159
{
138-
using (WritePropertyEventTrace(TraceProvider.UnmarshalledInvokedStart, TraceProvider.UnmarshalledInvokedEnd, functionIdentifier))
139-
{
140-
return InnerInvokeJSUnmarshalled(functionIdentifier, arg0, arg1);
141-
}
160+
return InvokeJSUnmarshalledWithTrace(functionIdentifier, arg0, arg1);
142161
}
143162
else
144163
{
145164
return InnerInvokeJSUnmarshalled(functionIdentifier, arg0, arg1);
146165
}
147166
}
148167

168+
/// <remarks>
169+
/// This method contains or is called by a try/catch containing method and
170+
/// can be significantly slower than other methods as a result on WebAssembly.
171+
/// See https://github.com/dotnet/runtime/issues/56309
172+
/// </remarks>
173+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
174+
private static bool InvokeJSUnmarshalledWithTrace(string functionIdentifier, IntPtr arg0, IntPtr arg1)
175+
{
176+
using (WritePropertyEventTrace(TraceProvider.UnmarshalledInvokedStart, TraceProvider.UnmarshalledInvokedEnd, functionIdentifier))
177+
{
178+
return InnerInvokeJSUnmarshalled(functionIdentifier, arg0, arg1);
179+
}
180+
}
181+
149182
private static bool InnerInvokeJSUnmarshalled(string functionIdentifier, IntPtr arg0, IntPtr arg1)
150183
{
151184
var methodId = GetMethodId(functionIdentifier);
@@ -179,10 +212,7 @@ public static string InvokeJS(string str)
179212
{
180213
if (_trace.IsEnabled)
181214
{
182-
using (WritePropertyEventTrace(TraceProvider.InvokeStart, TraceProvider.InvokeEnd, str))
183-
{
184-
return InnerInvokeJS(str);
185-
}
215+
return InvokeJSWithTrace(str);
186216
}
187217
else
188218
{
@@ -203,6 +233,20 @@ public static string InvokeJS(string str)
203233
}
204234
}
205235

236+
/// <remarks>
237+
/// This method contains or is called by a try/catch containing method and
238+
/// can be significantly slower than other methods as a result on WebAssembly.
239+
/// See https://github.com/dotnet/runtime/issues/56309
240+
/// </remarks>
241+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
242+
private static string InvokeJSWithTrace(string str)
243+
{
244+
using (WritePropertyEventTrace(TraceProvider.InvokeStart, TraceProvider.InvokeEnd, str))
245+
{
246+
return InnerInvokeJS(str);
247+
}
248+
}
249+
206250
private static string InnerInvokeJS(String str)
207251
{
208252
if (_logger.Value.IsEnabled(LogLevel.Debug))

src/Uno.UI/DataBinding/BindingExpression.cs

+22-12
Original file line numberDiff line numberDiff line change
@@ -541,23 +541,33 @@ private void SetTargetValueForXBindSelector()
541541
{
542542
try
543543
{
544-
var canSetTarget = _updateSources?.None(s => s.ValueType == null) ?? true;
545-
546-
if (canSetTarget)
547-
{
548-
SetTargetValueSafe(ParentBinding.XBindSelector(DataContext));
549-
}
550-
else
544+
/// <remarks>
545+
/// This method contains or is called by a try/catch containing method and
546+
/// can be significantly slower than other methods as a result on WebAssembly.
547+
/// See https://github.com/dotnet/runtime/issues/56309
548+
/// </remarks>
549+
void SetTargetValue()
551550
{
552-
// x:Bind failed bindings don't change the target value
553-
// if no FallbackValue was specified.
554-
ApplyFallbackValue(useTypeDefaultValue: false);
551+
var canSetTarget = _updateSources?.None(s => s.ValueType == null) ?? true;
555552

556-
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
553+
if (canSetTarget)
557554
{
558-
this.Log().Debug($"Binding path does not provide a value [{TargetPropertyDetails}] on [{_targetOwnerType}], using fallback value");
555+
SetTargetValueSafe(ParentBinding.XBindSelector(DataContext));
556+
}
557+
else
558+
{
559+
// x:Bind failed bindings don't change the target value
560+
// if no FallbackValue was specified.
561+
ApplyFallbackValue(useTypeDefaultValue: false);
562+
563+
if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
564+
{
565+
this.Log().Debug($"Binding path does not provide a value [{TargetPropertyDetails}] on [{_targetOwnerType}], using fallback value");
566+
}
559567
}
560568
}
569+
570+
SetTargetValue();
561571
}
562572
catch (Exception e)
563573
{

src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs

+13-3
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,21 @@ private void OnDataContextChanged(object? providedDataContext, object? actualDat
298298

299299
if (TryWriteDataContextChangedEventActivity() is { } trace)
300300
{
301-
// "using" statements are costly under we https://github.com/dotnet/runtime/issues/50783
302-
using (trace)
301+
/// <remarks>
302+
/// This method contains or is called by a try/catch containing method and
303+
/// can be significantly slower than other methods as a result on WebAssembly.
304+
/// See https://github.com/dotnet/runtime/issues/56309
305+
/// </remarks>
306+
void ApplyWithTrace (object? actualDataContext, IDisposable trace)
303307
{
304-
ApplyDataContext(actualDataContext);
308+
using (trace)
309+
{
310+
ApplyDataContext(actualDataContext);
311+
}
305312
}
313+
314+
// "using" statements are costly under we https://github.com/dotnet/runtime/issues/50783
315+
ApplyWithTrace(actualDataContext, trace);
306316
}
307317
else
308318
{

src/Uno.UI/UI/Xaml/DependencyObjectStore.cs

+25-7
Original file line numberDiff line numberDiff line change
@@ -419,16 +419,25 @@ internal void SetValue(DependencyProperty property, object? value, DependencyPro
419419
{
420420
if (_trace.IsEnabled)
421421
{
422-
using (WritePropertyEventTrace(TraceProvider.SetValueStart, TraceProvider.SetValueStop, property, precedence))
422+
/// <remarks>
423+
/// This method contains or is called by a try/catch containing method and
424+
/// can be significantly slower than other methods as a result on WebAssembly.
425+
/// See https://github.com/dotnet/runtime/issues/56309
426+
/// </remarks>
427+
void SetValueWithTrace(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails, bool isThemeBinding)
423428
{
424-
InnerSetValue(property, value, precedence, propertyDetails, isThemeBinding);
429+
using (WritePropertyEventTrace(TraceProvider.SetValueStart, TraceProvider.SetValueStop, property, precedence))
430+
{
431+
InnerSetValue(property, value, precedence, propertyDetails, isThemeBinding);
432+
}
425433
}
434+
435+
SetValueWithTrace(property, value, precedence, propertyDetails, isThemeBinding);
426436
}
427437
else
428438
{
429439
InnerSetValue(property, value, precedence, propertyDetails, isThemeBinding);
430440
}
431-
432441
}
433442

434443
private void InnerSetValue(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails, bool isThemeBinding)
@@ -1004,8 +1013,12 @@ private void CleanupInheritedProperties()
10041013

10051014
if (ActualInstance != null)
10061015
{
1007-
foreach (var dp in _updatedProperties)
1016+
// This block is a manual enumeration to avoid the foreach pattern
1017+
// See https://github.com/dotnet/runtime/issues/56309 for details
1018+
var propertiesEnumerator = _updatedProperties.GetEnumerator();
1019+
while(propertiesEnumerator.MoveNext())
10081020
{
1021+
var dp = propertiesEnumerator.Current;
10091022
SetValue(dp, DependencyProperty.UnsetValue, DependencyPropertyValuePrecedences.Inheritance);
10101023
}
10111024

@@ -1371,8 +1384,13 @@ private void PropagateInheritedNonLocalProperties(DependencyObjectStore? childSt
13711384
// call to IsAncestor.
13721385
var actualInstanceAlias = ActualInstance;
13731386

1374-
foreach (var sourceInstanceProperties in _inheritedForwardedProperties)
1387+
// This block is a manual enumeration to avoid the foreach pattern
1388+
// See https://github.com/dotnet/runtime/issues/56309 for details
1389+
var forwardedEnumerator = _inheritedForwardedProperties.GetEnumerator();
1390+
while(forwardedEnumerator.MoveNext())
13751391
{
1392+
var sourceInstanceProperties = forwardedEnumerator.Current;
1393+
13761394
if (
13771395
IsAncestor(actualInstanceAlias, ancestors, sourceInstanceProperties.Value.Target)
13781396
|| (
@@ -1404,9 +1422,9 @@ void Propagate(DependencyObjectStore store)
14041422
}
14051423
else
14061424
{
1407-
foreach (var child in _childrenStores)
1425+
for (var i = 0; i < _childrenStores.Count; i++)
14081426
{
1409-
Propagate(child);
1427+
Propagate(_childrenStores[i]);
14101428
}
14111429
}
14121430
}

src/Uno.UI/UI/Xaml/FrameworkElement.Layout.netstd.cs

+33-16
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,34 @@ internal sealed override void MeasureCore(Size availableSize)
5050
{
5151
if (_trace.IsEnabled)
5252
{
53-
var traceActivity = _trace.WriteEventActivity(
54-
TraceProvider.FrameworkElement_MeasureStart,
55-
TraceProvider.FrameworkElement_MeasureStop,
56-
new object[] { GetType().Name, this.GetDependencyObjectId(), Name, availableSize.ToString() }
57-
);
58-
59-
using (traceActivity)
53+
/// <remarks>
54+
/// This method contains or is called by a try/catch containing method and
55+
/// can be significantly slower than other methods as a result on WebAssembly.
56+
/// See https://github.com/dotnet/runtime/issues/56309
57+
/// </remarks>
58+
void MeasureCoreWithTrace(Size availableSize)
6059
{
61-
InnerMeasureCore(availableSize);
60+
var traceActivity = _trace.WriteEventActivity(
61+
TraceProvider.FrameworkElement_MeasureStart,
62+
TraceProvider.FrameworkElement_MeasureStop,
63+
new object[] { GetType().Name, this.GetDependencyObjectId(), Name, availableSize.ToString() }
64+
);
65+
66+
using (traceActivity)
67+
{
68+
InnerMeasureCore(availableSize);
69+
}
6270
}
71+
72+
MeasureCoreWithTrace(availableSize);
6373
}
6474
else
6575
{
6676
// This method is split in two functions to avoid the dynCalls
6777
// invocations generation for mono-wasm AOT inside of try/catch/finally blocks.
6878
InnerMeasureCore(availableSize);
6979
}
80+
7081
}
7182

7283
private void InnerMeasureCore(Size availableSize)
@@ -124,23 +135,29 @@ internal sealed override void ArrangeCore(Rect finalRect)
124135
{
125136
if (_trace.IsEnabled)
126137
{
127-
var traceActivity = _trace.WriteEventActivity(
128-
TraceProvider.FrameworkElement_ArrangeStart,
129-
TraceProvider.FrameworkElement_ArrangeStop,
130-
new object[] { GetType().Name, this.GetDependencyObjectId(), Name, finalRect.ToString() }
131-
);
132-
133-
using (traceActivity)
138+
void ArrangeCoreWithTrace(Rect finalRect)
134139
{
135-
InnerArrangeCore(finalRect);
140+
var traceActivity = _trace.WriteEventActivity(
141+
TraceProvider.FrameworkElement_ArrangeStart,
142+
TraceProvider.FrameworkElement_ArrangeStop,
143+
new object[] { GetType().Name, this.GetDependencyObjectId(), Name, finalRect.ToString() }
144+
);
145+
146+
using (traceActivity)
147+
{
148+
InnerArrangeCore(finalRect);
149+
}
136150
}
151+
152+
ArrangeCoreWithTrace(finalRect);
137153
}
138154
else
139155
{
140156
// This method is split in two functions to avoid the dynCalls
141157
// invocations generation for mono-wasm AOT inside of try/catch/finally blocks.
142158
InnerArrangeCore(finalRect);
143159
}
160+
144161
}
145162

146163
private static bool IsLessThanAndNotCloseTo(double a, double b) => a < (b - SIZE_EPSILON);

0 commit comments

Comments
 (0)