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");