Skip to content

Commit 79aab5c

Browse files
authored
[Xamarin.Android.Build.Tasks] Scan for JCWs for each ABI in parallel (#9215)
In order to ensure that all of our per-arch assemblies contain the same Java Callable Wrapper types, we scan them all. We can parallelize this operation per-ABI to save time. *Note*: It is ok to parallelize Cecil here, as each "set" contains completely different assemblies coming from completely different assembly resolvers. Fresh `dotnet build` of Android template (defaults to 4 architectures), total time `GenerateJavaSourcesAndMaybeClassifyMarshalMethods` takes: | Commit | Time (ms) | | :-------- | -------: | | 48239a5 | 11835 ms | | PR | 4548 ms |
1 parent e70ae00 commit 79aab5c

File tree

3 files changed

+26
-10
lines changed

3 files changed

+26
-10
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
using Xamarin.Android.Tools;
1717
using Microsoft.Android.Build.Tasks;
1818
using Java.Interop.Tools.JavaCallableWrappers.Adapters;
19+
using System.Threading.Tasks;
20+
using System.Collections.Concurrent;
1921

2022
namespace Xamarin.Android.Tasks
2123
{
@@ -183,26 +185,38 @@ void Run (bool useMarshalMethods)
183185
}
184186

185187
// Now that "never" never happened, we can proceed knowing that at least the assembly sets are the same for each architecture
186-
var nativeCodeGenStates = new Dictionary<AndroidTargetArch, NativeCodeGenState> ();
187-
bool generateJavaCode = true;
188+
var nativeCodeGenStates = new ConcurrentDictionary<AndroidTargetArch, NativeCodeGenState> ();
188189
NativeCodeGenState? templateCodeGenState = null;
189190

190-
foreach (var kvp in allAssembliesPerArch) {
191+
var firstArch = allAssembliesPerArch.First ().Key;
192+
var generateSucceeded = true;
193+
194+
// Generate Java sources in parallel
195+
Parallel.ForEach (allAssembliesPerArch, (kvp) => {
191196
AndroidTargetArch arch = kvp.Key;
192197
Dictionary<string, ITaskItem> archAssemblies = kvp.Value;
198+
199+
// We only need to generate Java code for one ABI, as the Java code is ABI-agnostic
200+
// Pick the "first" one as the one to generate Java code for
201+
var generateJavaCode = arch == firstArch;
202+
193203
(bool success, NativeCodeGenState? state) = GenerateJavaSourcesAndMaybeClassifyMarshalMethods (arch, archAssemblies, MaybeGetArchAssemblies (userAssembliesPerArch, arch), useMarshalMethods, generateJavaCode);
194204

195205
if (!success) {
196-
return;
206+
generateSucceeded = false;
197207
}
198208

209+
// If this is the first architecture, we need to store the state for later use
199210
if (generateJavaCode) {
200211
templateCodeGenState = state;
201-
generateJavaCode = false;
202212
}
203213

204-
nativeCodeGenStates.Add (arch, state);
205-
}
214+
nativeCodeGenStates.TryAdd (arch, state);
215+
});
216+
217+
// If we hit an error generating the Java code, we should bail out now
218+
if (!generateSucceeded)
219+
return;
206220

207221
if (templateCodeGenState == null) {
208222
throw new InvalidOperationException ($"Internal error: no native code generator state defined");

src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Java.Interop.Tools.TypeNameMappings;
1414
using Xamarin.Android.Tools;
1515
using Microsoft.Android.Build.Tasks;
16+
using System.Collections.Concurrent;
1617

1718
namespace Xamarin.Android.Tasks
1819
{
@@ -313,9 +314,9 @@ void AddEnvironment ()
313314
}
314315
}
315316

316-
Dictionary<AndroidTargetArch, NativeCodeGenState>? nativeCodeGenStates = null;
317+
ConcurrentDictionary<AndroidTargetArch, NativeCodeGenState>? nativeCodeGenStates = null;
317318
if (enableMarshalMethods) {
318-
nativeCodeGenStates = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<Dictionary<AndroidTargetArch, NativeCodeGenState>> (
319+
nativeCodeGenStates = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal<ConcurrentDictionary<AndroidTargetArch, NativeCodeGenState>> (
319320
ProjectSpecificTaskObjectKey (GenerateJavaStubs.NativeCodeGenStateRegisterTaskKey),
320321
RegisteredTaskObjectLifetime.Build
321322
);

src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Microsoft.Build.Utilities;
1616
using Mono.Cecil;
1717
using Xamarin.Android.Tools;
18+
using System.Collections.Concurrent;
1819

1920
namespace Xamarin.Android.Tasks;
2021

@@ -220,7 +221,7 @@ static string GetMonoInitSource (string androidSdkPlatform)
220221
return builder.ToString ();
221222
}
222223

223-
public static void EnsureAllArchitecturesAreIdentical (TaskLoggingHelper logger, Dictionary<AndroidTargetArch, NativeCodeGenState> javaStubStates)
224+
public static void EnsureAllArchitecturesAreIdentical (TaskLoggingHelper logger, ConcurrentDictionary<AndroidTargetArch, NativeCodeGenState> javaStubStates)
224225
{
225226
if (javaStubStates.Count <= 1) {
226227
return;

0 commit comments

Comments
 (0)