Skip to content

Commit a688e0b

Browse files
jeromelabanMartinZikmund
authored andcommitted
perf: Additional changes to mitigate PG-AOT dotnet/runtime issue #56309
See unoplatform#7005 for additional details
1 parent c4a739e commit a688e0b

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
@@ -415,16 +415,25 @@ internal void SetValue(DependencyProperty property, object? value, DependencyPro
415415
{
416416
if (_trace.IsEnabled)
417417
{
418-
using (WritePropertyEventTrace(TraceProvider.SetValueStart, TraceProvider.SetValueStop, property, precedence))
418+
/// <remarks>
419+
/// This method contains or is called by a try/catch containing method and
420+
/// can be significantly slower than other methods as a result on WebAssembly.
421+
/// See https://github.com/dotnet/runtime/issues/56309
422+
/// </remarks>
423+
void SetValueWithTrace(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails, bool isThemeBinding)
419424
{
420-
InnerSetValue(property, value, precedence, propertyDetails, isThemeBinding);
425+
using (WritePropertyEventTrace(TraceProvider.SetValueStart, TraceProvider.SetValueStop, property, precedence))
426+
{
427+
InnerSetValue(property, value, precedence, propertyDetails, isThemeBinding);
428+
}
421429
}
430+
431+
SetValueWithTrace(property, value, precedence, propertyDetails, isThemeBinding);
422432
}
423433
else
424434
{
425435
InnerSetValue(property, value, precedence, propertyDetails, isThemeBinding);
426436
}
427-
428437
}
429438

430439
private void InnerSetValue(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails, bool isThemeBinding)
@@ -1000,8 +1009,12 @@ private void CleanupInheritedProperties()
10001009

10011010
if (ActualInstance != null)
10021011
{
1003-
foreach (var dp in _updatedProperties)
1012+
// This block is a manual enumeration to avoid the foreach pattern
1013+
// See https://github.com/dotnet/runtime/issues/56309 for details
1014+
var propertiesEnumerator = _updatedProperties.GetEnumerator();
1015+
while(propertiesEnumerator.MoveNext())
10041016
{
1017+
var dp = propertiesEnumerator.Current;
10051018
SetValue(dp, DependencyProperty.UnsetValue, DependencyPropertyValuePrecedences.Inheritance);
10061019
}
10071020

@@ -1367,8 +1380,13 @@ private void PropagateInheritedNonLocalProperties(DependencyObjectStore? childSt
13671380
// call to IsAncestor.
13681381
var actualInstanceAlias = ActualInstance;
13691382

1370-
foreach (var sourceInstanceProperties in _inheritedForwardedProperties)
1383+
// This block is a manual enumeration to avoid the foreach pattern
1384+
// See https://github.com/dotnet/runtime/issues/56309 for details
1385+
var forwardedEnumerator = _inheritedForwardedProperties.GetEnumerator();
1386+
while(forwardedEnumerator.MoveNext())
13711387
{
1388+
var sourceInstanceProperties = forwardedEnumerator.Current;
1389+
13721390
if (
13731391
IsAncestor(actualInstanceAlias, ancestors, sourceInstanceProperties.Value.Target)
13741392
|| (
@@ -1400,9 +1418,9 @@ void Propagate(DependencyObjectStore store)
14001418
}
14011419
else
14021420
{
1403-
foreach (var child in _childrenStores)
1421+
for (var i = 0; i < _childrenStores.Count; i++)
14041422
{
1405-
Propagate(child);
1423+
Propagate(_childrenStores[i]);
14061424
}
14071425
}
14081426
}

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)