chore: import upstream snapshot with attribution
Lint / lint (push) Has been cancelled
CI / MacOS (push) Has been cancelled
CI / Windows (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:36:25 +08:00
commit 26446540fa
3151 changed files with 974126 additions and 0 deletions
@@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import java.util.HashMap;
import java.util.Map;
/**
* TVM API functions.
*/
public final class API {
private static ThreadLocal<Map<String, Function>> apiFuncs =
new ThreadLocal<Map<String, Function>>() {
@Override
protected Map<String, Function> initialValue() {
return new HashMap<String, Function>();
}
};
/**
* Get a tvm api function according by name.
* @param name function name.
* @return a TVM Function.
*/
public static Function get(final String name) {
Function func = apiFuncs.get().get(name);
if (func == null) {
func = Function.getFunction(name);
apiFuncs.get().put(name, func);
}
return func;
}
/**
* Cannot be instantiated.
*/
private API() {}
}
@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
/**
* Internal api functions.
*/
public final class APIInternal {
/**
* Get a tvm api function according by name.
* @param name function name.
* @return a TVM Function.
*/
public static Function get(final String name) {
return API.get(name);
}
/**
* Cannot be instantiated.
*/
private APIInternal() {}
}
@@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import java.io.File;
import java.io.IOException;
import org.apache.tvm.NativeLibraryLoader.Action;
/**
* Initializing methods and types.
*/
final class Base {
/**
* Hold Long reference for JNI.
*/
public static class RefLong {
public final long value;
public RefLong(final long value) {
this.value = value;
}
public RefLong() {
this(0L);
}
}
/**
* Hold TVMValue reference for JNI.
*/
public static class RefTVMValue {
public final TVMValue value;
public RefTVMValue(TVMValue value) {
this.value = value;
}
public RefTVMValue() {
this(null);
}
}
public static final LibInfo _LIB = new LibInfo();
static {
boolean loadNativeRuntimeLib = true;
try {
try {
tryLoadLibraryOS("tvm4j");
} catch (UnsatisfiedLinkError e) {
System.err.println("[WARN] TVM native library not found in path. "
+ "Copying native library from the archive. "
+ "Consider installing the library somewhere in the path "
+ "(for Windows: PATH, for Linux: LD_LIBRARY_PATH), "
+ "or specifying by Java cmd option -Djava.library.path=[lib path].");
NativeLibraryLoader.loadLibrary("tvm4j");
}
} catch (Throwable e) {
System.err.println("[WARN] Couldn't find native library tvm4j.");
e.printStackTrace();
System.err.println("Try to load tvm4j (runtime packed version) ...");
try {
System.loadLibrary("tvm4j_runtime_packed");
// if tvm runtime is packed in libtvm4j, we do not need to dlopen libtvm_runtime.so.
loadNativeRuntimeLib = false;
} catch (UnsatisfiedLinkError errFull) {
System.err.println("[ERROR] Couldn't find native library tvm4j_runtime_packed.");
throw new RuntimeException(errFull);
}
}
System.err.println("libtvm4j loads successfully.");
// always use linked lib
_LIB.nativeLibInit(null);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
_LIB.shutdown();
}
});
}
/**
* Load JNI for different OS.
* @param libname library name.
* @throws UnsatisfiedLinkError if loading fails.
*/
private static void tryLoadLibraryOS(String libname) throws UnsatisfiedLinkError {
try {
System.err.println(String.format("Try loading %s from native path.", libname));
System.loadLibrary(libname);
} catch (UnsatisfiedLinkError e) {
String os = System.getProperty("os.name");
// ref: http://lopica.sourceforge.net/os.html
if (os.startsWith("Linux")) {
tryLoadLibraryXPU(libname, "linux-x86_64");
} else if (os.startsWith("Mac")) {
tryLoadLibraryXPU(libname, "osx-x86_64");
} else {
// TODO(yizhi) support windows later
throw new UnsatisfiedLinkError("Windows not supported currently");
}
}
}
/**
* Load native library for different architectures.
* @param libname library name.
* @param arch architecture.
* @throws UnsatisfiedLinkError if loading fails
*/
private static void tryLoadLibraryXPU(String libname, String arch) throws UnsatisfiedLinkError {
System.err.println(String.format("Try loading %s-%s from native path.", libname, arch));
System.loadLibrary(String.format("%s-%s", libname, arch));
}
// helper function definitions
/**
* Check the return value of C API call.
* <p>
* This function will raise exception when error occurs.
* Wrap every API call with this function
* </p>
* @param ret return value from API calls
*/
public static void checkCall(int ret) throws TVMError {
if (ret != 0) {
throw new TVMError(_LIB.tvmFFIGetLastError());
}
}
/**
* TVM Runtime error.
*/
static class TVMError extends RuntimeException {
public TVMError(String err) {
super(err);
}
}
/**
* Cannot be instantiated.
*/
private Base() {}
}
@@ -0,0 +1,238 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import java.util.HashMap;
import java.util.Map;
import org.apache.tvm.rpc.RPC;
public class Device {
/**
* Provides the same information as the C++ enums DLDeviceType and
* TVMDeviceExtType.
*/
static final int kDLCPU = 1;
static final int kDLCUDA = 2;
static final int kDLCUDAHost = 3;
static final int kDLOpenCL = 4;
static final int kDLVulkan = 7;
static final int kDLMetal = 8;
static final int kDLVPI = 9;
static final int kDLROCM = 10;
static final int kDLROCMHost = 11;
static final int kDLExtDev = 12;
static final int kDLCUDAManaged = 13;
static final int kDLOneAPI = 14;
static final int kDLWebGPU = 15;
static final int kDLHexagon = 16;
private static final Map<Integer, String> DEVICE_TYPE_TO_NAME = new HashMap<Integer, String>();
private static final Map<String, Integer> DEVICE_NAME_TO_TYPE = new HashMap<String, Integer>();
static {
DEVICE_TYPE_TO_NAME.put(kDLCPU, "cpu");
DEVICE_TYPE_TO_NAME.put(kDLCUDA, "cuda");
DEVICE_TYPE_TO_NAME.put(kDLOpenCL, "opencl");
DEVICE_TYPE_TO_NAME.put(kDLVulkan, "vulkan");
DEVICE_TYPE_TO_NAME.put(kDLMetal, "metal");
DEVICE_TYPE_TO_NAME.put(kDLVPI, "vpi");
DEVICE_TYPE_TO_NAME.put(kDLHexagon, "hexagon");
DEVICE_NAME_TO_TYPE.put("cpu", kDLCPU);
DEVICE_NAME_TO_TYPE.put("cuda", kDLCUDA);
DEVICE_NAME_TO_TYPE.put("cl", kDLOpenCL);
DEVICE_NAME_TO_TYPE.put("opencl", kDLOpenCL);
DEVICE_NAME_TO_TYPE.put("vulkan", kDLVulkan);
DEVICE_NAME_TO_TYPE.put("metal", kDLMetal);
DEVICE_NAME_TO_TYPE.put("vpi", kDLVPI);
DEVICE_NAME_TO_TYPE.put("hexagon", kDLHexagon);
}
/**
* Construct a CPU device.
* @param devId The device id
* @return The created device
*/
public static Device cpu(int devId) {
return new Device(kDLCPU, devId);
}
public static Device cpu() {
return cpu(0);
}
/**
* Construct a CUDA GPU device.
* @param devId The device id
* @return The created device
*/
public static Device cuda(int devId) {
return new Device(kDLCUDA, devId);
}
public static Device cuda() {
return cuda(0);
}
/**
* Construct a OpenCL device.
* @param devId The device id
* @return The created device
*/
public static Device opencl(int devId) {
return new Device(kDLOpenCL, devId);
}
public static Device opencl() {
return opencl(0);
}
/**
* Construct a Vulkan device.
* @param devId The device id
* @return The created device
*/
public static Device vulkan(int devId) {
return new Device(kDLVulkan, devId);
}
public static Device vulkan() {
return vulkan(0);
}
/**
* Construct a metal device.
* @param devId The device id
* @return The created device
*/
public static Device metal(int devId) {
return new Device(kDLMetal, devId);
}
public static Device metal() {
return metal(0);
}
/**
* Construct a VPI simulated device.
* @param devId The device id
* @return The created device
*/
public static Device vpi(int devId) {
return new Device(kDLVPI, devId);
}
public static Device vpi() {
return vpi(0);
}
/**
* Construct a Hexagon device.
* @param devId The device id
* @return The created device
*/
public static Device hexagon(int devId) {
return new Device(kDLHexagon, devId);
}
public static Device hexagon() {
return hexagon(0);
}
public final int deviceType;
public final int deviceId;
public Device(int deviceType, int deviceId) {
this.deviceType = deviceType;
this.deviceId = deviceId;
}
public Device(String deviceType, int deviceId) {
this(DEVICE_NAME_TO_TYPE.get(deviceType), deviceId);
}
/**
* Whether this device exists.
* @return true if exists.
*/
public boolean exist() {
TVMValue ret = APIInternal.get("runtime.GetDeviceAttr")
.pushArg(deviceType)
.pushArg(deviceId)
.pushArg(0)
.invoke();
return ((TVMValueLong) ret).value != 0;
}
/**
* Maximum number of threads on each block.
* @return the maximum thread number.
*/
public long maxThreadsPerBlock() {
TVMValue ret = APIInternal.get("runtime.GetDeviceAttr")
.pushArg(deviceType)
.pushArg(deviceId)
.pushArg(1)
.invoke();
return ((TVMValueLong) ret).value;
}
/**
* Number of threads that executes in concurrent.
* @return the thread number.
*/
public long warpSize() {
TVMValue ret = APIInternal.get("runtime.GetDeviceAttr")
.pushArg(deviceType)
.pushArg(deviceId)
.pushArg(2)
.invoke();
return ret.asLong();
}
/**
* Synchronize until jobs finished at the device.
*/
public void sync() {
Base.checkCall(Base._LIB.tvmSynchronize(deviceType, deviceId));
}
@Override
public int hashCode() {
return (deviceType << 16) | deviceId;
}
@Override
public boolean equals(Object other) {
if (other != null && other instanceof Device) {
Device obj = (Device) other;
return deviceId == obj.deviceId && deviceType == obj.deviceType;
}
return false;
}
@Override
public String toString() {
if (deviceType >= RPC.RPC_SESS_MASK) {
int tblId = deviceType / RPC.RPC_SESS_MASK - 1;
int devType = deviceType % RPC.RPC_SESS_MASK;
return String.format("remote[%d]:%s(%d)", tblId, DEVICE_TYPE_TO_NAME.get(devType), deviceId);
}
return String.format("%s(%d)", DEVICE_TYPE_TO_NAME.get(deviceType), deviceId);
}
}
@@ -0,0 +1,294 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* TVM Packed Function.
*/
public class Function extends TVMObject {
/**
* Get registered function.
* @param name full function name.
* @return TVM function.
*/
public static Function getFunction(final String name) {
return getGlobalFunc(name, true);
}
/**
* Get list of global functions registered.
* @return List of global functions names.
*/
private static List<String> listGlobalFuncNames() {
List<String> names = new ArrayList<String>();
Base.checkCall(Base._LIB.tvmFFIFunctionListGlobalNames(names));
return Collections.unmodifiableList(names);
}
/**
* Get a global function by name.
* @param name The name of the function.
* @param allowMissing Whether allow missing function or raise an error.
* @return The function to be returned, None if function is missing.
*/
private static Function getGlobalFunc(String name, boolean allowMissing) {
Base.RefLong handle = new Base.RefLong();
Base.checkCall(Base._LIB.tvmFFIFunctionGetGlobal(name, handle));
if (handle.value != 0) {
return new Function(handle.value);
} else {
if (allowMissing) {
return null;
} else {
throw new IllegalArgumentException("Cannot find global function " + name);
}
}
}
Function(long handle) {
super(handle, TypeIndex.kTVMFFIFunction);
}
/**
* Easy for user to get the instance from returned TVMValue.
* @return this
*/
@Override
public Function asFunction() {
return this;
}
/**
* Invoke the function.
* @return the result.
*/
public TVMValue invoke() {
Base.RefTVMValue ret = new Base.RefTVMValue();
Base.checkCall(Base._LIB.tvmFFIFunctionCall(handle, ret));
return ret.value;
}
/**
* Push argument to the function.
* @param arg int argument.
* @return this
*/
public Function pushArg(int arg) {
Base._LIB.tvmFFIFunctionPushArgLong(arg);
return this;
}
/**
* Push argument to the function.
* @param arg long argument.
* @return this
*/
public Function pushArg(long arg) {
Base._LIB.tvmFFIFunctionPushArgLong(arg);
return this;
}
/**
* Push argument to the function.
* @param arg float argument.
* @return this
*/
public Function pushArg(float arg) {
Base._LIB.tvmFFIFunctionPushArgDouble(arg);
return this;
}
/**
* Push argument to the function.
* @param arg double argument.
* @return this
*/
public Function pushArg(double arg) {
Base._LIB.tvmFFIFunctionPushArgDouble(arg);
return this;
}
/**
* Push argument to the function.
* @param arg String argument.
* @return this
*/
public Function pushArg(String arg) {
Base._LIB.tvmFFIFunctionPushArgString(arg);
return this;
}
/**
* Push argument to the function.
* @param arg Tensor.
* @return this
*/
public Function pushArg(TensorBase arg) {
if (arg instanceof Tensor) {
Base._LIB.tvmFFIFunctionPushArgHandle(((Tensor) arg).handle, TypeIndex.kTVMFFITensor);
} else {
Base._LIB.tvmFFIFunctionPushArgHandle(arg.dltensorHandle, TypeIndex.kTVMFFIDLTensorPtr);
}
return this;
}
/**
* Push argument to the function.
* @param arg Module.
* @return this
*/
public Function pushArg(Module arg) {
Base._LIB.tvmFFIFunctionPushArgHandle(arg.handle, TypeIndex.kTVMFFIModule);
return this;
}
/**
* Push argument to the function.
* @param arg Function.
* @return this
*/
public Function pushArg(Function arg) {
Base._LIB.tvmFFIFunctionPushArgHandle(arg.handle, TypeIndex.kTVMFFIFunction);
return this;
}
/**
* Push argument to the function.
* @param arg bytes.
* @return this
*/
public Function pushArg(byte[] arg) {
Base._LIB.tvmFFIFunctionPushArgBytes(arg);
return this;
}
/**
* Push argument to the function.
* @param arg Device.
* @return this
*/
public Function pushArg(Device arg) {
Base._LIB.tvmFFIFunctionPushArgDevice(arg);
return this;
}
/**
* Invoke function with arguments.
* @param args Can be Integer, Long, Float, Double, String, Tensor.
* @return the result.
*/
public TVMValue call(Object... args) {
for (Object arg : args) {
pushArgToStack(arg);
}
return invoke();
}
private static void pushArgToStack(Object arg) {
if (arg instanceof TensorBase) {
TensorBase nd = (TensorBase) arg;
if (nd instanceof Tensor) {
Base._LIB.tvmFFIFunctionPushArgHandle(((Tensor) nd).handle, TypeIndex.kTVMFFITensor);
} else {
Base._LIB.tvmFFIFunctionPushArgHandle(nd.dltensorHandle, TypeIndex.kTVMFFIDLTensorPtr);
}
} else if (arg instanceof TVMObject) {
TVMObject obj = (TVMObject) arg;
Base._LIB.tvmFFIFunctionPushArgHandle(obj.handle, obj.typeIndex);
} else if (arg instanceof Integer) {
Base._LIB.tvmFFIFunctionPushArgLong((Integer) arg);
} else if (arg instanceof Long) {
Base._LIB.tvmFFIFunctionPushArgLong((Long) arg);
} else if (arg instanceof Float) {
Base._LIB.tvmFFIFunctionPushArgDouble((Float) arg);
} else if (arg instanceof Double) {
Base._LIB.tvmFFIFunctionPushArgDouble((Double) arg);
} else if (arg instanceof String) {
Base._LIB.tvmFFIFunctionPushArgString((String) arg);
} else if (arg instanceof byte[]) {
Base._LIB.tvmFFIFunctionPushArgBytes((byte[]) arg);
} else if (arg instanceof Device) {
Base._LIB.tvmFFIFunctionPushArgDevice((Device) arg);
} else if (arg instanceof TVMValueBytes) {
byte[] bytes = ((TVMValueBytes) arg).value;
Base._LIB.tvmFFIFunctionPushArgBytes(bytes);
} else if (arg instanceof TVMValueString) {
String str = ((TVMValueString) arg).value;
Base._LIB.tvmFFIFunctionPushArgString(str);
} else if (arg instanceof TVMValueDouble) {
double value = ((TVMValueDouble) arg).value;
Base._LIB.tvmFFIFunctionPushArgDouble(value);
} else if (arg instanceof TVMValueLong) {
long value = ((TVMValueLong) arg).value;
Base._LIB.tvmFFIFunctionPushArgLong(value);
} else if (arg instanceof TVMValueNull) {
Base._LIB.tvmFFIFunctionPushArgHandle(0, TypeIndex.kTVMFFINone);
} else {
throw new IllegalArgumentException("Invalid argument: " + arg);
}
}
public static interface Callback {
public Object invoke(TVMValue... args);
}
/**
* Register user-defined global function.
* @param name The function name.
* @param function The function to be registered.
* @param override Whether override existing entry.
*/
public static void register(String name, Callback function, boolean override) {
Base.RefLong createdFuncHandleRef = new Base.RefLong();
Base.checkCall(Base._LIB.tvmFFIFunctionCreateFromCallback(function, createdFuncHandleRef));
int ioverride = override ? 1 : 0;
Base.checkCall(Base._LIB.tvmFFIFunctionSetGlobal(name, createdFuncHandleRef.value, ioverride));
}
/**
* Register user-defined global function, do not override existing entry.
* @param name The function name.
* @param function The function to be registered.
*/
public static void register(String name, Callback function) {
register(name, function, false);
}
/**
* Convert a Java function to TVM function.
* @param function Java function.
* @return TVM function.
*/
public static Function convertFunc(Callback function) {
Base.RefLong createdFuncHandleRef = new Base.RefLong();
Base.checkCall(Base._LIB.tvmFFIFunctionCreateFromCallback(function, createdFuncHandleRef));
return new Function(createdFuncHandleRef.value);
}
private static Object invokeRegisteredCbFunc(Callback cb, TVMValue[] args) {
if (cb == null) {
System.err.println("[ERROR] Failed to get registered function");
return null;
}
return cb.invoke(args);
}
}
@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import java.util.List;
class LibInfo {
native int nativeLibInit(String tvmLibFile);
native int shutdown();
native String tvmFFIGetLastError();
// Object
native int tvmFFIObjectFree(long handle);
// Function
native void tvmFFIFunctionPushArgLong(long arg);
native void tvmFFIFunctionPushArgDouble(double arg);
native void tvmFFIFunctionPushArgString(String arg);
native void tvmFFIFunctionPushArgBytes(byte[] arg);
native void tvmFFIFunctionPushArgHandle(long arg, int argTypeIndex);
native void tvmFFIFunctionPushArgDevice(Device device);
native int tvmFFIFunctionListGlobalNames(List<String> funcNames);
native int tvmFFIFunctionGetGlobal(String name, Base.RefLong handle);
native int tvmFFIFunctionSetGlobal(String name, long handle, int override);
native int tvmFFIFunctionCall(long handle, Base.RefTVMValue retVal);
native int tvmFFIFunctionCreateFromCallback(Function.Callback function, Base.RefLong handle);
// Tensor
native int tvmFFIDLTensorGetShape(long handle, List<Long> shape);
native int tvmFFIDLTensorCopyFromTo(long from, long to);
native int tvmFFIDLTensorCopyFromJArray(byte[] fromRaw, long to);
native int tvmFFIDLTensorCopyToJArray(long from, byte[] to);
// the following functions are binded to keep things simpler
// One possibility is to enhance FFI to support shape directly
// so we do not need to run this binding through JNI
// Device
native int tvmSynchronize(int deviceType, int deviceId);
native int tvmTensorEmpty(long[] shape, int dtypeCode, int dtypeBits, int dtypeLanes,
int deviceType, int deviceId, Base.RefLong handle);
}
@@ -0,0 +1,133 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import java.util.HashMap;
import java.util.Map;
/**
* Container of compiled functions of TVM.
*/
public class Module extends TVMObject {
private static ThreadLocal<Map<String, Function>> apiFuncs =
new ThreadLocal<Map<String, Function>>() {
@Override
protected Map<String, Function> initialValue() {
return new HashMap<String, Function>();
}
};
private static Function getApi(String name) {
Function func = apiFuncs.get().get(name);
if (func == null) {
func = Function.getFunction(name);
apiFuncs.get().put(name, func);
}
return func;
}
Module(long handle) {
super(handle, TypeIndex.kTVMFFIModule);
}
private Function entry = null;
private final String entryName = "main";
/**
* Easy for user to get the instance from returned TVMValue.
* @return this
*/
@Override
public Module asModule() {
return this;
}
/**
* Get the entry function.
* @return The entry function if exist
*/
public Function entryFunc() {
if (entry == null) {
entry = getFunction(entryName);
}
return entry;
}
/**
* Get function from the module.
* @param name The name of the function.
* @param queryImports Whether also query modules imported by this module.
* @return The result function.
*/
public Function getFunction(String name, boolean queryImports) {
TVMValue ret = getApi("ffi.ModuleGetFunction")
.pushArg(this)
.pushArg(name)
.pushArg(queryImports ? 1 : 0)
.invoke();
return ret.asFunction();
}
public Function getFunction(String name) {
return getFunction(name, false);
}
/**
* Add module to the import list of current one.
* @param module The other module.
*/
public void importModule(Module module) {
getApi("ffi.ModuleImportModule").pushArg(this).pushArg(module).invoke();
}
/**
* Get type key of the module.
* @return type key of the module.
*/
public String typeKey() {
return getApi("ffi.ModuleGetTypeKind").pushArg(this).invoke().asString();
}
/**
* Load module from file.
* @param path The path to the module file.
* @param fmt The format of the file,
* if not specified it will be inferred from suffix of the file.
* @return The loaded module
*/
public static Module load(String path, String fmt) {
TVMValue ret = getApi("ffi.ModuleLoadFromFile").pushArg(path).pushArg(fmt).invoke();
return ret.asModule();
}
public static Module load(String path) {
return load(path, "");
}
/**
* Whether module runtime is enabled for target,
* e.g., The following code checks if cuda is enabled.
* Module.enabled("cuda")
* @param target The target device type.
* @return Whether runtime is enabled.
*/
public static boolean enabled(String target) {
TVMValue ret = getApi("runtime.RuntimeEnabled").pushArg(target).invoke();
return ret.asLong() != 0;
}
}
@@ -0,0 +1,157 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
class NativeLibraryLoader {
private static final String libPathInJar = "/lib/native/";
private static File tempDir;
static {
try {
tempDir = File.createTempFile("tvm4j", "");
if (!tempDir.delete() || !tempDir.mkdir()) {
throw new IOException("Couldn't create directory " + tempDir.getAbsolutePath());
}
/*
* Different cleanup strategies for Windows and Linux.
* TODO: shutdown hook won't work on Windows
*/
if (!"Windows".equals(getUnifiedOSName())) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
for (File f : tempDir.listFiles()) {
System.err.println("Deleting " + f.getAbsolutePath());
if (!f.delete()) {
System.err.println("[WARN] Couldn't delete temporary file " + f.getAbsolutePath());
}
}
System.err.println("Deleting " + tempDir.getAbsolutePath());
if (!tempDir.delete()) {
System.err.println(
"[WARN] Couldn't delete temporary directory " + tempDir.getAbsolutePath());
}
}
});
} else {
throw new RuntimeException("Windows not supported yet.");
}
} catch (IOException ex) {
System.err.println("Couldn't create temporary directory: " + ex.getMessage());
throw new RuntimeException(ex);
}
}
/**
* Find the library as a resource in jar, copy it to a tempfile
* and load it using System.load(). The name of the library has to be the
* base name, it is mapped to the corresponding system name using
* System.mapLibraryName(). e.g., the library "foo" is called "libfoo.so"
* under Linux and "foo.dll" under Windows, but you just have to pass "foo" to
* the loadLibrary().
*
* @param libname basename of the library
* @throws UnsatisfiedLinkError if library not found.
* @throws IOException if file not found.
*/
public static void loadLibrary(String libname) throws UnsatisfiedLinkError, IOException {
String mappedLibname = System.mapLibraryName(libname);
String loadLibname = mappedLibname;
if (mappedLibname.endsWith("dylib")) {
System.err.println("Replaced .dylib with .jnilib");
loadLibname = mappedLibname.replace(".dylib", ".jnilib");
}
System.err.println("Attempting to load " + loadLibname);
extractResourceFileToTempDir(loadLibname, new Action() {
@Override
public void invoke(File target) {
System.err.println("Loading library from " + target.getPath());
System.load(target.getPath());
}
});
}
/**
* Translate all those Windows to "Windows". ("Windows XP", "Windows Vista", "Windows 7", etc.)
*/
private static String unifyOSName(String osname) {
if (osname.startsWith("Windows")) {
return "Windows";
}
return osname;
}
private static String getUnifiedOSName() {
return unifyOSName(System.getProperty("os.name"));
}
private static File createTempFile(String name) throws IOException {
return new File(tempDir + File.separator + name);
}
static interface Action {
public void invoke(File file);
}
/**
* Copies the resource file to a temp file and do an action.
* @param filename source file name (in lib/native).
* @param action callback function to deal with the copied file.
*/
public static void extractResourceFileToTempDir(String filename, Action action)
throws IOException {
final String libFileInJar = libPathInJar + filename;
InputStream is = NativeLibraryLoader.class.getResourceAsStream(libFileInJar);
if (is == null) {
throw new UnsatisfiedLinkError("Couldn't find the resource " + filename);
}
System.err.println(String.format("Loading %s from %s", filename, libPathInJar));
try {
File tempfile = createTempFile(filename);
OutputStream os = new FileOutputStream(tempfile);
final long savedTime = System.currentTimeMillis();
byte[] buf = new byte[8192];
int len = is.read(buf);
while (len > 0) {
os.write(buf, 0, len);
len = is.read(buf);
}
os.flush();
final FileInputStream lock = new FileInputStream(tempfile);
os.close();
double seconds = (double) (System.currentTimeMillis() - savedTime) / 1e3;
System.err.println(String.format("Copying took %.2f seconds.", seconds));
action.invoke(tempfile);
lock.close();
} catch (IOException io) {
System.err.println("[ERROR] Could not create the temp file: " + io.toString());
throw io;
} catch (UnsatisfiedLinkError ule) {
System.err.println("Couldn't load copied link file: " + ule.toString());
throw ule;
}
}
}
@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
/**
* Base class of all TVM Objects.
*/
public class TVMObject extends TVMValue {
protected long handle;
public final int typeIndex;
public TVMObject(long handle, int typeIndex) {
this.handle = handle;
this.typeIndex = typeIndex;
}
public void release() {
Base.checkCall(Base._LIB.tvmFFIObjectFree(this.handle));
this.handle = 0;
}
@Override
protected void finalize() throws Throwable {
release();
super.finalize();
}
}
@@ -0,0 +1,106 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
public class TVMType {
public static final int INT = 0;
public static final int UINT = 1;
public static final int FLOAT = 2;
public static final int HANDLE = 4;
public final int typeCode;
public final int bits;
public final int numOfBytes;
public final int lanes;
/**
* TVMType constructor.
* @param typeStr type name, e.g., "float32", "float64", "uint8", etc.
* @param lanes Tensor lanes.
*/
public TVMType(String typeStr, int lanes) {
this.lanes = lanes;
int bitsTemp = 0;
if (typeStr.startsWith("int")) {
typeCode = INT;
bitsTemp = Integer.parseInt(typeStr.substring(3));
} else if (typeStr.startsWith("uint")) {
typeCode = UINT;
bitsTemp = Integer.parseInt(typeStr.substring(4));
} else if (typeStr.startsWith("float")) {
typeCode = FLOAT;
bitsTemp = Integer.parseInt(typeStr.substring(5));
} else if (typeStr.startsWith("handle")) {
typeCode = HANDLE;
bitsTemp = 64;
} else {
throw new IllegalArgumentException("Do not know how to handle type " + typeStr);
}
bits = (bitsTemp == 0) ? 32 : bitsTemp;
if ((bits & (bits - 1)) != 0 || bits < 8) {
throw new IllegalArgumentException("Do not know how to handle type " + typeStr);
}
numOfBytes = bits / 8;
}
public TVMType(String typeStr) {
this(typeStr, 1);
}
@Override
public int hashCode() {
return (typeCode << 16) | (bits << 8) | lanes;
}
@Override
public boolean equals(Object other) {
if (other != null && other instanceof TVMType) {
TVMType otherInst = (TVMType) other;
return (bits == otherInst.bits) && (typeCode == otherInst.typeCode)
&& (lanes == otherInst.lanes);
}
return false;
}
@Override
public String toString() {
String typeCodeStr;
switch (typeCode) {
case INT:
typeCodeStr = "int";
break;
case UINT:
typeCodeStr = "uint";
break;
case FLOAT:
typeCodeStr = "float";
break;
case HANDLE:
typeCodeStr = "handle";
break;
default:
typeCodeStr = "Unknown";
break;
}
String str = typeCodeStr + bits;
if (lanes != 1) {
str += lanes;
}
return str;
}
}
@@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
public class TVMValue {
protected TVMValue() {}
public void release() {}
public long asLong() {
throw new UnsupportedOperationException();
}
public double asDouble() {
throw new UnsupportedOperationException();
}
public byte[] asBytes() {
throw new UnsupportedOperationException();
}
public Module asModule() {
throw new UnsupportedOperationException();
}
public Function asFunction() {
throw new UnsupportedOperationException();
}
public TensorBase asTensor() {
throw new UnsupportedOperationException();
}
public String asString() {
throw new UnsupportedOperationException();
}
// easy for JNI to use.
long asHandle() {
throw new UnsupportedOperationException();
}
}
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
public class TVMValueBytes extends TVMValue {
public final byte[] value;
public TVMValueBytes(byte[] value) {
this.value = value;
}
@Override
public byte[] asBytes() {
return value;
}
}
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
public class TVMValueDouble extends TVMValue {
public final double value;
public TVMValueDouble(double value) {
this.value = value;
}
@Override
public double asDouble() {
return value;
}
}
@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
/**
* Java class related to TVM handles (ArgTypeCode.HANDLE)
*/
public class TVMValueHandle extends TVMValue {
public final long value;
public TVMValueHandle(long value) {
this.value = value;
}
@Override
public long asHandle() {
return value;
}
}
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
public class TVMValueLong extends TVMValue {
public final long value;
public TVMValueLong(long value) {
this.value = value;
}
@Override
public long asLong() {
return value;
}
}
@@ -0,0 +1,22 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
public class TVMValueNull extends TVMValue {
public TVMValueNull() {}
}
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
public class TVMValueString extends TVMValue {
public final String value;
public TVMValueString(String value) {
this.value = value;
}
@Override
public String asString() {
return value;
}
}
@@ -0,0 +1,409 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
/**
* Lightweight Tensor class of TVM runtime.
*/
public class Tensor extends TensorBase {
private final TVMType dtype;
private final Device device;
Tensor(long handle, boolean isView, TVMType dtype, Device dev) {
super(handle, isView);
this.dtype = dtype;
this.device = dev;
}
/**
* Copy from a native array.
* The Tensor type must by float64
* @param sourceArray the source data
*/
public void copyFrom(double[] sourceArray) {
checkCopySize(sourceArray.length);
if (dtype.typeCode != TVMType.FLOAT || dtype.bits != 64) {
throw new IllegalArgumentException("Cannot set double[] for " + dtype.toString() + " array");
}
byte[] nativeArr = new byte[sourceArray.length * dtype.numOfBytes];
for (int i = 0; i < sourceArray.length; ++i) {
wrapBytes(nativeArr, i * dtype.numOfBytes, dtype.numOfBytes).putDouble(sourceArray[i]);
}
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyFromJArray(nativeArr, this.dltensorHandle));
}
/**
* Copy from a native array.
* The Tensor type must by float32
* @param sourceArray the source data
*/
public void copyFrom(float[] sourceArray) {
checkCopySize(sourceArray.length);
if (dtype.typeCode != TVMType.FLOAT || dtype.bits != 32) {
throw new IllegalArgumentException("Cannot set float[] for " + dtype.toString() + " array");
}
byte[] nativeArr = new byte[sourceArray.length * dtype.numOfBytes];
for (int i = 0; i < sourceArray.length; ++i) {
wrapBytes(nativeArr, i * dtype.numOfBytes, dtype.numOfBytes).putFloat(sourceArray[i]);
}
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyFromJArray(nativeArr, this.dltensorHandle));
}
/**
* Copy from a native array.
* The Tensor type must by int64
* @param sourceArray the source data
*/
public void copyFrom(long[] sourceArray) {
checkCopySize(sourceArray.length);
if (dtype.typeCode != TVMType.INT || dtype.bits != 64) {
throw new IllegalArgumentException("Cannot set long[] for " + dtype.toString() + " array");
}
byte[] nativeArr = new byte[sourceArray.length * dtype.numOfBytes];
for (int i = 0; i < sourceArray.length; ++i) {
wrapBytes(nativeArr, i * dtype.numOfBytes, dtype.numOfBytes).putLong(sourceArray[i]);
}
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyFromJArray(nativeArr, this.dltensorHandle));
}
/**
* Copy from a native array.
* The Tensor type must by float32
* @param sourceArray the source data
*/
public void copyFrom(int[] sourceArray) {
checkCopySize(sourceArray.length);
if (dtype.typeCode != TVMType.INT || dtype.bits != 32) {
throw new IllegalArgumentException("Cannot set int[] for " + dtype.toString() + " array");
}
byte[] nativeArr = new byte[sourceArray.length * dtype.numOfBytes];
for (int i = 0; i < sourceArray.length; ++i) {
wrapBytes(nativeArr, i * dtype.numOfBytes, dtype.numOfBytes).putInt(sourceArray[i]);
}
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyFromJArray(nativeArr, this.dltensorHandle));
}
/**
* Copy from a native array.
* The Tensor type must by int16
* @param sourceArray the source data
*/
public void copyFrom(short[] sourceArray) {
checkCopySize(sourceArray.length);
if (dtype.typeCode != TVMType.INT || dtype.bits != 16) {
throw new IllegalArgumentException("Cannot set short[] for " + dtype.toString() + " array");
}
byte[] nativeArr = new byte[sourceArray.length * dtype.numOfBytes];
for (int i = 0; i < sourceArray.length; ++i) {
wrapBytes(nativeArr, i * dtype.numOfBytes, dtype.numOfBytes).putShort(sourceArray[i]);
}
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyFromJArray(nativeArr, this.dltensorHandle));
}
/**
* Copy from a native array.
* The Tensor type must by int8
* @param sourceArray the source data
*/
public void copyFrom(byte[] sourceArray) {
checkCopySize(sourceArray.length);
if (dtype.typeCode != TVMType.INT || dtype.bits != 8) {
throw new IllegalArgumentException("Cannot set byte[] for " + dtype.toString() + " array");
}
copyFromRaw(sourceArray);
}
/**
* Copy from a native array.
* The Tensor type must by uint16
* @param sourceArray the source data
*/
public void copyFrom(char[] sourceArray) {
checkCopySize(sourceArray.length);
if (dtype.typeCode != TVMType.UINT || dtype.bits != 16) {
throw new IllegalArgumentException("Cannot set char[] for " + dtype.toString() + " array");
}
byte[] nativeArr = new byte[sourceArray.length * dtype.numOfBytes];
for (int i = 0; i < sourceArray.length; ++i) {
wrapBytes(nativeArr, i * dtype.numOfBytes, dtype.numOfBytes).putChar(sourceArray[i]);
}
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyFromJArray(nativeArr, this.dltensorHandle));
}
private void checkCopySize(int sourceLength) {
long arrSize = size();
if (arrSize != sourceLength) {
throw new IllegalArgumentException(
String.format("Array shape size not match: %d v.s. %d", sourceLength, size()));
}
}
/**
* Copy from a raw byte array.
* @param sourceArray the source data
*/
public void copyFromRaw(byte[] sourceArray) {
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyFromJArray(sourceArray, this.dltensorHandle));
}
/**
* Get shape of current Tensor.
* @return an array representing shape of current tensor
*/
public long[] shape() {
List<Long> data = new ArrayList<Long>();
Base.checkCall(Base._LIB.tvmFFIDLTensorGetShape(this.dltensorHandle, data));
long[] shapeArr = new long[data.size()];
for (int i = 0; i < shapeArr.length; ++i) {
shapeArr[i] = data.get(i);
}
return shapeArr;
}
/**
* Get total size of current Tensor.
* @return size of current Tensor.
*/
public long size() {
long product = 1L;
long[] shapeArr = shape();
for (int i = 0; i < shapeArr.length; ++i) {
product *= shapeArr[i];
}
return product;
}
/**
* Return a copied flat java array of current array (row-major).
* The Tensor dtype must be float64
* @return A copy of array content.
*/
public double[] asDoubleArray() {
if (dtype.typeCode != TVMType.FLOAT || dtype.bits != 64) {
throw new IllegalArgumentException(
"Cannot set convert to double[] for " + dtype.toString() + " array");
}
byte[][] units = groupInternalBytes();
double[] array = new double[units.length];
for (int i = 0; i < units.length; ++i) {
array[i] = wrapBytes(units[i]).getDouble();
}
return array;
}
/**
* Return a copied flat java array of current array (row-major).
* The Tensor dtype must be float32
* @return A copy of array content.
*/
public float[] asFloatArray() {
if (dtype.typeCode != TVMType.FLOAT || dtype.bits != 32) {
throw new IllegalArgumentException(
"Cannot set convert to float[] for " + dtype.toString() + " array");
}
byte[][] units = groupInternalBytes();
float[] array = new float[units.length];
for (int i = 0; i < units.length; ++i) {
array[i] = wrapBytes(units[i]).getFloat();
}
return array;
}
/**
* Return a copied flat java array of current array (row-major).
* The Tensor dtype must be int64
* @return A copy of array content.
*/
public long[] asLongArray() {
if (dtype.typeCode != TVMType.INT || dtype.bits != 64) {
throw new IllegalArgumentException(
"Cannot set convert to long[] for " + dtype.toString() + " array");
}
byte[][] units = groupInternalBytes();
long[] array = new long[units.length];
for (int i = 0; i < units.length; ++i) {
array[i] = wrapBytes(units[i]).getLong();
}
return array;
}
/**
* Return a copied flat java array of current array (row-major).
* The Tensor dtype must be int32
* @return A copy of array content.
*/
public int[] asIntArray() {
if (dtype.typeCode != TVMType.INT || dtype.bits != 32) {
throw new IllegalArgumentException(
"Cannot set convert to int[] for " + dtype.toString() + " array");
}
byte[][] units = groupInternalBytes();
int[] array = new int[units.length];
for (int i = 0; i < units.length; ++i) {
array[i] = wrapBytes(units[i]).getInt();
}
return array;
}
/**
* Return a copied flat java array of current array (row-major).
* The Tensor dtype must be int16
* @return A copy of array content.
*/
public short[] asShortArray() {
if (dtype.typeCode != TVMType.INT || dtype.bits != 16) {
throw new IllegalArgumentException(
"Cannot set convert to short[] for " + dtype.toString() + " array");
}
byte[][] units = groupInternalBytes();
short[] array = new short[units.length];
for (int i = 0; i < units.length; ++i) {
array[i] = wrapBytes(units[i]).getShort();
}
return array;
}
/**
* Return a copied flat java array of current array (row-major).
* The Tensor dtype must be uint16
* @return A copy of array content.
*/
public char[] asCharArray() {
if (dtype.typeCode != TVMType.UINT || dtype.bits != 16) {
throw new IllegalArgumentException(
"Cannot set convert to char[] for " + dtype.toString() + " array");
}
byte[][] units = groupInternalBytes();
char[] array = new char[units.length];
for (int i = 0; i < units.length; ++i) {
array[i] = wrapBytes(units[i]).getChar();
}
return array;
}
/**
* Return a copied flat java array of current array (row-major).
* The Tensor dtype must be int8
* @return A copy of array content.
*/
public byte[] asByteArray() {
if (dtype.typeCode != TVMType.INT || dtype.bits != 8) {
throw new IllegalArgumentException(
"Cannot set convert to byte[] for " + dtype.toString() + " array");
}
return internal();
}
/**
* Return a copied internal byte array of current array (row-major).
* @return A copy of array content.
*/
public byte[] internal() {
Tensor tmp = Tensor.empty(shape(), dtype);
copyTo(tmp);
int arrLength = dtype.numOfBytes * (int) size();
byte[] arr = new byte[arrLength];
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyToJArray(this.dltensorHandle, arr));
return arr;
}
private byte[][] groupInternalBytes() {
byte[] raw = internal();
int unitSize = dtype.numOfBytes;
if (raw.length <= 0 || raw.length % unitSize != 0) {
throw new IllegalArgumentException(String.format(
"%s size %d cannot divide byte array size %d", dtype.toString(), unitSize, raw.length));
}
int numOfUnits = raw.length / unitSize;
byte[][] units = new byte[numOfUnits][unitSize];
for (int i = 0; i < numOfUnits; ++i) {
System.arraycopy(raw, i * unitSize, units[i], 0, unitSize);
}
return units;
}
/**
* Get the device of current array.
* @return the device.
*/
public Device device() {
return device;
}
/**
* Create an empty array given shape, type and device.
* @param shape The shape of the array.
* @param dtype The data type of the array.
* @param dev The device of the array.
* @return The array tvm supported.
*/
public static Tensor empty(long[] shape, TVMType dtype, Device dev) {
Base.RefLong refHandle = new Base.RefLong();
Base.checkCall(Base._LIB.tvmTensorEmpty(
shape, dtype.typeCode, dtype.bits, dtype.lanes, dev.deviceType, dev.deviceId, refHandle));
return new Tensor(refHandle.value, false, dtype, dev);
}
/**
* Create an empty array on cpu given shape and type.
* @param shape The shape of the array.
* @param dtype The data type of the array.
* @return The array tvm supported.
*/
public static Tensor empty(long[] shape, TVMType dtype) {
return empty(shape, dtype, Device.cpu(0));
}
/**
* Create an empty float32 array on cpu given shape.
* @param shape The shape of the array.
* @return The array tvm supported.
*/
public static Tensor empty(long[] shape) {
return empty(shape, new TVMType("float32", 1), Device.cpu(0));
}
/**
* Create an empty float32 array given shape and device.
* @param shape The shape of the array.
* @param dev The device of the array.
* @return The array tvm supported.
*/
public static Tensor empty(long[] shape, Device dev) {
return empty(shape, new TVMType("float32", 1), dev);
}
private static ByteBuffer wrapBytes(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
bb.order(ByteOrder.LITTLE_ENDIAN);
return bb;
}
private static ByteBuffer wrapBytes(byte[] bytes, int offset, int length) {
ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length);
bb.order(ByteOrder.LITTLE_ENDIAN);
return bb;
}
}
@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
/**
* Base class of Tensor. To handle callback array.
* Only deep-copy supported.
*/
public class TensorBase extends TVMValue {
protected long handle;
public final boolean isView;
protected final long dltensorHandle;
TensorBase(long handle, boolean isView) {
this.dltensorHandle = isView ? handle : handle + 8 * 2;
this.handle = isView ? 0 : handle;
this.isView = isView;
}
@Override
public TensorBase asTensor() {
return this;
}
/**
* Release the Tensor.
*/
public void release() {
if (this.handle != 0) {
Base.checkCall(Base._LIB.tvmFFIObjectFree(this.handle));
this.handle = 0;
}
}
@Override
protected void finalize() throws Throwable {
release();
super.finalize();
}
/**
* Copy array to target.
* @param target The target array to be copied, must have same shape as this array.
* @return target
*/
public TensorBase copyTo(TensorBase target) {
Base.checkCall(Base._LIB.tvmFFIDLTensorCopyFromTo(this.dltensorHandle, target.dltensorHandle));
return target;
}
}
@@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
// Type code used in API calls
public class TypeIndex {
public static final int kTVMFFINone = 0;
public static final int kTVMFFIInt = 1;
public static final int kTVMFFIBool = 2;
public static final int kTVMFFIFloat = 3;
public static final int kTVMFFIOpaquePtr = 4;
public static final int kTVMFFIDataType = 5;
public static final int kTVMFFIDevice = 6;
public static final int kTVMFFIDLTensorPtr = 7;
public static final int kTVMFFIRawStr = 8;
public static final int kTVMFFIByteArrayPtr = 9;
public static final int kTVMFFIObjectRValueRef = 10;
public static final int kTVMFFIStaticObjectBegin = 64;
public static final int kTVMFFIObject = 64;
public static final int kTVMFFIStr = 65;
public static final int kTVMFFIBytes = 66;
public static final int kTVMFFIError = 67;
public static final int kTVMFFIFunction = 68;
public static final int kTVMFFIShape = 70;
public static final int kTVMFFITensor = 71;
public static final int kTVMFFIArray = 72;
public static final int kTVMFFIMap = 73;
public static final int kTVMFFIModule = 73;
}
@@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import org.apache.tvm.Function;
import org.apache.tvm.TVMValue;
/**
* RPC Client.
*/
public class Client {
/**
* Connect to RPC Server.
* @param url The url of the host.
* @param port The port to connect to.
* @param key Additional key to match server.
* @return The connected session.
*/
public static RPCSession connect(String url, int port, String key) {
Function doConnect = RPC.getApi("Connect");
if (doConnect == null) {
throw new RuntimeException("Please compile with USE_RPC=1");
}
TVMValue sess = doConnect.pushArg(url).pushArg(port).pushArg(key).invoke();
return new RPCSession(sess.asModule());
}
/**
* Connect to RPC Server.
* @param url The url of the host.
* @param port The port to connect to.
* @return The connected session.
*/
public static RPCSession connect(String url, int port) {
return connect(url, port, "");
}
}
@@ -0,0 +1,102 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
/**
* Server processor for proxy connection.
*/
public class ConnectProxyServerProcessor implements ServerProcessor {
private final String host;
private final int port;
private final String key;
private volatile Socket currSocket = new Socket();
private Runnable callback;
/**
* Construct proxy server processor.
* @param host Proxy server host.
* @param port Proxy server port.
* @param key Proxy server key.
*/
public ConnectProxyServerProcessor(String host, int port, String key) {
this.host = host;
this.port = port;
this.key = "server:" + key;
}
/**
* Set a callback when a connection is received e.g., to record the time for a
* watchdog.
* @param callback Runnable object.
*/
public void setStartTimeCallback(Runnable callback) {
this.callback = callback;
}
/**
* Close the socket.
*/
@Override
public void terminate() {
Utils.closeQuietly(currSocket);
}
@Override
public void run() {
try {
SocketAddress address = new InetSocketAddress(host, port);
currSocket.connect(address, 6000);
final InputStream in = currSocket.getInputStream();
final OutputStream out = currSocket.getOutputStream();
out.write(Utils.toBytes(RPC.RPC_MAGIC));
out.write(Utils.toBytes(key.length()));
out.write(Utils.toBytes(key));
int magic = Utils.wrapBytes(Utils.recvAll(in, 4)).getInt();
if (magic == RPC.RPC_MAGIC + 1) {
throw new RuntimeException(String.format("key: %s has already been used in proxy", key));
} else if (magic == RPC.RPC_MAGIC + 2) {
System.err.println("RPCProxy do not have matching client key " + key);
} else if (magic != RPC.RPC_MAGIC) {
throw new RuntimeException(address + " is not RPC Proxy");
}
// Get key from remote
int keylen = Utils.wrapBytes(Utils.recvAll(in, 4)).getInt();
String remoteKey = Utils.decodeToStr(Utils.recvAll(in, keylen));
System.err.println("RPCProxy connected to " + address);
if (callback != null) {
callback.run();
}
SocketChannel sockChannel = new SocketChannel(currSocket);
new NativeServerLoop(sockChannel.getFsend(), sockChannel.getFrecv()).run();
System.err.println("Finish serving " + address);
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
terminate();
}
}
}
@@ -0,0 +1,269 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.BindException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
/**
* Server processor with tracker connection (based on standalone).
* This RPC Server registers itself with an RPC Tracker for a specific queue
* (using its device key) and listens for incoming requests.
*/
public class ConnectTrackerServerProcessor implements ServerProcessor {
private ServerSocket server;
private final String trackerHost;
private final int trackerPort;
// device key
private final String key;
// device key plus randomly generated key (per-session)
private final String matchKey;
private int serverPort = 5001;
public static final int MAX_SERVER_PORT = 5555;
// time to wait before aborting tracker connection (ms)
public static final int TRACKER_TIMEOUT = 6000;
// time to wait before retrying tracker connection (ms)
public static final int RETRY_PERIOD = TRACKER_TIMEOUT;
// time to wait for a connection before refreshing tracker connection (ms)
public static final int STALE_TRACKER_TIMEOUT = 300000;
// time to wait if no timeout value is specified (seconds)
public static final int HARD_TIMEOUT_DEFAULT = 300;
private RPCWatchdog watchdog;
private Socket trackerSocket;
/**
* Construct tracker server processor.
* @param trackerHost Tracker host.
* @param trackerPort Tracker port.
* @param key Device key.
* @param watchdog watch for timeout, etc.
* @throws java.io.IOException when socket fails to open.
*/
public ConnectTrackerServerProcessor(
String trackerHost, int trackerPort, String key, RPCWatchdog watchdog) throws IOException {
while (true) {
try {
this.server = new ServerSocket(serverPort);
server.setSoTimeout(STALE_TRACKER_TIMEOUT);
break;
} catch (BindException e) {
System.err.println(serverPort);
System.err.println(e);
serverPort++;
if (serverPort > MAX_SERVER_PORT) {
throw e;
}
}
}
System.err.println("using port: " + serverPort);
this.trackerHost = trackerHost;
this.trackerPort = trackerPort;
this.key = key;
this.matchKey = key + ":" + Math.random();
this.watchdog = watchdog;
}
public String getMatchKey() {
return matchKey;
}
@Override
public void terminate() {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
String recvKey = null;
try {
trackerSocket = connectToTracker();
// open a socket and handshake with tracker
register();
Socket socket = null;
InputStream in = null;
OutputStream out = null;
while (true) {
try {
System.err.println("waiting for requests...");
// wait for client request
socket = server.accept();
in = socket.getInputStream();
out = socket.getOutputStream();
int magic = Utils.wrapBytes(Utils.recvAll(in, 4)).getInt();
if (magic != RPC.RPC_MAGIC) {
out.write(Utils.toBytes(RPC.RPC_CODE_MISMATCH));
System.err.println("incorrect RPC magic");
Utils.closeQuietly(socket);
continue;
}
recvKey = Utils.recvString(in);
System.err.println("matchKey:" + matchKey);
System.err.println("key: " + recvKey);
// incorrect key
if (recvKey.indexOf(matchKey) == -1) {
out.write(Utils.toBytes(RPC.RPC_CODE_MISMATCH));
System.err.println("key mismatch, expected: " + matchKey + " got: " + recvKey);
Utils.closeQuietly(socket);
continue;
}
// successfully got client request and completed handshake with client
break;
} catch (SocketTimeoutException e) {
System.err.println("no incoming connections, refreshing...");
// need to reregister, if the tracker died we should see a socked closed exception
if (!needRefreshKey()) {
System.err.println("reregistering...");
register();
}
}
}
int timeout = HARD_TIMEOUT_DEFAULT;
int timeoutArgIndex = recvKey.indexOf(RPC.TIMEOUT_ARG);
if (timeoutArgIndex != -1) {
timeout = Integer.parseInt(recvKey.substring(timeoutArgIndex + RPC.TIMEOUT_ARG.length()));
}
System.err.println("alloted timeout: " + timeout);
if (!recvKey.startsWith("client:")) {
System.err.println("recv key mismatch...");
out.write(Utils.toBytes(RPC.RPC_CODE_MISMATCH));
} else {
out.write(Utils.toBytes(RPC.RPC_MAGIC));
// send server key to the client
Utils.sendString(out, recvKey);
}
System.err.println("Connection from " + socket.getRemoteSocketAddress().toString());
// received timeout in seconds
watchdog.startTimeout(timeout * 1000);
SocketChannel sockChannel = new SocketChannel(socket);
new NativeServerLoop(sockChannel.getFsend(), sockChannel.getFrecv()).run();
System.err.println("Finish serving " + socket.getRemoteSocketAddress().toString());
Utils.closeQuietly(socket);
} catch (ConnectException e) {
// if the tracker connection failed, wait a bit before retrying
try {
Thread.sleep(RETRY_PERIOD);
} catch (InterruptedException e_) {
System.err.println("interrupted before retrying to connect to tracker...");
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
try {
if (trackerSocket != null) {
trackerSocket.close();
}
server.close();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
private Socket connectToTracker() throws IOException {
trackerSocket = new Socket();
SocketAddress address = new InetSocketAddress(trackerHost, trackerPort);
trackerSocket.connect(address, TRACKER_TIMEOUT);
InputStream trackerIn = trackerSocket.getInputStream();
OutputStream trackerOut = trackerSocket.getOutputStream();
trackerOut.write(Utils.toBytes(RPC.RPC_TRACKER_MAGIC));
int trackerMagic = Utils.wrapBytes(Utils.recvAll(trackerIn, 4)).getInt();
if (trackerMagic != RPC.RPC_TRACKER_MAGIC) {
throw new SocketException("failed to connect to tracker (WRONG MAGIC)");
}
String infoJSON = generateCinfo(key);
Utils.sendString(trackerOut, infoJSON);
int recvCode = Integer.parseInt(Utils.recvString(trackerIn));
if (recvCode != RPC.TrackerCode.SUCCESS) {
throw new SocketException("failed to connect to tracker (not SUCCESS)");
}
return trackerSocket;
}
/*
* Register the RPC Server with the RPC Tracker.
*/
private void register() throws IOException {
InputStream trackerIn = trackerSocket.getInputStream();
OutputStream trackerOut = trackerSocket.getOutputStream();
// send a JSON with PUT, device key, RPC server port, and the randomly
// generated key
String putJSON = generatePut(RPC.TrackerCode.PUT, key, serverPort, matchKey);
Utils.sendString(trackerOut, putJSON);
int recvCode = Integer.parseInt(Utils.recvString(trackerIn));
if (recvCode != RPC.TrackerCode.SUCCESS) {
throw new SocketException("failed to register with tracker (not SUCCESS)");
}
System.err.println("registered with tracker...");
}
/*
* Check if the RPC Tracker has our key.
*/
private boolean needRefreshKey() throws IOException {
InputStream trackerIn = trackerSocket.getInputStream();
OutputStream trackerOut = trackerSocket.getOutputStream();
String getJSON = generateGetPendingMatchKeys(RPC.TrackerCode.GET_PENDING_MATCHKEYS);
Utils.sendString(trackerOut, getJSON);
String recvJSON = Utils.recvString(trackerIn);
System.err.println("pending matchkeys: " + recvJSON);
// fairly expensive string operation...
if (recvJSON.indexOf(matchKey) != -1) {
return true;
}
return false;
}
// handcrafted JSON
private String generateCinfo(String key) {
String cinfo = "{\"key\" : "
+ "\"server:" + key + "\", \"addr\": [null, \"" + serverPort + "\"]}";
return "[" + RPC.TrackerCode.UPDATE_INFO + ", " + cinfo + "]";
}
// handcrafted JSON
private String generatePut(int code, String key, int port, String matchKey) {
return "[" + code + ", "
+ "\"" + key + "\""
+ ", "
+ "[" + port + ", "
+ "\"" + matchKey + "\""
+ "]"
+ ", "
+ "null"
+ "]";
}
// handcrafted JSON
private String generateGetPendingMatchKeys(int code) {
return "[" + code + "]";
}
}
@@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.io.File;
import java.io.IOException;
import org.apache.tvm.Function;
import org.apache.tvm.Module;
import org.apache.tvm.TVMValue;
/**
* Call native ServerLoop on socket file descriptor.
*/
public class NativeServerLoop implements Runnable {
private final Function fsend;
private final Function frecv;
/**
* Constructor for NativeServerLoop.
* @param fsend socket.send function.
* @param frecv socket.recv function.
*/
public NativeServerLoop(final Function fsend, final Function frecv) {
this.fsend = fsend;
this.frecv = frecv;
}
@Override
public void run() {
File tempDir = null;
try {
tempDir = serverEnv();
System.err.println("starting server loop...");
RPC.getApi("ServerLoop").pushArg(fsend).pushArg(frecv).invoke();
System.err.println("done server loop...");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (tempDir != null) {
String[] entries = tempDir.list();
for (String s : entries) {
File currentFile = new File(tempDir.getPath(), s);
if (!currentFile.delete()) {
System.err.println(
"[WARN] Couldn't delete temporary file " + currentFile.getAbsolutePath());
}
}
if (!tempDir.delete()) {
System.err.println(
"[WARN] Couldn't delete temporary directory " + tempDir.getAbsolutePath());
}
}
}
}
private static File serverEnv() throws IOException {
// Server environment function return temp dir.
final File tempDir = File.createTempFile("tvm4j_rpc_", "");
if (!tempDir.delete() || !tempDir.mkdir()) {
throw new IOException("Couldn't create directory " + tempDir.getAbsolutePath());
}
Function.register("tvm.rpc.server.workpath", new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
return tempDir + File.separator + args[0].asString();
}
}, true);
Function.register("tvm.rpc.server.load_module", new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
String filename = args[0].asString();
String path = tempDir + File.separator + filename;
System.err.println("Load module from " + path);
return Module.load(path);
}
}, true);
return tempDir;
}
}
@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.util.HashMap;
import java.util.Map;
import org.apache.tvm.Function;
public class RPC {
public static final int RPC_TRACKER_MAGIC = 0x2f271;
public static final int RPC_MAGIC = 0xff271;
public static final int RPC_CODE_MISMATCH = RPC_MAGIC + 2;
public static final int RPC_SESS_MASK = 128;
public static final String TIMEOUT_ARG = "-timeout=";
public class TrackerCode {
public static final int PUT = 3;
public static final int UPDATE_INFO = 5;
public static final int GET_PENDING_MATCHKEYS = 7;
public static final int SUCCESS = 0;
}
private static ThreadLocal<Map<String, Function>> apiFuncs =
new ThreadLocal<Map<String, Function>>() {
@Override
protected Map<String, Function> initialValue() {
return new HashMap<String, Function>();
}
};
/**
* Get internal function starts with namespace tvm.rpc.
* @param name function name.
* @return the function, null if not exists.
*/
static Function getApi(String name) {
Function func = apiFuncs.get().get(name);
if (func == null) {
func = Function.getFunction("rpc." + name);
if (func == null) {
return null;
}
apiFuncs.get().put(name, func);
}
return func;
}
}
@@ -0,0 +1,272 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.tvm.Device;
import org.apache.tvm.Function;
import org.apache.tvm.Module;
/**
* RPC Client session module.
* Do not directly create the object, use Client.connect.
*/
public class RPCSession {
private final Module session;
private final int tblIndex;
private final Map<String, Function> remoteFuncs = new HashMap<String, Function>();
RPCSession(Module sess) {
session = sess;
tblIndex = (int) RPC.getApi("SessTableIndex").pushArg(session).invoke().asLong();
}
/**
* Get function from the session.
* @param name The name of the function.
* @return The result function.
*/
public Function getFunction(String name) {
return session.getFunction(name);
}
/**
* Construct a remote device.
* @param devType device type.
* @param devId device id.
* @return The corresponding encoded remote device.
*/
public Device device(String devType, int devId) {
Device dev = new Device(devType, devId);
int encode = (tblIndex + 1) * RPC.RPC_SESS_MASK;
return new TVMRemoteDevice(dev.deviceType + encode, devId, this);
}
/**
* Construct a remote device.
* @param devType device type.
* @return The corresponding encoded remote device.
*/
public Device device(String devType) {
return device(devType, 0);
}
/**
* Construct a remote device.
* @param devType device type.
* @param devId device id.
* @return The corresponding encoded remote device.
*/
public Device device(int devType, int devId) {
int encode = (tblIndex + 1) * RPC.RPC_SESS_MASK;
return new TVMRemoteDevice(devType + encode, devId, this);
}
/**
* Construct a remote device.
* @param devType device type.
* @return The corresponding encoded remote device.
*/
public Device device(int devType) {
return device(devType, 0);
}
/**
* Construct remote CPU device.
* @param devId device id.
* @return Remote CPU device.
*/
public Device cpu(int devId) {
return Device.cpu(devId);
}
/**
* Construct remote CPU device.
* @return Remote CPU device.
*/
public Device cpu() {
return cpu(0);
}
/**
* Construct remote CUDA GPU device.
* @param devId device id.
* @return Remote CUDA GPU device.
*/
public Device cuda(int devId) {
return Device.cuda(devId);
}
/**
* Construct remote CUDA GPU device.
* @return Remote CUDA GPU device.
*/
public Device cuda() {
return cuda(0);
}
/**
* Construct remote OpenCL device.
* @param devId device id.
* @return Remote OpenCL device.
*/
public Device cl(int devId) {
return Device.opencl(devId);
}
/**
* Construct remote OpenCL device.
* @return Remote OpenCL device.
*/
public Device cl() {
return cl(0);
}
/**
* Construct remote OpenCL device.
* @param devId device id.
* @return Remote OpenCL device.
*/
public Device vulkan(int devId) {
return Device.vulkan(devId);
}
/**
* Construct remote OpenCL device.
* @return Remote OpenCL device.
*/
public Device vulkan() {
return vulkan(0);
}
/**
* Construct remote Metal device.
* @param devId device id.
* @return Remote metal device.
*/
public Device metal(int devId) {
return Device.metal(devId);
}
/**
* Construct remote Metal device.
* @return Remote metal device.
*/
public Device metal() {
return metal(0);
}
/**
* Upload binary to remote runtime temp folder.
* @param data The binary in local to upload.
* @param target The path in remote, cannot be null.
*/
public void upload(byte[] data, String target) {
if (target == null) {
throw new IllegalArgumentException("Please specify the upload target");
}
final String funcName = "upload";
Function remoteFunc = remoteFuncs.get(funcName);
if (remoteFunc == null) {
remoteFunc = getFunction("tvm.rpc.server.upload");
remoteFuncs.put(funcName, remoteFunc);
}
remoteFunc.pushArg(target).pushArg(data).invoke();
}
/**
* Upload file to remote runtime temp folder.
* @param data The file in local to upload.
* @param target The path in remote.
* @throws java.io.IOException for network failure.
*/
public void upload(File data, String target) throws IOException {
byte[] blob = getBytesFromFile(data);
upload(blob, target);
}
/**
* Upload file to remote runtime temp folder.
* @param data The file in local to upload.
* @throws java.io.IOException for network failure.
*/
public void upload(File data) throws IOException {
upload(data, data.getName());
}
/**
* Download file from remote temp folder.
* @param path The relative location to remote temp folder.
* @return The result blob from the file.
*/
public byte[] download(String path) {
final String name = "download";
Function func = remoteFuncs.get(name);
if (func == null) {
func = getFunction("tvm.rpc.server.download");
remoteFuncs.put(name, func);
}
return func.pushArg(path).invoke().asBytes();
}
/**
* Load a remote module, the file need to be uploaded first.
* @param path The relative location to remote temp folder.
* @return The remote module containing remote function.
*/
public Module loadModule(String path) {
return RPC.getApi("LoadRemoteModule").pushArg(session).pushArg(path).invoke().asModule();
}
private static byte[] getBytesFromFile(File file) throws IOException {
// Get the size of the file
long length = file.length();
if (length > Integer.MAX_VALUE) {
throw new IOException("File " + file.getName() + " is too large!");
}
// cannot create an array using a long type.
byte[] bytes = new byte[(int) length];
// Read in the bytes
int offset = 0;
int numRead = 0;
InputStream is = new FileInputStream(file);
try {
while (
offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
} finally {
is.close();
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read file " + file.getName());
}
return bytes;
}
}
@@ -0,0 +1,91 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
/**
* Watchdog for RPC.
*/
public class RPCWatchdog extends Thread {
private int timeout = 0;
private boolean started = false;
public RPCWatchdog() {
super();
}
/**
* Start a timeout with watchdog (must be called before finishTimeout).
* @param timeout watchdog timeout in ms.
*/
public synchronized void startTimeout(int timeout) {
this.timeout = timeout;
started = true;
this.notify();
}
/**
* Finish a timeout with watchdog (must be called after startTimeout).
*/
public synchronized void finishTimeout() {
started = false;
this.notify();
}
/**
* Wait and kill RPC if timeout is exceeded.
*/
@Override
public void run() {
while (true) {
// timeout not started
synchronized (this) {
while (!started) {
try {
this.wait();
} catch (InterruptedException e) {
System.err.println("watchdog interrupted...");
}
}
}
synchronized (this) {
while (started) {
try {
System.err.println("waiting for timeout: " + timeout);
this.wait(timeout);
if (!started) {
System.err.println("watchdog woken up, ok...");
} else {
System.err.println("watchdog woke up!");
System.err.println("terminating...");
terminate();
}
} catch (InterruptedException e) {
System.err.println("watchdog interrupted...");
}
}
}
}
}
/**
* Default method to terminate the running RPCActivity process.
*/
protected void terminate() {
System.exit(0);
}
}
@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.io.IOException;
/**
* RPC Server.
*/
public class Server {
private final WorkerThread worker;
private static class WorkerThread extends Thread {
private volatile boolean running = true;
private final ServerProcessor processor;
public WorkerThread(ServerProcessor processor) {
this.processor = processor;
}
@Override
public void run() {
while (running) {
processor.run();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void terminate() {
running = false;
processor.terminate();
}
}
/**
* Start a standalone server.
* @param serverPort Port.
* @throws IOException if failed to bind localhost:port.
*/
public Server(int serverPort) throws IOException {
worker = new WorkerThread(new StandaloneServerProcessor(serverPort));
}
/**
* Start a server connected to proxy.
* Use sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess
* to get file descriptor for the socket.
* @param proxyHost The proxy server host.
* @param proxyPort The proxy server port.
* @param key The key to identify the server.
*/
public Server(String proxyHost, int proxyPort, String key) {
worker = new WorkerThread(new ConnectProxyServerProcessor(proxyHost, proxyPort, key));
}
/**
* Start the server.
*/
public void start() {
worker.start();
}
/**
* Stop the server.
*/
public void terminate() {
worker.terminate();
}
}
@@ -0,0 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
/**
* Abstract runnable class for RPC server process.
*/
public interface ServerProcessor extends Runnable {
public void terminate();
}
@@ -0,0 +1,69 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.io.IOException;
import java.net.Socket;
import org.apache.tvm.Function;
import org.apache.tvm.TVMValue;
import org.apache.tvm.TVMValueBytes;
public class SocketChannel {
private final Socket socket;
SocketChannel(Socket sock) {
socket = sock;
}
private Function fsend = Function.convertFunc(new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
byte[] data = args[0].asBytes();
try {
socket.getOutputStream().write(data);
} catch (IOException e) {
e.printStackTrace();
return -1;
}
return data.length;
}
});
private Function frecv = Function.convertFunc(new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
long size = args[0].asLong();
try {
return new TVMValueBytes(Utils.recvAll(socket.getInputStream(), (int) size));
} catch (IOException e) {
e.printStackTrace();
return -1;
}
}
});
public Function getFsend() {
return fsend;
}
public Function getFrecv() {
return frecv;
}
}
@@ -0,0 +1,77 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Server processor for standalone.
*/
public class StandaloneServerProcessor implements ServerProcessor {
private final ServerSocket server;
public StandaloneServerProcessor(int serverPort) throws IOException {
this.server = new ServerSocket(serverPort);
}
@Override
public void terminate() {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
final Socket socket = server.accept();
final InputStream in = socket.getInputStream();
final OutputStream out = socket.getOutputStream();
int magic = Utils.wrapBytes(Utils.recvAll(in, 4)).getInt();
if (magic != RPC.RPC_MAGIC) {
Utils.closeQuietly(socket);
return;
}
int keyLen = Utils.wrapBytes(Utils.recvAll(in, 4)).getInt();
String key = Utils.decodeToStr(Utils.recvAll(in, keyLen));
if (!key.startsWith("client:")) {
out.write(Utils.toBytes(RPC.RPC_MAGIC + 2));
} else {
out.write(Utils.toBytes(RPC.RPC_MAGIC));
// send server key to the client
String serverKey = "server:java";
out.write(Utils.toBytes(serverKey.length()));
out.write(Utils.toBytes(serverKey));
}
SocketChannel sockChannel = new SocketChannel(socket);
System.err.println("Connection from " + socket.getRemoteSocketAddress().toString());
new NativeServerLoop(sockChannel.getFsend(), sockChannel.getFrecv()).run();
System.err.println("Finish serving " + socket.getRemoteSocketAddress().toString());
Utils.closeQuietly(socket);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import org.apache.tvm.Device;
// always related to RPCSession. Cannot construct by users.
public class TVMRemoteDevice extends Device {
public final RPCSession rpcSession;
TVMRemoteDevice(int deviceType, int deviceId, RPCSession rpcSession) {
super(deviceType, deviceId);
this.rpcSession = rpcSession;
}
}
@@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Utilities for RPC.
*/
class Utils {
public static byte[] recvAll(final InputStream in, final int numBytes) throws IOException {
byte[] res = new byte[numBytes];
int numRead = 0;
while (numRead < numBytes) {
int chunk = in.read(res, numRead, Math.min(numBytes - numRead, 1024));
numRead += chunk;
}
return res;
}
public static void closeQuietly(Socket socket) {
if (socket != null) {
try {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
} catch (IOException ioe) {
// close quietly, do nothing.
}
}
}
public static ByteBuffer wrapBytes(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
bb.order(ByteOrder.LITTLE_ENDIAN);
return bb;
}
public static byte[] toBytes(int number) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.order(ByteOrder.LITTLE_ENDIAN);
return bb.putInt(number).array();
}
public static byte[] toBytes(String str) {
byte[] bytes = new byte[str.length()];
for (int i = 0; i < str.length(); ++i) {
bytes[i] = (byte) str.charAt(i);
}
return bytes;
}
public static String decodeToStr(byte[] bytes) {
StringBuilder builder = new StringBuilder();
for (byte bt : bytes) {
builder.append((char) bt);
}
return builder.toString();
}
public static String recvString(InputStream in) throws IOException {
String recvString = null;
int len = wrapBytes(Utils.recvAll(in, 4)).getInt();
recvString = decodeToStr(Utils.recvAll(in, len));
return recvString;
}
public static void sendString(OutputStream out, String string) throws IOException {
out.write(toBytes(string.length()));
out.write(toBytes(string));
}
}
@@ -0,0 +1,131 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class FunctionTest {
@Test
public void test_reg_sum_number() {
Function.register("sum_number", new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
long res = 0L;
for (TVMValue arg : args) {
res += arg.asLong();
}
return res;
}
});
Function func = Function.getFunction("sum_number");
TVMValue res = func.pushArg(10).pushArg(20).invoke();
assertEquals(30, res.asLong());
res.release();
func.release();
}
@Test
public void test_add_string() {
System.err.println("[TEST] test_add_string");
Function func = Function.convertFunc(new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
String res = "";
for (TVMValue arg : args) {
res += arg.asString();
}
return res;
}
});
TVMValue res = func.pushArg("Hello").pushArg(" ").pushArg("World!").invoke();
assertEquals("Hello World!", res.asString());
res.release();
func.release();
}
@Test
public void test_sum_first_byte() {
Function func = Function.convertFunc(new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
byte[] bt = new byte[1];
for (TVMValue arg : args) {
bt[0] += arg.asBytes()[0];
}
return bt;
}
});
TVMValue res = func.pushArg(new byte[] {1}).pushArg(new byte[] {2, 3}).invoke();
assertArrayEquals(new byte[] {3}, res.asBytes());
res.release();
func.release();
}
@Test
public void test_sum_tensor() {
final long[] shape = new long[] {2, 1};
Function func = Function.convertFunc(new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
double sum = 0.0;
for (TVMValue arg : args) {
Tensor arr = Tensor.empty(shape, new TVMType("float32"));
arg.asTensor().copyTo(arr);
float[] nativeArr = arr.asFloatArray();
for (int i = 0; i < nativeArr.length; ++i) {
sum += nativeArr[i];
}
arr.release();
}
return sum;
}
});
Tensor arr = Tensor.empty(shape, new TVMType("float32"));
arr.copyFrom(new float[] {2f, 3f});
TVMValue res = func.pushArg(arr).pushArg(arr).invoke();
assertEquals(10.0, res.asDouble(), 1e-3);
res.release();
func.release();
}
@Test
public void test_return_function() {
Function myFunc = Function.convertFunc(new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
final long y = args[0].asLong();
return Function.convertFunc(new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
final long x = args[0].asLong();
return x + y;
}
});
}
});
Function func = myFunc.pushArg(10).invoke().asFunction();
TVMValue res = func.pushArg(20).invoke();
assertEquals(30, res.asLong());
func.release();
myFunc.release();
}
}
@@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import static org.junit.Assert.*;
import java.io.File;
import java.util.Random;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ModuleTest {
private final Logger logger = LoggerFactory.getLogger(ModuleTest.class);
private static String loadingDir;
@BeforeClass
public static void beforeClass() {
loadingDir = System.getProperty("test.tempdir");
}
@Test
public void test_load_add_func_cpu() {
Module fadd = Module.load(loadingDir + File.separator + "add_cpu.so");
Device dev = new Device("cpu", 0);
long[] shape = new long[] {2};
Tensor arr = Tensor.empty(shape, dev);
arr.copyFrom(new float[] {3f, 4f});
Tensor res = Tensor.empty(shape, dev);
fadd.entryFunc().pushArg(arr).pushArg(arr).pushArg(res).invoke();
assertArrayEquals(new float[] {6f, 8f}, res.asFloatArray(), 1e-3f);
// test call() api
fadd.entryFunc().call(arr, arr, res);
assertArrayEquals(new float[] {6f, 8f}, res.asFloatArray(), 1e-3f);
arr.release();
res.release();
fadd.release();
}
@Test
public void test_load_add_func_cuda() {
final Random RND = new Random(0);
Device dev = new Device("cuda", 0);
if (!dev.exist()) {
logger.warn("CUDA GPU does not exist. Skip the test.");
return;
}
Module fadd = Module.load(loadingDir + File.separator + "add_cuda.so");
final int dim = 100;
long[] shape = new long[] {dim};
Tensor arr = Tensor.empty(shape, dev);
float[] data = new float[dim];
float[] dataX2 = new float[dim];
for (int i = 0; i < dim; ++i) {
data[i] = RND.nextFloat();
dataX2[i] = data[i] * 2;
}
arr.copyFrom(data);
Tensor res = Tensor.empty(shape, dev);
fadd.entryFunc().pushArg(arr).pushArg(arr).pushArg(res).invoke();
assertArrayEquals(dataX2, res.asFloatArray(), 1e-3f);
arr.release();
res.release();
fadd.release();
}
}
@@ -0,0 +1,80 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm;
import static org.junit.Assert.*;
import org.junit.Test;
public class TensorTest {
@Test
public void test_from_float32() {
Tensor tensor = Tensor.empty(new long[] {2, 2}, new TVMType("float32"));
tensor.copyFrom(new float[] {1, 2, 3, 4});
assertArrayEquals(new float[] {1f, 2f, 3f, 4f}, tensor.asFloatArray(), 1e-3f);
tensor.release();
}
@Test
public void test_from_float64() {
Tensor tensor = Tensor.empty(new long[] {2, 2}, new TVMType("float64"));
tensor.copyFrom(new double[] {1, 2, 3, 4});
assertArrayEquals(new double[] {1.0, 2.0, 3.0, 4.0}, tensor.asDoubleArray(), 1e-3);
tensor.release();
}
@Test
public void test_from_int8() {
Tensor tensor = Tensor.empty(new long[] {2, 2}, new TVMType("int8"));
tensor.copyFrom(new byte[] {1, 2, 3, 4});
assertArrayEquals(new byte[] {1, 2, 3, 4}, tensor.asByteArray());
tensor.release();
}
@Test
public void test_from_int16() {
Tensor tensor = Tensor.empty(new long[] {2, 2}, new TVMType("int16"));
tensor.copyFrom(new short[] {1, 2, 3, 4});
assertArrayEquals(new short[] {1, 2, 3, 4}, tensor.asShortArray());
tensor.release();
}
@Test
public void test_from_int32() {
Tensor tensor = Tensor.empty(new long[] {2, 2}, new TVMType("int32"));
tensor.copyFrom(new int[] {1, 2, 3, 4});
assertArrayEquals(new int[] {1, 2, 3, 4}, tensor.asIntArray());
tensor.release();
}
@Test
public void test_from_int64() {
Tensor tensor = Tensor.empty(new long[] {2, 2}, new TVMType("int64"));
tensor.copyFrom(new long[] {1, 2, 3, 4});
assertArrayEquals(new long[] {1, 2, 3, 4}, tensor.asLongArray());
tensor.release();
}
@Test
public void test_from_uint16() {
Tensor tensor = Tensor.empty(new long[] {2, 2}, new TVMType("uint16"));
tensor.copyFrom(new char[] {65535, 2, 3, 4});
assertArrayEquals(new char[] {65535, 2, 3, 4}, tensor.asCharArray());
tensor.release();
}
}
@@ -0,0 +1,44 @@
package org.apache.tvm;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
import java.io.IOException;
import org.apache.tvm.rpc.Server;
public class TestUtils {
public static class RefInt {
public int value;
}
public static Server startServer(RefInt portRef) {
Server server = null;
int port = 9981;
for (int i = 0; i < 10; ++i) {
try {
server = new Server(port + i);
server.start();
portRef.value = port + i;
return server;
} catch (IOException e) {
}
}
throw new RuntimeException("Cannot find an available port.");
}
}
@@ -0,0 +1,120 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package org.apache.tvm.rpc;
import static org.junit.Assert.assertEquals;
import org.apache.tvm.Function;
import org.apache.tvm.Module;
import org.apache.tvm.TVMValue;
import org.apache.tvm.TestUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RPCTest {
private final Logger logger = LoggerFactory.getLogger(RPCTest.class);
@Ignore("RPC test is not enabled")
@Test
public void test_addone() {
if (!Module.enabled("rpc")) {
logger.warn("RPC is not enabled. Skip.");
return;
}
Function.register("test.rpc.addone", new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
return args[0].asLong() + 1L;
}
});
TestUtils.RefInt port = new TestUtils.RefInt();
Server server = null;
try {
server = TestUtils.startServer(port);
RPCSession client = Client.connect("127.0.0.1", port.value);
Function func = client.getFunction("test.rpc.addone");
assertEquals(11L, func.call(10).asLong());
} finally {
if (server != null) {
server.terminate();
}
}
}
@Ignore("RPC test is not enabled")
@Test
public void test_strcat() {
if (!Module.enabled("rpc")) {
logger.warn("RPC is not enabled. Skip.");
return;
}
Function.register("test.rpc.strcat", new Function.Callback() {
@Override
public Object invoke(TVMValue... args) {
return args[0].asString() + ":" + args[1].asLong();
}
});
TestUtils.RefInt port = new TestUtils.RefInt();
Server server = null;
try {
server = TestUtils.startServer(port);
RPCSession client = Client.connect("127.0.0.1", port.value);
Function func = client.getFunction("test.rpc.strcat");
assertEquals("abc:11", func.call("abc", 11L).asString());
} finally {
if (server != null) {
server.terminate();
}
}
}
@Ignore("Proxy server may not have been ready when this test runs,"
+ " will add retry when callback function can deal with Java exception."
+ " After that we'll enable this test.")
@Test
public void
test_connect_proxy_server() {
String proxyHost = System.getProperty("test.rpc.proxy.host");
int proxyPort = Integer.parseInt(System.getProperty("test.rpc.proxy.port"));
Function.register("test.rpc.proxy.addone", new Function.Callback() {
@Override
public Object invoke(TVMValue... tvmValues) {
return tvmValues[0].asLong() + 1L;
}
});
Server server = null;
try {
server = new Server(proxyHost, proxyPort, "x1");
server.start();
RPCSession client = Client.connect(proxyHost, proxyPort, "x1");
Function f1 = client.getFunction("test.rpc.proxy.addone");
assertEquals(11L, f1.call(10L).asLong());
} finally {
if (server != null) {
server.terminate();
}
}
}
}
@@ -0,0 +1,83 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
# Prepare test library for standalone wasm runtime test.
import os
import sys
import tvm
from tvm import relax, te
from tvm.script import relax as R
def prepare_relax_lib(base_path):
pipeline = relax.get_pipeline()
@tvm.script.ir_module
class Mod:
@R.function
def main(x: R.Tensor(["n"], "float32"), y: R.Tensor(["n"], "float32")):
lv0 = R.add(x, y)
return lv0
target = tvm.target.Target("llvm")
mod = pipeline(Mod)
ex = relax.build(mod, target)
relax_path = os.path.join(base_path, "add_relax.so")
ex.export_library(relax_path)
def prepare_cpu_lib(base_path):
target = "llvm"
if not tvm.runtime.enabled(target):
raise RuntimeError(f"Target {target} is not enbaled")
n = te.var("n")
A = te.placeholder((n,), name="A")
B = te.placeholder((n,), name="B")
C = te.compute(A.shape, lambda *i: A(*i) + B(*i), name="C")
mod = tvm.IRModule.from_expr(te.create_prim_func([A, B, C]).with_attr("global_symbol", "myadd"))
fadd = tvm.build(mod, target)
lib_path = os.path.join(base_path, "add_cpu.so")
fadd.export_library(lib_path)
def prepare_gpu_lib(base_path):
if not tvm.cuda().exist:
print("CUDA is not enabled, skip the generation")
return
n = te.var("n")
A = te.placeholder((n,), name="A")
B = te.placeholder((n,), name="B")
C = te.compute(A.shape, lambda *i: A(*i) + B(*i), name="C")
mod = tvm.IRModule.from_expr(te.create_prim_func([A, B, C]).with_attr("global_symbol", "myadd"))
sch = tvm.s_tir.Schedule(mod)
sch.work_on("myadd")
(i,) = sch.get_loops(block=sch.get_sblock("C"))
i0, i1 = sch.split(i, [None, 32])
sch.bind(i0, "blockIdx.x")
sch.bind(i1, "threadIdx.x")
fadd = tvm.build(sch.mod, "cuda")
lib_path = os.path.join(base_path, "add_cuda.so")
fadd.export_library(lib_path)
if __name__ == "__main__":
base_path = sys.argv[1]
prepare_cpu_lib(base_path)
prepare_gpu_lib(base_path)
prepare_relax_lib(base_path)
@@ -0,0 +1,39 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
from tvm.rpc import proxy
def start_proxy_server(port, timeout):
prox = proxy.Proxy("127.0.0.1", port=port, port_end=port + 1)
if timeout > 0:
import time
time.sleep(timeout)
prox.terminate()
else:
prox.proc.join()
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
sys.exit(-1)
port = int(sys.argv[1])
timeout = 0 if len(sys.argv) == 2 else float(sys.argv[2])
start_proxy_server(port, timeout)