org.nd4j.linalg.factory.Nd4jBackend Maven / Gradle / Ivy
The newest version!
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* * under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
package org.nd4j.linalg.factory;
import lombok.extern.slf4j.Slf4j;
import org.nd4j.common.config.ND4JClassLoading;
import org.nd4j.common.config.ND4JEnvironmentVars;
import org.nd4j.common.config.ND4JSystemProperties;
import org.nd4j.context.Nd4jContext;
import org.nd4j.common.io.Resource;
import java.io.File;
import java.io.IOException;
import java.net.URLClassLoader;
import java.security.PrivilegedActionException;
import java.util.*;
@Slf4j
public abstract class Nd4jBackend {
public static final int BACKEND_PRIORITY_CPU;
public static final int BACKEND_PRIORITY_GPU;
public static final int BACKEND_PRIORITY_AURORA;
/**
* @deprecated Use {@link ND4JEnvironmentVars#BACKEND_DYNAMIC_LOAD_CLASSPATH}
*/
@Deprecated
public final static String DYNAMIC_LOAD_CLASSPATH = ND4JEnvironmentVars.BACKEND_DYNAMIC_LOAD_CLASSPATH;
/**
* @deprecated Use {@link ND4JSystemProperties#DYNAMIC_LOAD_CLASSPATH_PROPERTY}
*/
@Deprecated
public final static String DYNAMIC_LOAD_CLASSPATH_PROPERTY = ND4JSystemProperties.DYNAMIC_LOAD_CLASSPATH_PROPERTY;
private static boolean triedDynamicLoad = false;
static {
int n = 0;
String s = System.getenv(ND4JEnvironmentVars.BACKEND_PRIORITY_CPU);
if (s != null && s.length() > 0) {
try {
n = Integer.parseInt(s);
} catch (NumberFormatException e) {
throw new RuntimeException(e);
}
}
BACKEND_PRIORITY_CPU = n;
}
static {
int n = 100;
String s = System.getenv(ND4JEnvironmentVars.BACKEND_PRIORITY_GPU);
if (s != null && s.length() > 0) {
try {
n = Integer.parseInt(s);
} catch (NumberFormatException e) {
throw new RuntimeException(e);
}
}
BACKEND_PRIORITY_GPU = n;
}
static {
int n = 100;
String s = System.getenv(ND4JEnvironmentVars.BACKEND_PRIORITY_AURORA);
if (s != null && s.length() > 0) {
try {
n = Integer.parseInt(s);
} catch (NumberFormatException e) {
throw new RuntimeException(e);
}
}
BACKEND_PRIORITY_AURORA = n;
}
/**
* Returns true if the
* backend allows order to be specified
* on blas operations (cblas)
* @return true if the backend allows
* order to be specified on blas operations
*/
public abstract boolean allowsOrder();
/**
* Gets a priority number for the backend.
*
* Backends are loaded in priority order (highest first).
* @return a priority number.
*/
public abstract int getPriority();
/**
* Determines whether a given backend is available in the current environment.
* @return true if the backend is available; false otherwise.
*/
public abstract boolean isAvailable();
/**
* Returns true if the backend can
* run on the os or not
* @return
*/
public abstract boolean canRun();
/**
* Get the configuration resource
* @return
*/
public abstract Resource getConfigurationResource();
/**
* Get the actual (concrete/implementation) class for standard INDArrays for this backend
*/
public abstract Class getNDArrayClass();
public abstract Environment getEnvironment();
/**
* Get the build information of the backend
*/
public abstract String buildInfo();
/**
* Loads the best available backend.
* @return
*/
public static Nd4jBackend load() throws NoAvailableBackendException {
String logInitProperty = System.getProperty(ND4JSystemProperties.LOG_INITIALIZATION, "true");
boolean logInit = Boolean.parseBoolean(logInitProperty);
List backends = new ArrayList<>();
ServiceLoader loader = ND4JClassLoading.loadService(Nd4jBackend.class);
try {
for (Nd4jBackend nd4jBackend : loader) {
backends.add(nd4jBackend);
}
} catch (ServiceConfigurationError serviceError) {
// a fatal error due to a syntax or provider construction error.
// backends mustn't throw an exception during construction.
log.warn("failed to process available backends", serviceError);
return null;
}
Collections.sort(backends, (o1, o2) -> {
// high-priority first
return o2.getPriority() - o1.getPriority();
});
for (Nd4jBackend backend : backends) {
boolean available = false;
String error = null;
try {
available = backend.isAvailable();
} catch (Exception e) {
error = e.getMessage();
}
if (!available) {
if(logInit) {
log.warn("Skipped [{}] backend (unavailable): {}", backend.getClass().getSimpleName(), error);
}
continue;
}
try {
Nd4jContext.getInstance().updateProperties(backend.getConfigurationResource().getInputStream());
} catch (IOException e) {
log.error("",e);
}
if(logInit) {
log.info("Loaded [{}] backend", backend.getClass().getSimpleName());
}
return backend;
}
//need to dynamically load jars and recall, note that we do this right before the backend loads.
//An existing backend should take precedence over
//ones being dynamically discovered.
//Note that we prioritize jvm properties first, followed by environment variables.
String[] jarUris;
if (System.getProperties().containsKey(ND4JSystemProperties.DYNAMIC_LOAD_CLASSPATH_PROPERTY) && !triedDynamicLoad) {
jarUris = System.getProperties().getProperty(ND4JSystemProperties.DYNAMIC_LOAD_CLASSPATH_PROPERTY).split(";");
// Do not call System.getenv(): Accessing all variables requires higher security privileges
} else if (System.getenv(ND4JEnvironmentVars.BACKEND_DYNAMIC_LOAD_CLASSPATH) != null && !triedDynamicLoad) {
jarUris = System.getenv(ND4JEnvironmentVars.BACKEND_DYNAMIC_LOAD_CLASSPATH).split(";");
}
else
throw new NoAvailableBackendException(
"Please ensure that you have an nd4j backend on your classpath. Please see: https://deeplearning4j.konduit.ai/nd4j/backend");
triedDynamicLoad = true;
//load all the discoverable uris and try to load the backend again
for (String uri : jarUris) {
loadLibrary(new File(uri));
}
return load();
}
/**
* Adds the supplied Java Archive library to java.class.path. This is benign
* if the library is already loaded.
* @param jar the jar file to add
* @throws NoAvailableBackendException
*/
public static synchronized void loadLibrary(File jar) throws NoAvailableBackendException {
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
URLClassLoader loader = (URLClassLoader) ND4JClassLoading.getNd4jClassloader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : Arrays.asList(loader.getURLs())) {
if (it.equals(url)) {
return;
}
}
java.lang.reflect.Method method =
URLClassLoader.class.getDeclaredMethod("addURL", new Class[] {java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[] {url});
} catch (final NoSuchMethodException | IllegalAccessException
| java.net.MalformedURLException | java.lang.reflect.InvocationTargetException e) {
throw new NoAvailableBackendException(e);
}
}
/**
*
* @return
* @throws IOException
*/
public Properties getProperties() throws IOException {
return getContext().getConf();
}
/**
*
* @return
* @throws IOException
*/
public Nd4jContext getContext() throws IOException {
return Nd4jContext.getInstance();
}
@Override
public String toString() {
return getClass().getName();
}
public abstract void logBackendInit();
@SuppressWarnings("serial")
public static class NoAvailableBackendException extends Exception {
public NoAvailableBackendException(String s) {
super(s);
}
/**
* Constructs a new exception with the specified cause and a detail
* message of (cause==null ? null : cause.toString()) (which
* typically contains the class and detail message of cause).
* This constructor is useful for exceptions that are little more than
* wrappers for other throwables (for example, {@link
* PrivilegedActionException}).
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A null value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public NoAvailableBackendException(Throwable cause) {
super(cause);
}
}
}