From 324fa56569b02a26d7f0a0328c782c4cbb515320 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Fri, 28 Feb 2025 02:12:40 -0800
Subject: [PATCH 01/13] add a dependency on the oshi-core library (to obtain
CPU feature flags)
---
snaploader/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/snaploader/build.gradle b/snaploader/build.gradle
index 3be1ac0..a752cc9 100644
--- a/snaploader/build.gradle
+++ b/snaploader/build.gradle
@@ -27,5 +27,5 @@ jar { // assemble jar options [java -jar]
}
dependencies {
-
+ implementation('com.github.oshi:oshi-core:6.7.0')
}
From dd7d34c03e50312b49d933f0fb47ffa7ec179345 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Fri, 28 Feb 2025 02:14:34 -0800
Subject: [PATCH 02/13] NativeVariant: add the Cpu.hasExtensions() method
---
.../platform/util/NativeVariant.java | 81 ++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
index 8aaa525..e154833 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023-2024, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
+ * Copyright (c) 2023-2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -32,6 +32,14 @@
package electrostatic4j.snaploader.platform.util;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.TreeSet;
+import oshi.SystemInfo;
+import oshi.hardware.CentralProcessor;
+import oshi.hardware.HardwareAbstractionLayer;
+
/**
* Wraps objects for native variant constituents (OS + ARCH={CPU + INSTRUCT_SET} + VM).
*
@@ -250,6 +258,77 @@ public static boolean isAMD() {
public static boolean isARM() {
return OS_ARCH.getProperty().contains("arm") || OS_ARCH.getProperty().contains("aarch");
}
+
+ /**
+ * Cache the names of instruction-set architecture (ISA) extensions that
+ * are present.
+ */
+ private static final Collection extNameCache = new TreeSet<>();
+
+ /**
+ * Lazily initializes the collection of ISA extensions that are present.
+ */
+ private static synchronized void initializeExtNameCache() {
+ if (extNameCache.isEmpty()) {
+ // Obtain the list of CPU features from OSHI:
+ SystemInfo si = new SystemInfo();
+ HardwareAbstractionLayer hal = si.getHardware();
+ CentralProcessor cpu = hal.getProcessor();
+ List strings = cpu.getFeatureFlags();
+
+ for (String string : strings) {
+ String lcString = string.toLowerCase(Locale.ROOT);
+
+ // Matches to test for interesting X86 ISA extensions:
+ if (lcString.matches(".*\\bavx\\b.*")) {
+ extNameCache.add("avx");
+ }
+ if (lcString.matches(".*\\bavx2\\b.*")) {
+ extNameCache.add("avx2");
+ }
+ if (lcString.matches(".*\\bbmi1\\b.*")) {
+ extNameCache.add("bmi1");
+ }
+ if (lcString.matches(".*\\bf16c\\b.*")) {
+ extNameCache.add("f16c");
+ }
+ if (lcString.matches(".*\\bfma\\b.*")) {
+ extNameCache.add("fma");
+ }
+ if (lcString.matches(".*\\bsse4_1\\b.*")) {
+ extNameCache.add("sse4_1");
+ }
+ if (lcString.matches(".*\\bsse4_2\\b.*")) {
+ extNameCache.add("sse4_2");
+ }
+ }
+ /*
+ * Add an empty string so that the name cache
+ * is guaranteed to no longer be empty. This ensures
+ * that getFeatureFlags() is invoked at most once.
+ */
+ extNameCache.add("");
+ }
+ }
+
+ /**
+ * Tests whether the named ISA extensions are all present.
+ *
+ * @param names the names of the extensions to test for
+ * @return {@code true} if all are present, otherwise {@code false}
+ */
+ public static boolean hasExtensions(String... names) {
+ initializeExtNameCache();
+
+ for (String name : names) {
+ String lcName = name.toLowerCase(Locale.ROOT);
+ boolean isPresent = extNameCache.contains(lcName);
+ if (!isPresent) {
+ return false;
+ }
+ }
+ return true;
+ }
}
/**
From 2680449e89c2d23303fe9b5af90e54e28ece5392 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Sat, 1 Mar 2025 11:06:09 -0800
Subject: [PATCH 03/13] NativeVariant: solve Windows issue, eliminate caching
of ISA extensions
---
.../platform/util/NativeVariant.java | 85 ++++++-------------
1 file changed, 28 insertions(+), 57 deletions(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
index e154833..fcbfb24 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
@@ -36,6 +36,8 @@
import java.util.List;
import java.util.Locale;
import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.HardwareAbstractionLayer;
@@ -260,73 +262,42 @@ public static boolean isARM() {
}
/**
- * Cache the names of instruction-set architecture (ISA) extensions that
- * are present.
- */
- private static final Collection extNameCache = new TreeSet<>();
-
- /**
- * Lazily initializes the collection of ISA extensions that are present.
+ * Tests whether the named ISA extensions are all present.
+ *
+ * @param requiredNames the names of the extensions to test for
+ * @return {@code true} if all are present, otherwise {@code false}
*/
- private static synchronized void initializeExtNameCache() {
- if (extNameCache.isEmpty()) {
- // Obtain the list of CPU features from OSHI:
- SystemInfo si = new SystemInfo();
- HardwareAbstractionLayer hal = si.getHardware();
- CentralProcessor cpu = hal.getProcessor();
- List strings = cpu.getFeatureFlags();
+ public static boolean hasExtensions(String... requiredNames) {
+ // Obtain the list of CPU feature strings from OSHI:
+ SystemInfo si = new SystemInfo();
+ HardwareAbstractionLayer hal = si.getHardware();
+ CentralProcessor cpu = hal.getProcessor();
+ List oshiList = cpu.getFeatureFlags();
- for (String string : strings) {
- String lcString = string.toLowerCase(Locale.ROOT);
+ Pattern pattern = Pattern.compile("[a-z][a-z0-9_]*");
- // Matches to test for interesting X86 ISA extensions:
- if (lcString.matches(".*\\bavx\\b.*")) {
- extNameCache.add("avx");
- }
- if (lcString.matches(".*\\bavx2\\b.*")) {
- extNameCache.add("avx2");
- }
- if (lcString.matches(".*\\bbmi1\\b.*")) {
- extNameCache.add("bmi1");
- }
- if (lcString.matches(".*\\bf16c\\b.*")) {
- extNameCache.add("f16c");
- }
- if (lcString.matches(".*\\bfma\\b.*")) {
- extNameCache.add("fma");
- }
- if (lcString.matches(".*\\bsse4_1\\b.*")) {
- extNameCache.add("sse4_1");
- }
- if (lcString.matches(".*\\bsse4_2\\b.*")) {
- extNameCache.add("sse4_2");
- }
+ // Convert the list to a collection of feature names:
+ Collection presentFeatures = new TreeSet<>();
+ for (String oshiString : oshiList) {
+ String lcString = oshiString.toLowerCase(Locale.ROOT);
+ Matcher matcher = pattern.matcher(lcString);
+ while (matcher.find()) {
+ String featureName = matcher.group();
+ presentFeatures.add(featureName);
}
- /*
- * Add an empty string so that the name cache
- * is guaranteed to no longer be empty. This ensures
- * that getFeatureFlags() is invoked at most once.
- */
- extNameCache.add("");
}
- }
- /**
- * Tests whether the named ISA extensions are all present.
- *
- * @param names the names of the extensions to test for
- * @return {@code true} if all are present, otherwise {@code false}
- */
- public static boolean hasExtensions(String... names) {
- initializeExtNameCache();
-
- for (String name : names) {
- String lcName = name.toLowerCase(Locale.ROOT);
- boolean isPresent = extNameCache.contains(lcName);
+ // Test for each required extension:
+ for (String extensionName : requiredNames) {
+ String lcName = extensionName.toLowerCase(Locale.ROOT);
+ String pfName = "pf_" + lcName + "_instructions_available";
+ boolean isPresent = presentFeatures.contains(lcName)
+ || presentFeatures.contains(pfName);
if (!isPresent) {
return false;
}
}
+
return true;
}
}
From 0baf927fed56e688503aa055602bec12ba624b96 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Mon, 3 Mar 2025 12:39:09 -0800
Subject: [PATCH 04/13] NativeVariant: re-implement caching, fix
Windows-on-ARM, add comments
---
.../platform/util/NativeVariant.java | 67 ++++++++++++++-----
1 file changed, 52 insertions(+), 15 deletions(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
index fcbfb24..70681b1 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
@@ -91,6 +91,15 @@ public enum NativeVariant {
private final String property;
+ /**
+ * named CPU features that were detected by the OSHI library
+ */
+ private static Collection presentFeatures;
+ /**
+ * serialize access to presentFeatures
+ */
+ private static Object synchronizeFeatures = new Object();
+
NativeVariant(final String property) {
this.property = property;
}
@@ -262,12 +271,11 @@ public static boolean isARM() {
}
/**
- * Tests whether the named ISA extensions are all present.
- *
- * @param requiredNames the names of the extensions to test for
- * @return {@code true} if all are present, otherwise {@code false}
+ * Reads named CPU features from the OSHI library and parses them into
+ * words. If system commands are executed, this might be an expensive
+ * operation.
*/
- public static boolean hasExtensions(String... requiredNames) {
+ private static Collection readFeatureFlags() {
// Obtain the list of CPU feature strings from OSHI:
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
@@ -277,24 +285,53 @@ public static boolean hasExtensions(String... requiredNames) {
Pattern pattern = Pattern.compile("[a-z][a-z0-9_]*");
// Convert the list to a collection of feature names:
- Collection presentFeatures = new TreeSet<>();
+ Collection result = new TreeSet<>();
for (String oshiString : oshiList) {
String lcString = oshiString.toLowerCase(Locale.ROOT);
Matcher matcher = pattern.matcher(lcString);
while (matcher.find()) {
String featureName = matcher.group();
- presentFeatures.add(featureName);
+ result.add(featureName);
}
}
- // Test for each required extension:
- for (String extensionName : requiredNames) {
- String lcName = extensionName.toLowerCase(Locale.ROOT);
- String pfName = "pf_" + lcName + "_instructions_available";
- boolean isPresent = presentFeatures.contains(lcName)
- || presentFeatures.contains(pfName);
- if (!isPresent) {
- return false;
+ return result;
+ }
+
+ /**
+ * Tests whether the named ISA extensions are all present.
+ *
+ * @param requiredNames the names of the extensions to test for
+ * @return {@code true} if the current platform supports all of the
+ * specified extensions, otherwise {@code false}
+ */
+ public static boolean hasExtensions(String... requiredNames) {
+ synchronized (synchronizeFeatures) {
+ if (presentFeatures == null) {
+ presentFeatures = readFeatureFlags();
+ }
+
+ // Test for each required extension:
+ for (String extensionName : requiredNames) {
+ String lcName = extensionName.toLowerCase(Locale.ROOT);
+ /*
+ * On Windows, ISA extensions are coded as features
+ * with names like "PF_xxx_INSTRUCTIONS_AVAILABLE" and
+ * "PF_ARM_xxx_INSTRUCTIONS_AVAILABLE".
+ *
+ * For details see
+ * https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
+ */
+ String pfNameArm = "pf_arm_" + lcName + "_instructions_available";
+ String pfNameX86 = "pf_" + lcName + "_instructions_available";
+ boolean isPresent = presentFeatures.contains(lcName)
+ || presentFeatures.contains(pfNameX86)
+ || presentFeatures.contains(pfNameArm);
+
+ // conjunctive test: fails if any required extension is missing
+ if (!isPresent) {
+ return false;
+ }
}
}
From b13ce2f8daa4412640b4298de9ad0ea848a473c6 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Mon, 3 Mar 2025 12:41:35 -0800
Subject: [PATCH 05/13] NativeVariant: document the names of some well-known
ISA extensions
---
.../platform/util/NativeVariant.java | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
index 70681b1..6a59228 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
@@ -300,6 +300,38 @@ private static Collection readFeatureFlags() {
/**
* Tests whether the named ISA extensions are all present.
+ *
+ * Extension names are case-insensitive and might be reported
+ * differently by different operating systems or even by different
+ * versions of the same operating system.
+ *
+ * Examples of extension names:
+ * - "3dnow" for AMD 3D-Now
+ * - "avx" for x86 AVX
+ * - "avx2" for x86 AVX2
+ * - "avx512f" for x86 AVX512F
+ * - "bmi1" for x86 bit-manipulation instruction set 1
+ * - "f16c" for x86 half-precision floating-point
+ * - "fma" for x86 fused multiply-add
+ * - "fmac" for Arm floating-point multiply-accumulate
+ * - "mmx" for x86 MMX
+ * - "neon" for Arm NEON
+ * - "sse3" for x86 SSE3
+ * - "sse4_1" for x86 SSE4.1
+ * - "sse4_2" for x86 SSE4.2
+ * - "ssse3" for x86 SSSE3
+ * - "v8" for Arm V8
+ * - "v8_crc32" for Arm V8 extra CRC32
+ * - "v8_crypto" for Arm V8 extra cryptographic
+ * - "v81_atomic" for Arm V8.1 atomic
+ * - "v82_dp" for Arm V8.2 DP
+ * - "v83_jscvt" for Arm v8.3 JSCVT
+ * - "v83_lrcpc" for Arm v8.3 LRCPC
+ *
+ *
+ * Wikipedia provides informal descriptions of many ISA extensions.
+ * https://en.wikipedia.org/wiki/Template:Multimedia_extensions offers a
+ * good starting point.
*
* @param requiredNames the names of the extensions to test for
* @return {@code true} if the current platform supports all of the
From dd2be1bd1abd06c3488ef5419c9579c98f1964ca Mon Sep 17 00:00:00 2001
From: stephengold
Date: Mon, 3 Mar 2025 14:57:22 -0800
Subject: [PATCH 06/13] NativeVariant: remove 2 HTML tags from the javadoc
---
.../snaploader/platform/util/NativeVariant.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
index 6a59228..ed58c55 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
@@ -303,7 +303,7 @@ private static Collection readFeatureFlags() {
*
* Extension names are case-insensitive and might be reported
* differently by different operating systems or even by different
- * versions of the same operating system.
+ * versions of the same operating system.
*
* Examples of extension names:
* - "3dnow" for AMD 3D-Now
@@ -327,7 +327,7 @@ private static Collection readFeatureFlags() {
* - "v82_dp" for Arm V8.2 DP
* - "v83_jscvt" for Arm v8.3 JSCVT
* - "v83_lrcpc" for Arm v8.3 LRCPC
- *
+ *
*
* Wikipedia provides informal descriptions of many ISA extensions.
* https://en.wikipedia.org/wiki/Template:Multimedia_extensions offers a
From 27992441bff98508aa663639c5217f5d8163a2a2 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Mon, 3 Mar 2025 15:23:28 -0800
Subject: [PATCH 07/13] NativeVariant: move 2 fields to the Cpu inner class
(for readability)
---
.../platform/util/NativeVariant.java | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
index ed58c55..597a0b2 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
@@ -91,15 +91,6 @@ public enum NativeVariant {
private final String property;
- /**
- * named CPU features that were detected by the OSHI library
- */
- private static Collection presentFeatures;
- /**
- * serialize access to presentFeatures
- */
- private static Object synchronizeFeatures = new Object();
-
NativeVariant(final String property) {
this.property = property;
}
@@ -151,6 +142,15 @@ public static boolean isAndroid() {
* A namespace class exposing the CPU propositions.
*/
public static final class Cpu {
+ /**
+ * named CPU features that were detected by the OSHI library
+ */
+ private static Collection presentFeatures;
+ /**
+ * serialize access to presentFeatures
+ */
+ private static Object synchronizeFeatures = new Object();
+
private Cpu() {
}
From 7a9c74c90c958b1932b4f02d2f5d4bdbdb449fb7 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Mon, 3 Mar 2025 22:34:55 -0800
Subject: [PATCH 08/13] NativeVariant: readFeatureFlags() workaround for macOS
---
.../snaploader/platform/util/NativeVariant.java | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
index 597a0b2..07a10b8 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/NativeVariant.java
@@ -287,6 +287,13 @@ private static Collection readFeatureFlags() {
// Convert the list to a collection of feature names:
Collection result = new TreeSet<>();
for (String oshiString : oshiList) {
+ /*
+ * On macOS, strings ending with ": 0" indicate
+ * disabled features, so ignore all such lines.
+ */
+ if (oshiString.endsWith(": 0")) {
+ continue;
+ }
String lcString = oshiString.toLowerCase(Locale.ROOT);
Matcher matcher = pattern.matcher(lcString);
while (matcher.find()) {
From d7577e9191f2a4ccca58f03953751ff47a7a89bf Mon Sep 17 00:00:00 2001
From: stephengold
Date: Tue, 4 Mar 2025 13:30:06 -0800
Subject: [PATCH 09/13] PlatformPredicate: add a combining constructor
---
.../platform/util/PlatformPredicate.java | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PlatformPredicate.java b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PlatformPredicate.java
index 5e1c497..25de2c2 100644
--- a/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PlatformPredicate.java
+++ b/snaploader/src/main/java/electrostatic4j/snaploader/platform/util/PlatformPredicate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023-2024, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
+ * Copyright (c) 2023-2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -134,6 +134,19 @@ public PlatformPredicate(boolean predicate) {
this.predicate = predicate;
}
+ /**
+ * Instantiates a predicate object that combines a pre-existing predicate
+ * with one or more instruction-set extensions. The result is true if and
+ * only if the base predicate is true and all named extensions are present.
+ *
+ * @param base a pre-existing predicate (not null)
+ * @param isaExtensions names of required ISA extensions
+ */
+ public PlatformPredicate(PlatformPredicate base, String... isaExtensions) {
+ this.predicate = base.evaluatePredicate()
+ && NativeVariant.Cpu.hasExtensions(isaExtensions);
+ }
+
/**
* Evaluate the propositions of the predefined platform-predicate.
*
From 030916119074305d88bc31485f52d093111aece4 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Thu, 6 Mar 2025 19:42:17 -0800
Subject: [PATCH 10/13] make the OSHI dependency into a transitive one
---
snaploader/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/snaploader/build.gradle b/snaploader/build.gradle
index a752cc9..c5e1323 100644
--- a/snaploader/build.gradle
+++ b/snaploader/build.gradle
@@ -27,5 +27,5 @@ jar { // assemble jar options [java -jar]
}
dependencies {
- implementation('com.github.oshi:oshi-core:6.7.0')
+ api('com.github.oshi:oshi-core:6.7.0')
}
From cb518698e0f73fc66e154cf479bc8bf208150b0d Mon Sep 17 00:00:00 2001
From: stephengold
Date: Fri, 7 Mar 2025 11:59:35 -0800
Subject: [PATCH 11/13] snaploader-examples: added the TestCpuFeatures test
---
snaploader-examples/build.gradle | 7 ++
.../snaploader/examples/TestCpuFeatures.java | 111 ++++++++++++++++++
2 files changed, 118 insertions(+)
create mode 100644 snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCpuFeatures.java
diff --git a/snaploader-examples/build.gradle b/snaploader-examples/build.gradle
index d706d4e..4d9483b 100644
--- a/snaploader-examples/build.gradle
+++ b/snaploader-examples/build.gradle
@@ -86,4 +86,11 @@ task createJar(type : Jar, dependsOn : copyLibs){
dependencies {
implementation project(path: ':snaploader')
+ implementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.24.3'
+
+ implementation 'com.github.stephengold:jolt-jni-Linux64:0.9.7'
+ runtimeOnly 'com.github.stephengold:jolt-jni-Linux64:0.9.7:DebugSp'
+ runtimeOnly 'com.github.stephengold:jolt-jni-Linux64_fma:0.9.7:DebugSp'
+ runtimeOnly 'com.github.stephengold:jolt-jni-Windows64:0.9.7:DebugSp'
+ runtimeOnly 'com.github.stephengold:jolt-jni-Windows64_avx2:0.9.7:DebugSp'
}
\ No newline at end of file
diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCpuFeatures.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCpuFeatures.java
new file mode 100644
index 0000000..8fe894f
--- /dev/null
+++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCpuFeatures.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2025, The Electrostatic-Sandbox Distributed Simulation Framework, jSnapLoader
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'AvrSandbox' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package electrostatic4j.snaploader.examples;
+
+import com.github.stephengold.joltjni.Jolt;
+import electrostatic4j.snaploader.LibraryInfo;
+import electrostatic4j.snaploader.LoadingCriterion;
+import electrostatic4j.snaploader.NativeBinaryLoader;
+import electrostatic4j.snaploader.filesystem.DirectoryPath;
+import electrostatic4j.snaploader.platform.NativeDynamicLibrary;
+import electrostatic4j.snaploader.platform.util.NativeVariant;
+import electrostatic4j.snaploader.platform.util.PlatformPredicate;
+
+/**
+ * Tests selection between native libraries based on CPU features.
+ *
+ * @author Stephen Gold sgold@sonic.net
+ */
+public final class TestCpuFeatures {
+
+ public static void main(String[] argv) {
+ // Test for each of the relevant CPU features:
+ System.out.println("avx = " + NativeVariant.Cpu.hasExtensions("avx"));
+ System.out.println("avx2 = " + NativeVariant.Cpu.hasExtensions("avx2"));
+ System.out.println("bmi1 = " + NativeVariant.Cpu.hasExtensions("bmi1"));
+ System.out.println("f16c = " + NativeVariant.Cpu.hasExtensions("f16c"));
+ System.out.println("fma = " + NativeVariant.Cpu.hasExtensions("fma"));
+ System.out.println("sse4_1 = " + NativeVariant.Cpu.hasExtensions("sse4_1"));
+ System.out.println("sse4_2 = " + NativeVariant.Cpu.hasExtensions("sse4_2"));
+
+ // Define a custom predicate for Linux with all 7 CPU features:
+ PlatformPredicate linuxWithFma = new PlatformPredicate(
+ PlatformPredicate.LINUX_X86_64,
+ "avx", "avx2", "bmi1", "f16c", "fma", "sse4_1", "sse4_2");
+ System.out.println("linuxWithFma = " + linuxWithFma.evaluatePredicate());
+
+ // Define a custom predicate for Windows with 4 CPU features:
+ PlatformPredicate windowsWithAvx2 = new PlatformPredicate(
+ PlatformPredicate.WIN_X86_64,
+ "avx", "avx2", "sse4_1", "sse4_2");
+ System.out.println("windowsWithAvx2 = " + windowsWithAvx2.evaluatePredicate());
+ System.out.flush();
+
+ LibraryInfo info = new LibraryInfo(
+ new DirectoryPath("linux/x86-64/com/github/stephengold"),
+ "joltjni", DirectoryPath.USER_DIR);
+ NativeBinaryLoader loader = new NativeBinaryLoader(info);
+ NativeDynamicLibrary[] libraries = {
+ new NativeDynamicLibrary("linux/x86-64-fma/com/github/stephengold", linuxWithFma), // must precede vanilla LINUX_X86_64
+ new NativeDynamicLibrary("linux/x86-64/com/github/stephengold", PlatformPredicate.LINUX_X86_64),
+ new NativeDynamicLibrary("windows/x86-64-avx2/com/github/stephengold", windowsWithAvx2), // must precede vanilla WIN_X86_64
+ new NativeDynamicLibrary("windows/x86-64/com/github/stephengold", PlatformPredicate.WIN_X86_64)
+ };
+ loader.registerNativeLibraries(libraries).initPlatformLibrary();
+ loader.setLoggingEnabled(true);
+ loader.setRetryWithCleanExtraction(true);
+ try {
+ loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING);
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to load the joltjni library!");
+ }
+ System.err.flush();
+
+ // Invoke native code to obtain the configuration of the native library.
+ String configuration = Jolt.getConfigurationString();
+ /*
+ * Depending which native library was loaded, the configuration string
+ * should be one of the following:
+ *
+ * On LINUX_X86_64 platforms, either
+ * Single precision x86 64-bit with instructions: SSE2 SSE4.1 SSE4.2 AVX AVX2 F16C LZCNT TZCNT FMADD (Debug Renderer) (16-bit ObjectLayer) (Assertions) (ObjectStream) (Debug) (C++ RTTI) (C++ Exceptions)
+ * or
+ * Single precision x86 64-bit with instructions: SSE2 (Debug Renderer) (16-bit ObjectLayer) (Assertions) (ObjectStream) (Debug) (C++ RTTI) (C++ Exceptions)
+ *
+ * On WIN_X86_64 platforms, either
+ * Single precision x86 64-bit with instructions: SSE2 SSE4.1 SSE4.2 AVX AVX2 F16C LZCNT TZCNT (FP Exceptions) (Debug Renderer) (16-bit ObjectLayer) (Assertions) (ObjectStream) (Debug) (C++ RTTI) (C++ Exceptions)
+ * or
+ * Single precision x86 64-bit with instructions: SSE2 (FP Exceptions) (Debug Renderer) (16-bit ObjectLayer) (Assertions) (ObjectStream) (Debug) (C++ RTTI) (C++ Exceptions)
+ */
+ System.out.println(configuration);
+ }
+}
From 73c35a79c85c1ad5d9849ccb9af9104458a0e1ee Mon Sep 17 00:00:00 2001
From: stephengold
Date: Fri, 7 Mar 2025 12:08:56 -0800
Subject: [PATCH 12/13] snaploader-examples: register a Gradle task to run
TestCpuFeatures
---
snaploader-examples/build.gradle | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/snaploader-examples/build.gradle b/snaploader-examples/build.gradle
index 4d9483b..057c1b7 100644
--- a/snaploader-examples/build.gradle
+++ b/snaploader-examples/build.gradle
@@ -26,6 +26,12 @@ tasks.register("TestBasicFeatures2") {
application.mainClass = 'electrostatic4j.snaploader.examples.TestBasicFeatures2'
}
+tasks.register("TestCpuFeatures", JavaExec) {
+ classpath sourceSets.main.runtimeClasspath
+ description = 'Runs the TestCpuFeatures example app.'
+ mainClass = 'electrostatic4j.snaploader.examples.TestCpuFeatures'
+}
+
tasks.register("MonitorableExample") {
application.mainClass = 'electrostatic4j.snaploader.examples.MonitorableExample'
}
From 410229333c062a4f25e0fa0591075ea8c9cc31a7 Mon Sep 17 00:00:00 2001
From: stephengold
Date: Fri, 7 Mar 2025 14:49:50 -0800
Subject: [PATCH 13/13] TestCpuFeatures: specify CLEAN_EXTRACTION to
loadLibrary()
---
.../electrostatic4j/snaploader/examples/TestCpuFeatures.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCpuFeatures.java b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCpuFeatures.java
index 8fe894f..b80d953 100644
--- a/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCpuFeatures.java
+++ b/snaploader-examples/src/main/java/electrostatic4j/snaploader/examples/TestCpuFeatures.java
@@ -84,7 +84,7 @@ public static void main(String[] argv) {
loader.setLoggingEnabled(true);
loader.setRetryWithCleanExtraction(true);
try {
- loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING);
+ loader.loadLibrary(LoadingCriterion.CLEAN_EXTRACTION);
} catch (Exception e) {
throw new IllegalStateException("Failed to load the joltjni library!");
}