2021-02-01 14:31:20 +09:00
|
|
|
/*
|
|
|
|
* ******************************************************************************
|
|
|
|
* *
|
|
|
|
* *
|
|
|
|
* * 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.
|
|
|
|
* *
|
2021-02-01 17:47:29 +09:00
|
|
|
* * See the NOTICE file distributed with this work for additional
|
|
|
|
* * information regarding copyright ownership.
|
2021-02-01 14:31:20 +09:00
|
|
|
* * 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
|
|
|
|
* *****************************************************************************
|
|
|
|
*/
|
2019-06-06 15:21:15 +03:00
|
|
|
|
|
|
|
package org.nd4j.linalg.factory;
|
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
2020-10-05 19:25:01 -07:00
|
|
|
import org.nd4j.common.config.ND4JClassLoading;
|
2020-04-29 11:19:26 +10:00
|
|
|
import org.nd4j.common.config.ND4JEnvironmentVars;
|
|
|
|
import org.nd4j.common.config.ND4JSystemProperties;
|
2019-06-06 15:21:15 +03:00
|
|
|
import org.nd4j.context.Nd4jContext;
|
2020-04-29 11:19:26 +10:00
|
|
|
import org.nd4j.common.io.Resource;
|
2019-06-06 15:21:15 +03:00
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
2020-10-05 19:25:01 -07:00
|
|
|
import java.net.URLClassLoader;
|
2019-06-06 15:21:15 +03:00
|
|
|
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;
|
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
|
2020-01-04 13:45:07 +11:00
|
|
|
public abstract Environment getEnvironment();
|
|
|
|
|
2021-02-01 14:31:20 +09:00
|
|
|
/**
|
|
|
|
* Get the build information of the backend
|
|
|
|
*/
|
|
|
|
public abstract String buildInfo();
|
2019-06-06 15:21:15 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads the best available backend.
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
public static Nd4jBackend load() throws NoAvailableBackendException {
|
|
|
|
|
2019-10-26 12:38:08 +11:00
|
|
|
String logInitProperty = System.getProperty(ND4JSystemProperties.LOG_INITIALIZATION, "true");
|
|
|
|
boolean logInit = Boolean.parseBoolean(logInitProperty);
|
|
|
|
|
2020-10-05 19:25:01 -07:00
|
|
|
List<Nd4jBackend> backends = new ArrayList<>();
|
|
|
|
ServiceLoader<Nd4jBackend> loader = ND4JClassLoading.loadService(Nd4jBackend.class);
|
2019-06-06 15:21:15 +03:00
|
|
|
try {
|
2020-10-05 19:25:01 -07:00
|
|
|
for (Nd4jBackend nd4jBackend : loader) {
|
|
|
|
backends.add(nd4jBackend);
|
|
|
|
}
|
2019-06-06 15:21:15 +03:00
|
|
|
} catch (ServiceConfigurationError serviceError) {
|
|
|
|
// a fatal error due to a syntax or provider construction error.
|
|
|
|
// backends mustn't throw an exception during construction.
|
|
|
|
throw new RuntimeException("failed to process available backends", serviceError);
|
|
|
|
}
|
|
|
|
|
|
|
|
Collections.sort(backends, new Comparator<Nd4jBackend>() {
|
|
|
|
@Override
|
|
|
|
public int compare(Nd4jBackend o1, Nd4jBackend 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) {
|
2019-10-26 12:38:08 +11:00
|
|
|
if(logInit) {
|
|
|
|
log.warn("Skipped [{}] backend (unavailable): {}", backend.getClass().getSimpleName(), error);
|
|
|
|
}
|
2019-06-06 15:21:15 +03:00
|
|
|
continue;
|
2022-09-20 15:40:53 +02:00
|
|
|
} else {
|
|
|
|
log.info("Loaded [{}] backend.", backend.getClass().getSimpleName());
|
2019-06-06 15:21:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
Nd4jContext.getInstance().updateProperties(backend.getConfigurationResource().getInputStream());
|
|
|
|
} catch (IOException e) {
|
2020-04-23 01:36:49 +03:00
|
|
|
log.error("",e);
|
2019-06-06 15:21:15 +03:00
|
|
|
}
|
|
|
|
|
2019-10-26 12:38:08 +11:00
|
|
|
if(logInit) {
|
|
|
|
log.info("Loaded [{}] backend", backend.getClass().getSimpleName());
|
|
|
|
}
|
2019-06-06 15:21:15 +03:00
|
|
|
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(
|
2020-05-12 14:52:48 +10:00
|
|
|
"Please ensure that you have an nd4j backend on your classpath. Please see: https://deeplearning4j.konduit.ai/nd4j/backend");
|
2019-06-06 15:21:15 +03:00
|
|
|
|
|
|
|
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*/
|
2020-10-05 19:25:01 -07:00
|
|
|
java.net.URLClassLoader loader = (URLClassLoader) ND4JClassLoading.getNd4jClassloader();
|
2019-06-06 15:21:15 +03:00
|
|
|
java.net.URL url = jar.toURI().toURL();
|
|
|
|
/*Disallow if already loaded*/
|
2022-10-21 15:19:32 +02:00
|
|
|
for (java.net.URL it : loader.getURLs()) {
|
2019-06-06 15:21:15 +03:00
|
|
|
if (it.equals(url)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
java.lang.reflect.Method method =
|
2022-10-21 15:19:32 +02:00
|
|
|
java.net.URLClassLoader.class.getDeclaredMethod("addURL", java.net.URL.class);
|
2019-06-06 15:21:15 +03:00
|
|
|
method.setAccessible(true); /*promote the method to public access*/
|
2022-10-21 15:19:32 +02:00
|
|
|
method.invoke(loader, url);
|
2019-06-06 15:21:15 +03:00
|
|
|
} catch (final java.lang.NoSuchMethodException | java.lang.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();
|
|
|
|
}
|
|
|
|
|
2019-10-26 12:38:08 +11:00
|
|
|
public abstract void logBackendInit();
|
|
|
|
|
2019-06-06 15:21:15 +03:00
|
|
|
|
|
|
|
@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
|
2022-10-21 15:19:32 +02:00
|
|
|
* message of {@code (cause==null ? null : cause.toString())} (which
|
|
|
|
* typically contains the class and detail message of cause).
|
2019-06-06 15:21:15 +03:00
|
|
|
* 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
|
2022-10-21 15:19:32 +02:00
|
|
|
* {@link #getCause()} method). (A null value is
|
2019-06-06 15:21:15 +03:00
|
|
|
* permitted, and indicates that the cause is nonexistent or
|
|
|
|
* unknown.)
|
|
|
|
* @since 1.4
|
|
|
|
*/
|
|
|
|
public NoAvailableBackendException(Throwable cause) {
|
|
|
|
super(cause);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|