diff --git a/Common/ML/CMakeLists.txt b/Common/ML/CMakeLists.txt index 0ed52e1a23e20..f552d33c31e8b 100644 --- a/Common/ML/CMakeLists.txt +++ b/Common/ML/CMakeLists.txt @@ -20,3 +20,38 @@ target_compile_definitions(${targetName} PRIVATE $<$:ORT_CUDA_BUILD> $<$:ORT_MIGRAPHX_BUILD> $<$:ORT_TENSORRT_BUILD>) + +if(BUILD_TESTING) + set(ONNXRUNTIME_INFERENCE_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test/onnxruntime-inference) + + o2_add_executable(onnxruntime-ep-inference + SOURCES ${ONNXRUNTIME_INFERENCE_TEST_DIR}/onnxruntime_ep_inference.cxx + COMPONENT_NAME ML + IS_TEST + NO_INSTALL + TARGETVARNAME onnxruntimeInferenceTestTarget + PUBLIC_LINK_LIBRARIES onnxruntime::onnxruntime) + target_compile_features(${onnxruntimeInferenceTestTarget} PRIVATE cxx_std_17) + target_compile_definitions(${onnxruntimeInferenceTestTarget} PRIVATE + $<$:ORT_CUDA_BUILD> + $<$:ORT_MIGRAPHX_BUILD> + $<$:ORT_TENSORRT_BUILD>) + + foreach(provider CUDA MIGRAPHX TENSORRT) + set(ONNXRUNTIME_INFERENCE_TEST_${provider} 0) + if(ORT_${provider}_BUILD) + set(ONNXRUNTIME_INFERENCE_TEST_${provider} 1) + endif() + endforeach() + + o2_add_test_command(NAME Common/ML/onnxruntime-inference + COMMAND ${ONNXRUNTIME_INFERENCE_TEST_DIR}/run-onnxruntime-all-eps.sh + COMMAND_LINE_ARGS ${ONNXRUNTIME_INFERENCE_TEST_DIR}/net.onnx + ENVIRONMENT + "ONNXRUNTIME_INFERENCE_TEST_BINARY=$" + "ORT_CUDA_BUILD=${ONNXRUNTIME_INFERENCE_TEST_CUDA}" + "ORT_MIGRAPHX_BUILD=${ONNXRUNTIME_INFERENCE_TEST_MIGRAPHX}" + "ORT_TENSORRT_BUILD=${ONNXRUNTIME_INFERENCE_TEST_TENSORRT}" + LABELS ml onnxruntime gpu + TIMEOUT 300) +endif() diff --git a/Common/ML/test/onnxruntime-inference/CMakeLists.txt b/Common/ML/test/onnxruntime-inference/CMakeLists.txt new file mode 100644 index 0000000000000..e865446441aa0 --- /dev/null +++ b/Common/ML/test/onnxruntime-inference/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +cmake_minimum_required(VERSION 3.16) +project(onnxruntime_inference_test LANGUAGES CXX) + +find_package(onnxruntime CONFIG REQUIRED) + +add_executable(onnxruntime-ep-inference onnxruntime_ep_inference.cxx) +target_compile_features(onnxruntime-ep-inference PRIVATE cxx_std_17) +target_link_libraries(onnxruntime-ep-inference PRIVATE onnxruntime::onnxruntime) +target_compile_definitions(onnxruntime-ep-inference PRIVATE + $<$:ORT_CUDA_BUILD> + $<$:ORT_MIGRAPHX_BUILD> + $<$:ORT_TENSORRT_BUILD>) + +install(TARGETS onnxruntime-ep-inference RUNTIME DESTINATION bin) +install(PROGRAMS + run-onnxruntime-all-eps.sh + run-onnxruntime-cpu.sh + run-onnxruntime-cuda.sh + run-onnxruntime-migraphx.sh + run-onnxruntime-tensorrt.sh + run-local-onnxruntime-inference-test.sh + DESTINATION bin) +install(FILES net.onnx DESTINATION test/onnxruntime-inference) diff --git a/Common/ML/test/onnxruntime-inference/net.onnx b/Common/ML/test/onnxruntime-inference/net.onnx new file mode 100644 index 0000000000000..448aa72dacacf Binary files /dev/null and b/Common/ML/test/onnxruntime-inference/net.onnx differ diff --git a/Common/ML/test/onnxruntime-inference/onnxruntime_ep_inference.cxx b/Common/ML/test/onnxruntime-inference/onnxruntime_ep_inference.cxx new file mode 100644 index 0000000000000..a066c193707a8 --- /dev/null +++ b/Common/ML/test/onnxruntime-inference/onnxruntime_ep_inference.cxx @@ -0,0 +1,352 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file onnxruntime_ep_inference.h +/// \author Christian Sonnabend +/// \brief A test script for inferencing an ONNX model with a specific execution provider + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct Arguments { + std::string modelPath; + std::string provider; + int deviceId = 0; + size_t expectedInputElements = 0; + size_t expectedOutputElements = 0; + bool requireProviderAssignment = true; +}; + +std::string toLower(std::string value) +{ + std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { + return static_cast(std::tolower(c)); + }); + return value; +} + +bool hasProvider(const std::vector& providers, const std::string& provider) +{ + return std::find(providers.begin(), providers.end(), provider) != providers.end(); +} + +std::string join(const std::vector& values) +{ + std::ostringstream os; + for (size_t i = 0; i < values.size(); ++i) { + os << (i == 0 ? "" : ", ") << values[i]; + } + return os.str(); +} + +void usage(const char* argv0) +{ + std::cerr << "usage: " << argv0 + << " --model MODEL.onnx --provider cpu|migraphx|cuda|tensorrt " + "[--device-id N] [--expected-input-elements N] " + "[--expected-output-elements N] [--allow-cpu-fallback]\n"; +} + +Arguments parseArguments(int argc, char** argv) +{ + Arguments args; + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + auto needValue = [&](const char* name) -> std::string { + if (i + 1 >= argc) { + throw std::runtime_error(std::string("missing value for ") + name); + } + return argv[++i]; + }; + + if (arg == "--model") { + args.modelPath = needValue("--model"); + } else if (arg == "--provider") { + args.provider = toLower(needValue("--provider")); + } else if (arg == "--device-id") { + args.deviceId = std::stoi(needValue("--device-id")); + } else if (arg == "--expected-input-elements") { + args.expectedInputElements = std::stoull(needValue("--expected-input-elements")); + } else if (arg == "--expected-output-elements") { + args.expectedOutputElements = std::stoull(needValue("--expected-output-elements")); + } else if (arg == "--allow-cpu-fallback") { + args.requireProviderAssignment = false; + } else if (arg == "--help" || arg == "-h") { + usage(argv[0]); + std::exit(0); + } else { + throw std::runtime_error("unknown argument: " + arg); + } + } + + if (args.modelPath.empty()) { + throw std::runtime_error("--model is required"); + } + if (args.provider != "cpu" && args.provider != "migraphx" && args.provider != "cuda" && args.provider != "tensorrt") { + throw std::runtime_error("--provider must be one of: cpu, migraphx, cuda, tensorrt"); + } + return args; +} + +std::string ortProviderName(const std::string& provider) +{ + if (provider == "cpu") { + return "CPUExecutionProvider"; + } + if (provider == "migraphx") { + return "MIGraphXExecutionProvider"; + } + if (provider == "cuda") { + return "CUDAExecutionProvider"; + } + if (provider == "tensorrt") { + return "TensorrtExecutionProvider"; + } + throw std::runtime_error("unsupported provider: " + provider); +} + +void appendProvider(Ort::SessionOptions& options, const Arguments& args) +{ + if (args.provider == "cpu") { + return; + } + if (args.provider == "cuda") { +#ifdef ORT_CUDA_BUILD + OrtCUDAProviderOptions cudaOptions{}; + cudaOptions.device_id = args.deviceId; + options.AppendExecutionProvider_CUDA(cudaOptions); + return; +#else + throw std::runtime_error("CUDA execution provider support was not enabled at build time"); +#endif + } + if (args.provider == "migraphx") { +#ifdef ORT_MIGRAPHX_BUILD + OrtMIGraphXProviderOptions migraphxOptions{}; + migraphxOptions.device_id = args.deviceId; + migraphxOptions.migraphx_mem_limit = std::numeric_limits::max(); + options.AppendExecutionProvider_MIGraphX(migraphxOptions); + return; +#else + throw std::runtime_error("MIGraphX execution provider support was not enabled at build time"); +#endif + } + if (args.provider == "tensorrt") { +#ifdef ORT_TENSORRT_BUILD + Ort::TensorRTProviderOptions tensorrtOptions; + tensorrtOptions.Update({{"device_id", std::to_string(args.deviceId)}}); + options.AppendExecutionProvider_TensorRT_V2(*tensorrtOptions); + return; +#else + throw std::runtime_error("TensorRT execution provider support was not enabled at build time"); +#endif + } +} + +std::vector concreteShape(std::vector shape) +{ + for (auto& dim : shape) { + if (dim <= 0) { + dim = 1; + } + } + return shape; +} + +size_t elementCount(const std::vector& shape) +{ + if (shape.empty()) { + return 1; + } + return std::accumulate(shape.begin(), shape.end(), size_t{1}, [](size_t product, int64_t dim) { + if (dim <= 0) { + throw std::runtime_error("invalid concrete tensor dimension"); + } + return product * static_cast(dim); + }); +} + +std::string shapeString(const std::vector& shape) +{ + std::ostringstream os; + os << "["; + for (size_t i = 0; i < shape.size(); ++i) { + os << (i == 0 ? "" : ",") << shape[i]; + } + os << "]"; + return os.str(); +} + +bool assignedToProvider(const Ort::Session& session, const std::string& providerName, size_t& assignedNodes) +{ + assignedNodes = 0; + for (const auto& subgraph : session.GetEpGraphAssignmentInfo()) { + if (subgraph.GetEpName() == providerName) { + assignedNodes += subgraph.GetNodes().size(); + } + } + return assignedNodes > 0; +} + +} // namespace + +int main(int argc, char** argv) +{ + try { + const auto args = parseArguments(argc, argv); + const auto providerName = ortProviderName(args.provider); + const auto availableProviders = Ort::GetAvailableProviders(); + if (!hasProvider(availableProviders, providerName)) { + throw std::runtime_error(providerName + " is not available in this ONNX Runtime build. Available providers: " + join(availableProviders)); + } + + Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "onnxruntime-ep-inference"); + Ort::SessionOptions options; + options.SetIntraOpNumThreads(1); + options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); + appendProvider(options, args); + + Ort::Session session(env, args.modelPath.c_str(), options); + size_t assignedNodes = 0; + if (args.provider != "cpu" && args.requireProviderAssignment && !assignedToProvider(session, providerName, assignedNodes)) { + throw std::runtime_error(providerName + " did not receive any graph nodes"); + } + + Ort::AllocatorWithDefaultOptions allocator; + Ort::MemoryInfo memoryInfo = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + + std::vector inputNames; + std::vector inputNamePointers; + std::vector> inputBuffers; + std::vector inputValues; + size_t totalInputElements = 0; + + const size_t inputCount = session.GetInputCount(); + if (inputCount == 0) { + throw std::runtime_error("model has no inputs"); + } + inputNames.reserve(inputCount); + inputNamePointers.reserve(inputCount); + inputBuffers.reserve(inputCount); + inputValues.reserve(inputCount); + + for (size_t i = 0; i < inputCount; ++i) { + auto name = session.GetInputNameAllocated(i, allocator); + inputNames.emplace_back(name.get()); + inputNamePointers.push_back(inputNames.back().c_str()); + + auto typeInfo = session.GetInputTypeInfo(i); + if (typeInfo.GetONNXType() != ONNX_TYPE_TENSOR) { + throw std::runtime_error("input " + inputNames.back() + " is not a tensor"); + } + auto tensorInfo = typeInfo.GetTensorTypeAndShapeInfo(); + if (tensorInfo.GetElementType() != ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) { + throw std::runtime_error("input " + inputNames.back() + " is not a float tensor"); + } + + const auto shape = concreteShape(tensorInfo.GetShape()); + const auto elements = elementCount(shape); + totalInputElements += elements; + inputBuffers.emplace_back(elements); + for (size_t j = 0; j < elements; ++j) { + inputBuffers.back()[j] = static_cast((static_cast((i + j) % 23) - 11) * 0.03125f); + } + inputValues.emplace_back(Ort::Value::CreateTensor( + memoryInfo, inputBuffers.back().data(), elements, shape.data(), shape.size())); + std::cout << "input[" << i << "] " << inputNames.back() << " shape=" << shapeString(shape) + << " elements=" << elements << "\n"; + } + + if (args.expectedInputElements != 0 && totalInputElements != args.expectedInputElements) { + throw std::runtime_error("model input element count is " + std::to_string(totalInputElements) + + ", expected " + std::to_string(args.expectedInputElements)); + } + + std::vector outputNames; + std::vector outputNamePointers; + const size_t outputCount = session.GetOutputCount(); + if (outputCount == 0) { + throw std::runtime_error("model has no outputs"); + } + outputNames.reserve(outputCount); + outputNamePointers.reserve(outputCount); + for (size_t i = 0; i < outputCount; ++i) { + auto name = session.GetOutputNameAllocated(i, allocator); + outputNames.emplace_back(name.get()); + outputNamePointers.push_back(outputNames.back().c_str()); + } + + auto outputs = session.Run(Ort::RunOptions{nullptr}, + inputNamePointers.data(), + inputValues.data(), + inputValues.size(), + outputNamePointers.data(), + outputNamePointers.size()); + + if (outputs.size() != outputCount) { + throw std::runtime_error("ONNX Runtime returned an unexpected number of outputs"); + } + + size_t totalOutputElements = 0; + for (size_t i = 0; i < outputs.size(); ++i) { + if (!outputs[i].IsTensor()) { + throw std::runtime_error("output " + outputNames[i] + " is not a tensor"); + } + auto tensorInfo = outputs[i].GetTensorTypeAndShapeInfo(); + if (tensorInfo.GetElementType() != ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) { + throw std::runtime_error("output " + outputNames[i] + " is not a float tensor"); + } + const auto shape = tensorInfo.GetShape(); + const auto elements = tensorInfo.GetElementCount(); + totalOutputElements += elements; + const float* data = outputs[i].GetTensorData(); + for (size_t j = 0; j < elements; ++j) { + if (!std::isfinite(data[j])) { + throw std::runtime_error("output " + outputNames[i] + " contains a non-finite value"); + } + } + std::cout << "output[" << i << "] " << outputNames[i] << " shape=" << shapeString(shape) + << " elements=" << elements << "\n"; + } + + if (args.expectedOutputElements != 0 && totalOutputElements != args.expectedOutputElements) { + throw std::runtime_error("model output element count is " + std::to_string(totalOutputElements) + + ", expected " + std::to_string(args.expectedOutputElements)); + } + + std::cout << "provider=" << providerName << " assigned_nodes=" << assignedNodes + << " total_inputs=" << totalInputElements + << " total_outputs=" << totalOutputElements << "\n"; + return 0; + } catch (const Ort::Exception& ex) { + std::cerr << "ONNX Runtime error: " << ex.what() << "\n"; + } catch (const std::exception& ex) { + std::cerr << "error: " << ex.what() << "\n"; + } + + usage(argv[0]); + return 1; +} diff --git a/Common/ML/test/onnxruntime-inference/run-local-onnxruntime-inference-test.sh b/Common/ML/test/onnxruntime-inference/run-local-onnxruntime-inference-test.sh new file mode 100755 index 0000000000000..c1ac4931133a4 --- /dev/null +++ b/Common/ML/test/onnxruntime-inference/run-local-onnxruntime-inference-test.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +usage: run-local-onnxruntime-inference-test.sh [options] + +Build and run the ONNX Runtime execution-provider inference smoke test in the +currently loaded aliBuild environment. If needed, the script re-runs itself in +an environment that provides ONNXRuntime, CMake, and Ninja. + +Options: + --model FILE ONNX model to test. Defaults to the bundled net.onnx. + --build-dir DIR Temporary CMake build dir. Defaults to /tmp. + --providers LIST Comma-separated providers to force, e.g. cpu,cuda. + By default, providers are selected from ort-init.sh. + --device-id N GPU device id passed to CUDA/MIGraphX/TensorRT tests. + --help Show this message. +EOF +} + +ORIGINAL_ARGS=("$@") +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +if [[ -f ${SCRIPT_DIR}/net.onnx ]]; then + MODEL=${SCRIPT_DIR}/net.onnx +else + MODEL=${SCRIPT_DIR}/../test/onnxruntime-inference/net.onnx +fi +BUILD_DIR=${TMPDIR:-/tmp}/onnxruntime-inference-test-local-${USER:-user} +PROVIDERS= +DEVICE_ID= + +while [[ $# -gt 0 ]]; do + case "$1" in + --model) + MODEL=$2 + shift 2 + ;; + --build-dir) + BUILD_DIR=$2 + shift 2 + ;; + --providers) + PROVIDERS=$2 + shift 2 + ;; + --device-id) + DEVICE_ID=$2 + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + *) + echo "run-local-onnxruntime-inference-test: unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [[ ! -f $MODEL ]]; then + echo "run-local-onnxruntime-inference-test: model not found: $MODEL" >&2 + exit 2 +fi + +detect_onnxruntime_root() { + [[ -n ${ONNXRUNTIME_ROOT:-} && -d $ONNXRUNTIME_ROOT/lib/cmake/onnxruntime ]] && return 0 + + IFS=: read -r -a SEARCH_PATHS <<< "${CMAKE_PREFIX_PATH:-}:${LD_LIBRARY_PATH:-}:${ROOT_INCLUDE_PATH:-}" + for PATH_ENTRY in "${SEARCH_PATHS[@]}"; do + CANDIDATE= + case "$PATH_ENTRY" in + */ONNXRuntime/*/lib) + CANDIDATE=${PATH_ENTRY%/lib} + ;; + */ONNXRuntime/*/include/onnxruntime) + CANDIDATE=${PATH_ENTRY%/include/onnxruntime} + ;; + */ONNXRuntime/*) + CANDIDATE=$PATH_ENTRY + ;; + esac + if [[ -n $CANDIDATE && -d $CANDIDATE/lib/cmake/onnxruntime ]]; then + export ONNXRUNTIME_ROOT=$CANDIDATE + return 0 + fi + done + return 1 +} + +if ! detect_onnxruntime_root || ! command -v cmake > /dev/null || ! command -v ninja > /dev/null; then + if [[ ${ONNXRUNTIME_INFERENCE_TEST_BOOTSTRAPPED:-0} != 1 ]] && command -v alienv > /dev/null; then + export ONNXRUNTIME_INFERENCE_TEST_BOOTSTRAPPED=1 + exec alienv setenv ONNXRuntime/latest,CMake/latest,ninja/latest \ + -c "$SCRIPT_DIR/run-local-onnxruntime-inference-test.sh" "${ORIGINAL_ARGS[@]}" + fi +fi + +if [[ -z ${ONNXRUNTIME_ROOT:-} ]]; then + echo "run-local-onnxruntime-inference-test: ONNXRUNTIME_ROOT is not set" >&2 + echo "Could not infer it from the loaded environment." >&2 + exit 2 +fi + +if [[ -f $ONNXRUNTIME_ROOT/etc/ort-init.sh ]]; then + source "$ONNXRUNTIME_ROOT/etc/ort-init.sh" +fi + +if [[ -n $PROVIDERS ]]; then + export ONNXRUNTIME_INFERENCE_TEST_PROVIDERS=$PROVIDERS +fi +if [[ -n $DEVICE_ID ]]; then + export ONNXRUNTIME_INFERENCE_TEST_DEVICE_ID=$DEVICE_ID +fi + +if [[ -n ${ONNXRUNTIME_INFERENCE_TEST_BINARY:-} ]]; then + : +elif [[ -x ${SCRIPT_DIR}/onnxruntime-ep-inference && ! -f ${SCRIPT_DIR}/CMakeLists.txt ]]; then + export ONNXRUNTIME_INFERENCE_TEST_BINARY="$SCRIPT_DIR/onnxruntime-ep-inference" +else + rm -Rf "$BUILD_DIR" + cmake -S "$SCRIPT_DIR" \ + -B "$BUILD_DIR" \ + -G Ninja \ + -Donnxruntime_DIR="$ONNXRUNTIME_ROOT/lib/cmake/onnxruntime" \ + -DORT_CUDA_BUILD="${ORT_CUDA_BUILD:-0}" \ + -DORT_MIGRAPHX_BUILD="${ORT_MIGRAPHX_BUILD:-0}" \ + -DORT_TENSORRT_BUILD="${ORT_TENSORRT_BUILD:-0}" + cmake --build "$BUILD_DIR" + export ONNXRUNTIME_INFERENCE_TEST_BINARY="$BUILD_DIR/onnxruntime-ep-inference" +fi +"$SCRIPT_DIR/run-onnxruntime-all-eps.sh" "$MODEL" diff --git a/Common/ML/test/onnxruntime-inference/run-onnxruntime-all-eps.sh b/Common/ML/test/onnxruntime-inference/run-onnxruntime-all-eps.sh new file mode 100755 index 0000000000000..cc230aa77ba14 --- /dev/null +++ b/Common/ML/test/onnxruntime-inference/run-onnxruntime-all-eps.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +MODEL=${1:-${SCRIPT_DIR}/net.onnx} +if [[ ! -f $MODEL ]]; then + echo "onnxruntime-inference-test: model not found: $MODEL" >&2 + exit 2 +fi + +if [[ -n ${ONNXRUNTIME_ROOT:-} && -f $ONNXRUNTIME_ROOT/etc/ort-init.sh ]]; then + source "$ONNXRUNTIME_ROOT/etc/ort-init.sh" +fi +if [[ -n ${GPU_SYSTEM_ROOT:-} && -f $GPU_SYSTEM_ROOT/etc/gpu-features-available.sh ]]; then + source "$GPU_SYSTEM_ROOT/etc/gpu-features-available.sh" +fi + +has_cuda_device() { + if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null 2>&1; then + return 0 + fi + compgen -G "/proc/driver/nvidia/gpus/*" >/dev/null +} + +has_rocm_device() { + if command -v rocm-smi >/dev/null 2>&1 && rocm-smi -i >/dev/null 2>&1; then + return 0 + fi + [[ -e /dev/kfd ]] && compgen -G "/dev/dri/renderD*" >/dev/null +} + +if [[ -n ${ONNXRUNTIME_INFERENCE_TEST_PROVIDERS:-} ]]; then + IFS=', ' read -r -a PROVIDERS <<< "$ONNXRUNTIME_INFERENCE_TEST_PROVIDERS" + for PROVIDER in "${PROVIDERS[@]}"; do + PROVIDER=${PROVIDER,,} + if [[ $PROVIDER == "cuda" && ${O2_GPU_CUDA_AVAILABLE:-0} == 1 ]] && ! has_cuda_device; then + echo "onnxruntime-inference-test: CUDA is available but no CUDA device was detected" >&2 + exit 1 + fi + if [[ $PROVIDER == "tensorrt" && ${O2_GPU_CUDA_AVAILABLE:-0} == 1 ]] && ! has_cuda_device; then + echo "onnxruntime-inference-test: TensorRT is available but no CUDA device was detected" >&2 + exit 1 + fi + if [[ $PROVIDER == "migraphx" && ${O2_GPU_ROCM_AVAILABLE:-0} == 1 ]] && ! has_rocm_device; then + echo "onnxruntime-inference-test: ROCm is available but no ROCm device was detected" >&2 + exit 1 + fi + done +else + PROVIDERS=(cpu) + if [[ ${ORT_MIGRAPHX_BUILD:-0} == 1 ]]; then + if has_rocm_device; then + PROVIDERS+=(migraphx) + elif [[ ${O2_GPU_ROCM_AVAILABLE:-0} == 1 ]]; then + echo "onnxruntime-inference-test: ROCm is available but no ROCm device was detected" >&2 + exit 1 + else + echo "onnxruntime-inference-test: skipping migraphx, no ROCm device detected" + fi + fi + if [[ ${ORT_CUDA_BUILD:-0} == 1 ]]; then + if has_cuda_device; then + PROVIDERS+=(cuda) + elif [[ ${O2_GPU_CUDA_AVAILABLE:-0} == 1 ]]; then + echo "onnxruntime-inference-test: CUDA is available but no CUDA device was detected" >&2 + exit 1 + else + echo "onnxruntime-inference-test: skipping cuda, no CUDA device detected" + fi + fi + if [[ ${ORT_TENSORRT_BUILD:-0} == 1 ]]; then + if has_cuda_device; then + PROVIDERS+=(tensorrt) + elif [[ ${O2_GPU_CUDA_AVAILABLE:-0} == 1 ]]; then + echo "onnxruntime-inference-test: TensorRT is available but no CUDA device was detected" >&2 + exit 1 + else + echo "onnxruntime-inference-test: skipping tensorrt, no CUDA device detected" + fi + fi +fi + +echo "onnxruntime-inference-test: selected providers: ${PROVIDERS[*]}" + +FAILURES=() +for PROVIDER in "${PROVIDERS[@]}"; do + [[ -n $PROVIDER ]] || continue + PROVIDER=${PROVIDER,,} + echo "onnxruntime-inference-test: running ${PROVIDER}" + if ! "${SCRIPT_DIR}/run-onnxruntime-${PROVIDER}.sh" "$MODEL"; then + FAILURES+=("$PROVIDER") + fi +done + +if [[ ${#FAILURES[@]} != 0 ]]; then + echo "onnxruntime-inference-test: failed providers: ${FAILURES[*]}" >&2 + exit 1 +fi + +echo "onnxruntime-inference-test: all providers passed" diff --git a/Common/ML/test/onnxruntime-inference/run-onnxruntime-cpu.sh b/Common/ML/test/onnxruntime-inference/run-onnxruntime-cpu.sh new file mode 100755 index 0000000000000..839995e88f6d4 --- /dev/null +++ b/Common/ML/test/onnxruntime-inference/run-onnxruntime-cpu.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +MODEL=${1:-${SCRIPT_DIR}/net.onnx} +if [[ ! -f $MODEL ]]; then + echo "onnxruntime-inference-test: model not found: $MODEL" >&2 + exit 2 +fi + +TESTER=${ONNXRUNTIME_INFERENCE_TEST_BINARY:-${SCRIPT_DIR}/onnxruntime-ep-inference} + +exec "$TESTER" \ + --model "$MODEL" \ + --provider cpu \ + --expected-input-elements "${ONNXRUNTIME_INFERENCE_TEST_EXPECTED_INPUTS:-246}" \ + --expected-output-elements "${ONNXRUNTIME_INFERENCE_TEST_EXPECTED_OUTPUTS:-7}" diff --git a/Common/ML/test/onnxruntime-inference/run-onnxruntime-cuda.sh b/Common/ML/test/onnxruntime-inference/run-onnxruntime-cuda.sh new file mode 100755 index 0000000000000..a8331000e4976 --- /dev/null +++ b/Common/ML/test/onnxruntime-inference/run-onnxruntime-cuda.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +MODEL=${1:-${SCRIPT_DIR}/net.onnx} +if [[ ! -f $MODEL ]]; then + echo "onnxruntime-inference-test: model not found: $MODEL" >&2 + exit 2 +fi + +TESTER=${ONNXRUNTIME_INFERENCE_TEST_BINARY:-${SCRIPT_DIR}/onnxruntime-ep-inference} + +if [[ -n ${GPU_SYSTEM_ROOT:-} && -f $GPU_SYSTEM_ROOT/etc/gpu-features-available.sh ]]; then + source "$GPU_SYSTEM_ROOT/etc/gpu-features-available.sh" +fi + +add_cuda_driver_path() { + local roots=() + local root candidate + + shopt -s nullglob + roots+=(/usr/local/cuda*) + shopt -u nullglob + [[ -n ${O2_GPU_CUDA_HOME:-} ]] && roots+=("$O2_GPU_CUDA_HOME") + roots+=(/usr/local/nvidia/lib64 /usr/local/nvidia/lib) + roots+=(/usr/lib64 /usr/lib/x86_64-linux-gnu /usr/lib/wsl/lib) + + for root in "${roots[@]}"; do + [[ -d $root ]] || continue + candidate=$(find "$root" -type d -name stubs -prune -false -o \ + \( -type f -o -type l \) \( -name libcuda.so -o -name libcuda.so.1 \) \ + -printf '%h\n' -quit 2>/dev/null || true) + if [[ -n $candidate ]]; then + export LD_LIBRARY_PATH="$candidate${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + echo "onnxruntime-inference-test: added CUDA driver library path: $candidate" + return 0 + fi + done + + echo "onnxruntime-inference-test: no CUDA driver library found; LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}" >&2 +} + +add_cuda_driver_path + +exec "$TESTER" \ + --model "$MODEL" \ + --provider cuda \ + --device-id "${ONNXRUNTIME_INFERENCE_TEST_DEVICE_ID:-0}" \ + --expected-input-elements "${ONNXRUNTIME_INFERENCE_TEST_EXPECTED_INPUTS:-246}" \ + --expected-output-elements "${ONNXRUNTIME_INFERENCE_TEST_EXPECTED_OUTPUTS:-7}" diff --git a/Common/ML/test/onnxruntime-inference/run-onnxruntime-migraphx.sh b/Common/ML/test/onnxruntime-inference/run-onnxruntime-migraphx.sh new file mode 100755 index 0000000000000..58cf870432b16 --- /dev/null +++ b/Common/ML/test/onnxruntime-inference/run-onnxruntime-migraphx.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +MODEL=${1:-${SCRIPT_DIR}/net.onnx} +if [[ ! -f $MODEL ]]; then + echo "onnxruntime-inference-test: model not found: $MODEL" >&2 + exit 2 +fi + +TESTER=${ONNXRUNTIME_INFERENCE_TEST_BINARY:-${SCRIPT_DIR}/onnxruntime-ep-inference} + +exec "$TESTER" \ + --model "$MODEL" \ + --provider migraphx \ + --device-id "${ONNXRUNTIME_INFERENCE_TEST_DEVICE_ID:-0}" \ + --expected-input-elements "${ONNXRUNTIME_INFERENCE_TEST_EXPECTED_INPUTS:-246}" \ + --expected-output-elements "${ONNXRUNTIME_INFERENCE_TEST_EXPECTED_OUTPUTS:-7}" diff --git a/Common/ML/test/onnxruntime-inference/run-onnxruntime-tensorrt.sh b/Common/ML/test/onnxruntime-inference/run-onnxruntime-tensorrt.sh new file mode 100755 index 0000000000000..d31df523e90a4 --- /dev/null +++ b/Common/ML/test/onnxruntime-inference/run-onnxruntime-tensorrt.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +MODEL=${1:-${SCRIPT_DIR}/net.onnx} +if [[ ! -f $MODEL ]]; then + echo "onnxruntime-inference-test: model not found: $MODEL" >&2 + exit 2 +fi + +TESTER=${ONNXRUNTIME_INFERENCE_TEST_BINARY:-${SCRIPT_DIR}/onnxruntime-ep-inference} + +exec "$TESTER" \ + --model "$MODEL" \ + --provider tensorrt \ + --device-id "${ONNXRUNTIME_INFERENCE_TEST_DEVICE_ID:-0}" \ + --expected-input-elements "${ONNXRUNTIME_INFERENCE_TEST_EXPECTED_INPUTS:-246}" \ + --expected-output-elements "${ONNXRUNTIME_INFERENCE_TEST_EXPECTED_OUTPUTS:-7}"