You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@systemml.apache.org by ni...@apache.org on 2017/11/16 22:13:28 UTC
systemml git commit: [SYSTEMML-1630] Allow setting and resetting of
sysml.native.blas property for different execution
Repository: systemml
Updated Branches:
refs/heads/master 7dc61c05b -> fe9b023c7
[SYSTEMML-1630] Allow setting and resetting of sysml.native.blas
property for different execution
Project: http://git-wip-us.apache.org/repos/asf/systemml/repo
Commit: http://git-wip-us.apache.org/repos/asf/systemml/commit/fe9b023c
Tree: http://git-wip-us.apache.org/repos/asf/systemml/tree/fe9b023c
Diff: http://git-wip-us.apache.org/repos/asf/systemml/diff/fe9b023c
Branch: refs/heads/master
Commit: fe9b023c750939f5a3e8e74cbc8202aca9b14fe9
Parents: 7dc61c0
Author: Niketan Pansare <np...@us.ibm.com>
Authored: Thu Nov 16 14:12:29 2017 -0800
Committer: Niketan Pansare <np...@us.ibm.com>
Committed: Thu Nov 16 14:12:29 2017 -0800
----------------------------------------------------------------------
.../apache/sysml/api/ScriptExecutorUtils.java | 8 +-
.../org/apache/sysml/utils/NativeHelper.java | 306 +++++++++++--------
.../java/org/apache/sysml/utils/Statistics.java | 4 +-
3 files changed, 187 insertions(+), 131 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/systemml/blob/fe9b023c/src/main/java/org/apache/sysml/api/ScriptExecutorUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/api/ScriptExecutorUtils.java b/src/main/java/org/apache/sysml/api/ScriptExecutorUtils.java
index d69a863..253a317 100644
--- a/src/main/java/org/apache/sysml/api/ScriptExecutorUtils.java
+++ b/src/main/java/org/apache/sysml/api/ScriptExecutorUtils.java
@@ -77,12 +77,8 @@ public class ScriptExecutorUtils {
DMLScript.FINEGRAINED_STATISTICS = DMLScript.STATISTICS && dmlconf.getBooleanValue(DMLConfig.EXTRA_FINEGRAINED_STATS);
DMLScript.SYNCHRONIZE_GPU = dmlconf.getBooleanValue(DMLConfig.SYNCHRONIZE_GPU);
DMLScript.EAGER_CUDA_FREE = dmlconf.getBooleanValue(DMLConfig.EAGER_CUDA_FREE);
- DMLScript.STATISTICS_MAX_WRAP_LEN = dmlconf.getIntValue(DMLConfig.STATS_MAX_WRAP_LEN);
-
- String customLibPath = dmlconf.getTextValue(DMLConfig.NATIVE_BLAS_DIR);
- if(!customLibPath.equalsIgnoreCase("none")) {
- NativeHelper.initializeCustomBLAS(customLibPath, dmlconf.getTextValue(DMLConfig.NATIVE_BLAS));
- }
+ DMLScript.STATISTICS_MAX_WRAP_LEN = dmlconf.getIntValue(DMLConfig.STATS_MAX_WRAP_LEN);
+ NativeHelper.initialize(dmlconf.getTextValue(DMLConfig.NATIVE_BLAS_DIR), dmlconf.getTextValue(DMLConfig.NATIVE_BLAS).trim());
if(DMLScript.USE_ACCELERATOR) {
DMLScript.FLOATING_POINT_PRECISION = dmlconf.getTextValue(DMLConfig.FLOATING_POINT_PRECISION);
http://git-wip-us.apache.org/repos/asf/systemml/blob/fe9b023c/src/main/java/org/apache/sysml/utils/NativeHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/utils/NativeHelper.java b/src/main/java/org/apache/sysml/utils/NativeHelper.java
index c9c2e08..db8e74b 100644
--- a/src/main/java/org/apache/sysml/utils/NativeHelper.java
+++ b/src/main/java/org/apache/sysml/utils/NativeHelper.java
@@ -24,7 +24,6 @@ import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import java.util.HashMap;
import java.util.Vector;
import java.io.InputStream;
import java.io.OutputStream;
@@ -43,141 +42,207 @@ import org.apache.sysml.runtime.DMLRuntimeException;
* By default, it first tries to load Intel MKL, else tries to load OpenBLAS.
*/
public class NativeHelper {
- private static boolean isSystemMLLoaded = false;
+
+ public static enum NativeBlasState {
+ NOT_ATTEMPTED_LOADING_NATIVE_BLAS,
+ SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_IN_USE,
+ SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_NOT_IN_USE,
+ ATTEMPTED_LOADING_NATIVE_BLAS_UNSUCCESSFULLY
+ };
+
+ public static NativeBlasState CURRENT_NATIVE_BLAS_STATE = NativeBlasState.NOT_ATTEMPTED_LOADING_NATIVE_BLAS;
+ private static String blasType;
private static final Log LOG = LogFactory.getLog(NativeHelper.class.getName());
- private static HashMap<String, String> supportedArchitectures = new HashMap<>();
- public static String blasType;
+
+ // Useful for deciding whether to use native BLAS in parfor environment.
private static int maxNumThreads = -1;
private static boolean setMaxNumThreads = false;
- static {
- // Note: we only support 64 bit Java on x86 and AMD machine
- supportedArchitectures.put("x86_64", "x86_64");
- supportedArchitectures.put("amd64", "x86_64");
+
+ /**
+ * Called by Statistics to print the loaded BLAS.
+ *
+ * @return empty string or the BLAS that is loaded
+ */
+ public static String getCurrentBLAS() {
+ return blasType != null ? blasType : "";
}
-
- private static boolean attemptedLoading = false;
-
- private static String hintOnFailures = "";
-
- public static void initializeCustomBLAS(String customLibPath, String userSpecifiedBLAS) throws DMLRuntimeException {
- if(attemptedLoading && blasType != null && isSupportedBLAS(userSpecifiedBLAS) && !blasType.equalsIgnoreCase(userSpecifiedBLAS) ) {
+
+ /**
+ * Called by runtime to check if the BLAS is available for exploitation
+ *
+ * @return true if CURRENT_NATIVE_BLAS_STATE is SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_NOT_IN_USE else false
+ */
+ public static boolean isNativeLibraryLoaded() {
+ if(!isBLASLoaded()) {
+ DMLConfig dmlConfig = ConfigurationManager.getDMLConfig();
+ String userSpecifiedBLAS = (dmlConfig == null) ? "auto" : dmlConfig.getTextValue(DMLConfig.NATIVE_BLAS).trim().toLowerCase();
+ String customLibPath = (dmlConfig == null) ? "none" : dmlConfig.getTextValue(DMLConfig.NATIVE_BLAS_DIR).trim().toLowerCase();
+ performLoading(customLibPath, userSpecifiedBLAS);
+ }
+ if(maxNumThreads == -1)
+ maxNumThreads = OptimizerUtils.getConstrainedNumThreads(-1);
+ if(CURRENT_NATIVE_BLAS_STATE == NativeBlasState.SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_IN_USE && !setMaxNumThreads && maxNumThreads != -1) {
+ // This method helps us decide whether to use GetPrimitiveArrayCritical or GetDoubleArrayElements in JNI as each has different tradeoffs.
+ // In current implementation, we always use GetPrimitiveArrayCritical as it has proven to be fastest.
+ // We can revisit this decision later and hence I would not recommend removing this method.
+ setMaxNumThreads(maxNumThreads);
+ setMaxNumThreads = true;
+ }
+ return CURRENT_NATIVE_BLAS_STATE == NativeBlasState.SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_IN_USE;
+ }
+
+ /**
+ * Initialize the native library before executing the DML program
+ *
+ * @param customLibPath specified by sysml.native.blas.directory
+ * @param userSpecifiedBLAS specified by sysml.native.blas
+ * @throws DMLRuntimeException if error
+ */
+ public static void initialize(String customLibPath, String userSpecifiedBLAS) throws DMLRuntimeException {
+ if(isBLASLoaded() && isSupportedBLAS(userSpecifiedBLAS) && !blasType.equalsIgnoreCase(userSpecifiedBLAS)) {
throw new DMLRuntimeException("Cannot replace previously loaded blas \"" + blasType + "\" with \"" + userSpecifiedBLAS + "\".");
}
- else {
- init(customLibPath, userSpecifiedBLAS);
+ else if(isBLASLoaded() && userSpecifiedBLAS.equalsIgnoreCase("none")) {
+ CURRENT_NATIVE_BLAS_STATE = NativeBlasState.SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_NOT_IN_USE;
+ }
+ else if(isBLASLoaded() && userSpecifiedBLAS.equalsIgnoreCase(blasType)) {
+ CURRENT_NATIVE_BLAS_STATE = NativeBlasState.SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_IN_USE;
+ }
+ else if(!isBLASLoaded() && isSupportedBLAS(userSpecifiedBLAS)) {
+ performLoading(customLibPath, userSpecifiedBLAS);
}
}
+ /**
+ * Return true if the given BLAS type is supported.
+ *
+ * @param userSpecifiedBLAS BLAS type specified via sysml.native.blas property
+ * @return true if the userSpecifiedBLAS is auto | mkl | openblas, else false
+ */
private static boolean isSupportedBLAS(String userSpecifiedBLAS) {
- return userSpecifiedBLAS.equalsIgnoreCase("auto") || userSpecifiedBLAS.equalsIgnoreCase("mkl") || userSpecifiedBLAS.equalsIgnoreCase("openblas");
+ return userSpecifiedBLAS.equalsIgnoreCase("auto") ||
+ userSpecifiedBLAS.equalsIgnoreCase("mkl") ||
+ userSpecifiedBLAS.equalsIgnoreCase("openblas");
+ }
+
+ /**
+ * Note: we only support 64 bit Java on x86 and AMD machine
+ *
+ * @return true if the hardware architecture is supported
+ */
+ private static boolean isSupportedArchitecture() {
+ if(SystemUtils.OS_ARCH.equals("x86_64") || SystemUtils.OS_ARCH.equals("amd64")) {
+ return true;
+ }
+ LOG.info("Unsupported architecture for native BLAS:" + SystemUtils.OS_ARCH);
+ return false;
+ }
+
+ /**
+ * Check if native BLAS libraries have been successfully loaded
+ * @return true if CURRENT_NATIVE_BLAS_STATE is SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_IN_USE or SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_NOT_IN_USE
+ */
+ private static boolean isBLASLoaded() {
+ return CURRENT_NATIVE_BLAS_STATE == NativeBlasState.SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_IN_USE ||
+ CURRENT_NATIVE_BLAS_STATE == NativeBlasState.SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_NOT_IN_USE;
+ }
+
+ /**
+ * Check if we should attempt to perform loading.
+ * If custom library path is provided, we should attempt to load again if not already loaded.
+ *
+ * @param customLibPath custom library path
+ * @return true if we should attempt to load blas again
+ */
+ private static boolean shouldReload(String customLibPath) {
+ boolean isValidBLASDirectory = customLibPath != null && !customLibPath.equalsIgnoreCase("none");
+ return CURRENT_NATIVE_BLAS_STATE == NativeBlasState.NOT_ATTEMPTED_LOADING_NATIVE_BLAS ||
+ (isValidBLASDirectory && !isBLASLoaded());
}
// Performing loading in a method instead of a static block will throw a detailed stack trace in case of fatal errors
- private static void init(String customLibPath, String userSpecifiedBLAS) {
+ private static void performLoading(String customLibPath, String userSpecifiedBLAS) {
// Only Linux supported for BLAS
if(!SystemUtils.IS_OS_LINUX)
return;
// attemptedLoading variable ensures that we don't try to load SystemML and other dependencies
// again and again especially in the parfor (hence the double-checking with synchronized).
- if(!attemptedLoading || customLibPath != null) {
- // -------------------------------------------------------------------------------------
- if(userSpecifiedBLAS == null) {
- DMLConfig dmlConfig = ConfigurationManager.getDMLConfig();
- userSpecifiedBLAS = (dmlConfig == null) ? "auto" : dmlConfig.getTextValue(DMLConfig.NATIVE_BLAS).trim().toLowerCase();
- }
- if(isSupportedBLAS(userSpecifiedBLAS)) {
- long start = System.nanoTime();
- if(!supportedArchitectures.containsKey(SystemUtils.OS_ARCH)) {
- LOG.info("Unsupported architecture for native BLAS:" + SystemUtils.OS_ARCH);
- return;
- }
- synchronized(NativeHelper.class) {
- if(!attemptedLoading || customLibPath != null) {
- // -----------------------------------------------------------------------------
- // =============================================================================
- // By default, we will native.blas=true and we will attempt to load MKL first.
- // If MKL is not enabled then we try to load OpenBLAS.
- // If both MKL and OpenBLAS are not available we fall back to Java BLAS.
- if(userSpecifiedBLAS.equalsIgnoreCase("auto")) {
- blasType = isMKLAvailable(customLibPath) ? "mkl" : isOpenBLASAvailable(customLibPath) ? "openblas" : null;
- if(blasType == null)
- LOG.info("Unable to load either MKL or OpenBLAS due to " + hintOnFailures);
- }
- else if(userSpecifiedBLAS.equalsIgnoreCase("mkl")) {
- blasType = isMKLAvailable(customLibPath) ? "mkl" : null;
- if(blasType == null)
- LOG.info("Unable to load MKL due to " + hintOnFailures);
- }
- else if(userSpecifiedBLAS.equalsIgnoreCase("openblas")) {
- blasType = isOpenBLASAvailable(customLibPath) ? "openblas" : null;
- if(blasType == null)
- LOG.info("Unable to load OpenBLAS due to " + hintOnFailures);
- }
- else {
- // Only thrown at development time.
- throw new RuntimeException("Unsupported BLAS:" + userSpecifiedBLAS);
- }
- // =============================================================================
- if(blasType != null && loadLibraryHelper("libsystemml_" + blasType + "-Linux-x86_64.so")) {
- String blasPathAndHint = "";
- // ------------------------------------------------------------
- // This logic gets the list of native libraries that are loaded
- if(LOG.isDebugEnabled()) {
- // Only perform the checking of library paths when DEBUG is enabled to avoid runtime overhead.
- try {
- java.lang.reflect.Field loadedLibraryNamesField = ClassLoader.class.getDeclaredField("loadedLibraryNames");
- loadedLibraryNamesField.setAccessible(true);
- @SuppressWarnings("unchecked")
- Vector<String> libraries = (Vector<String>) loadedLibraryNamesField.get(ClassLoader.getSystemClassLoader());
- LOG.debug("List of native libraries loaded:" + libraries);
- for(String library : libraries) {
- if(library.contains("libmkl_rt") || library.contains("libopenblas")) {
- blasPathAndHint = " from the path " + library;
- break;
- }
- }
- } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
- LOG.debug("Error while finding list of native libraries:" + e.getMessage());
- }
- }
- // ------------------------------------------------------------
-
- LOG.info("Using native blas: " + blasType + blasPathAndHint);
- isSystemMLLoaded = true;
- }
+ if(shouldReload(customLibPath) && isSupportedBLAS(userSpecifiedBLAS) && isSupportedArchitecture()) {
+ long start = System.nanoTime();
+ synchronized(NativeHelper.class) {
+ if(shouldReload(customLibPath)) {
+ // Set attempted loading unsuccessful in case of exception
+ CURRENT_NATIVE_BLAS_STATE = NativeBlasState.ATTEMPTED_LOADING_NATIVE_BLAS_UNSUCCESSFULLY;
+ String [] blas = new String[] { userSpecifiedBLAS };
+ if(userSpecifiedBLAS.equalsIgnoreCase("auto")) {
+ blas = new String[] { "mkl", "openblas" };
+ }
+ if(checkAndLoadBLAS(customLibPath, blas) && loadLibraryHelper("libsystemml_" + blasType + "-Linux-x86_64.so")) {
+ LOG.info("Using native blas: " + blasType + getNativeBLASPath());
+ CURRENT_NATIVE_BLAS_STATE = NativeBlasState.SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_IN_USE;
}
}
- double timeToLoadInMilliseconds = (System.nanoTime()-start)*1e-6;
- if(timeToLoadInMilliseconds > 1000)
- LOG.warn("Time to load native blas: " + timeToLoadInMilliseconds + " milliseconds.");
}
- else {
- LOG.debug("Using internal Java BLAS as native BLAS support the configuration 'sysml.native.blas'=" + userSpecifiedBLAS + ".");
+ double timeToLoadInMilliseconds = (System.nanoTime()-start)*1e-6;
+ if(timeToLoadInMilliseconds > 1000)
+ LOG.warn("Time to load native blas: " + timeToLoadInMilliseconds + " milliseconds.");
+ }
+ else if(LOG.isDebugEnabled() && !isSupportedBLAS(userSpecifiedBLAS)) {
+ LOG.debug("Using internal Java BLAS as native BLAS support the configuration 'sysml.native.blas'=" + userSpecifiedBLAS + ".");
+ }
+ }
+
+ private static boolean checkAndLoadBLAS(String customLibPath, String [] listBLAS) {
+ if(customLibPath != null && customLibPath.equalsIgnoreCase("none"))
+ customLibPath = null;
+
+ boolean isLoaded = false;
+ for(int i = 0; i < listBLAS.length; i++) {
+ String blas = listBLAS[i];
+ if(blas.equalsIgnoreCase("mkl")) {
+ isLoaded = loadBLAS(customLibPath, "mkl_rt", null);
+ }
+ else if(blas.equalsIgnoreCase("openblas")) {
+ boolean isGompLoaded = loadBLAS(customLibPath, "gomp", "gomp required for loading OpenBLAS-enabled SystemML library");
+ if(isGompLoaded) {
+ isLoaded = loadBLAS(customLibPath, "openblas", null);
+ }
+ }
+ if(isLoaded) {
+ blasType = blas;
+ break;
}
- attemptedLoading = true;
}
+ return isLoaded;
}
-
- public static boolean isNativeLibraryLoaded() {
- // We allow BLAS to be enabled or disabled or explicitly selected in one of the two ways:
- // 1. DML Configuration: native.blas (boolean flag)
- // 2. Environment variable: SYSTEMML_BLAS (can be set to mkl, openblas or none)
- // The option 1 will be removed in later SystemML versions.
- // The option 2 is useful for two reasons:
- // - Developer testing of different BLAS
- // - Provides fine-grained control. Certain machines could use mkl while others use openblas, etc.
- init(null, null);
- if(maxNumThreads == -1)
- maxNumThreads = OptimizerUtils.getConstrainedNumThreads(-1);
- if(isSystemMLLoaded && !setMaxNumThreads && maxNumThreads != -1) {
- // This method helps us decide whether to use GetPrimitiveArrayCritical or GetDoubleArrayElements in JNI as each has different tradeoffs.
- // In current implementation, we always use GetPrimitiveArrayCritical as it has proven to be fastest.
- // We can revisit this decision later and hence I would not recommend removing this method.
- setMaxNumThreads(maxNumThreads);
- setMaxNumThreads = true;
+
+ /**
+ * Useful method for debugging.
+ *
+ * @return empty string (if !LOG.isDebugEnabled()) or the path from where openblas or mkl is loaded.
+ */
+ private static String getNativeBLASPath() {
+ String blasPathAndHint = "";
+ if(LOG.isDebugEnabled()) {
+ // Only perform the checking of library paths when DEBUG is enabled to avoid runtime overhead.
+ try {
+ java.lang.reflect.Field loadedLibraryNamesField = ClassLoader.class.getDeclaredField("loadedLibraryNames");
+ loadedLibraryNamesField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Vector<String> libraries = (Vector<String>) loadedLibraryNamesField.get(ClassLoader.getSystemClassLoader());
+ LOG.debug("List of native libraries loaded:" + libraries);
+ for(String library : libraries) {
+ if(library.contains("libmkl_rt") || library.contains("libopenblas")) {
+ blasPathAndHint = " from the path " + library;
+ break;
+ }
+ }
+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
+ LOG.debug("Error while finding list of native libraries:" + e.getMessage());
+ }
}
- return isSystemMLLoaded;
+ return blasPathAndHint;
}
public static int getMaxNumThreads() {
@@ -186,17 +251,14 @@ public class NativeHelper {
return maxNumThreads;
}
-
- private static boolean isMKLAvailable(String customLibPath) {
- return loadBLAS(customLibPath, "mkl_rt", null);
- }
-
- private static boolean isOpenBLASAvailable(String customLibPath) {
- if(!loadBLAS(customLibPath, "gomp", "gomp required for loading OpenBLAS-enabled SystemML library"))
- return false;
- return loadBLAS(customLibPath, "openblas", null);
- }
-
+ /**
+ * Attempts to load native BLAS
+ *
+ * @param customLibPath can be null (if we want to only want to use LD_LIBRARY_PATH), else the
+ * @param blas can be gomp, openblas or mkl_rt
+ * @param optionalMsg message for debugging
+ * @return true if successfully loaded BLAS
+ */
private static boolean loadBLAS(String customLibPath, String blas, String optionalMsg) {
// First attempt to load from custom library path
if(customLibPath != null) {
@@ -219,8 +281,6 @@ public class NativeHelper {
return true;
}
catch (UnsatisfiedLinkError e) {
- if(!hintOnFailures.contains(blas))
- hintOnFailures = hintOnFailures + blas + " ";
if(optionalMsg != null)
LOG.debug("Unable to load " + blas + "(" + optionalMsg + "):" + e.getMessage());
else
http://git-wip-us.apache.org/repos/asf/systemml/blob/fe9b023c/src/main/java/org/apache/sysml/utils/Statistics.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/sysml/utils/Statistics.java b/src/main/java/org/apache/sysml/utils/Statistics.java
index 44bb232..762c167 100644
--- a/src/main/java/org/apache/sysml/utils/Statistics.java
+++ b/src/main/java/org/apache/sysml/utils/Statistics.java
@@ -774,8 +774,8 @@ public class Statistics
//show extended caching/compilation statistics
if( DMLScript.STATISTICS )
{
- if(NativeHelper.blasType != null) {
- String blas = NativeHelper.blasType != null ? NativeHelper.blasType : "";
+ if(NativeHelper.CURRENT_NATIVE_BLAS_STATE == NativeHelper.NativeBlasState.SUCCESSFULLY_LOADED_NATIVE_BLAS_AND_IN_USE) {
+ String blas = NativeHelper.getCurrentBLAS();
sb.append("Native " + blas + " calls (dense mult/conv/bwdF/bwdD):\t" + numNativeLibMatrixMultCalls.longValue() + "/" +
numNativeConv2dCalls.longValue() + "/" + numNativeConv2dBwdFilterCalls.longValue()
+ "/" + numNativeConv2dBwdDataCalls.longValue() + ".\n");