From a8265954d179f9a8472093d1d0bf52b23cd69297 Mon Sep 17 00:00:00 2001 From: baominghelly <1508269885@qq.com> Date: Wed, 5 Aug 2026 03:36:17 +0000 Subject: [PATCH 1/4] feat: add Cambricon hardware benchmarks --- docs/installation.md | 4 +- infinibench/hardware/README.md | 11 +- .../cambricon-memory-benchmark/CMakeLists.txt | 60 ++++ .../cambricon-memory-benchmark/build.sh | 54 +++ .../include/cache_benchmark.h | 306 +++++++++++++++++ .../include/cnrt_utils.h | 108 ++++++ .../include/memory_bandwidth_test.h | 258 ++++++++++++++ .../include/stream_benchmark.h | 318 ++++++++++++++++++ .../cambricon-memory-benchmark/src/main.mlu | 124 +++++++ infinibench/hardware/constants.py | 8 + infinibench/hardware/hardware_adapter.py | 59 +++- tests/test_hardware_adapter.py | 60 ++++ 12 files changed, 1363 insertions(+), 7 deletions(-) create mode 100644 infinibench/hardware/cambricon-memory-benchmark/CMakeLists.txt create mode 100755 infinibench/hardware/cambricon-memory-benchmark/build.sh create mode 100644 infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h create mode 100644 infinibench/hardware/cambricon-memory-benchmark/include/cnrt_utils.h create mode 100644 infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h create mode 100644 infinibench/hardware/cambricon-memory-benchmark/include/stream_benchmark.h create mode 100644 infinibench/hardware/cambricon-memory-benchmark/src/main.mlu diff --git a/docs/installation.md b/docs/installation.md index 17e62349..ddb8087d 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -34,8 +34,8 @@ cd infinibench/hardware/cuda-memory-benchmark bash build.sh --platform cuda ``` -For MetaX, Iluvatar, Hygon, and Moore Threads build and runtime instructions, -see [Hardware Benchmarks](../infinibench/hardware/README.md). +For MetaX, Iluvatar, Hygon, Moore Threads, and Cambricon build and runtime +instructions, see [Hardware Benchmarks](../infinibench/hardware/README.md). **Note**: This requires: - CUDA toolkit (compatible with your GPU driver) diff --git a/infinibench/hardware/README.md b/infinibench/hardware/README.md index 27573cfe..80ddd795 100644 --- a/infinibench/hardware/README.md +++ b/infinibench/hardware/README.md @@ -1,7 +1,7 @@ # Hardware Benchmarks -InfiniBench provides one hardware adapter for NVIDIA CUDA and four additional -CUDA-compatible accelerator platforms. Existing CUDA command shapes and metric +InfiniBench provides one hardware adapter for NVIDIA CUDA and five additional +accelerator platforms. Existing CUDA command shapes and metric names are kept unchanged. ## Platforms @@ -13,6 +13,7 @@ names are kept unchanged. | Iluvatar CoreX | `corex`, `iluvatar` | `bash build.sh --platform corex` | `cuda-memory-benchmark/build/cuda_perf_suite` | | Hygon DCU | `hygon` | `bash build.sh --platform hygon` | `cuda-memory-benchmark/build/cuda_perf_suite` | | Moore Threads | `moore` | `bash build.sh --platform moore` | `cuda-memory-benchmark/build/cuda_perf_suite` | +| Cambricon | `cambricon`, `mlu` | `bash build.sh` | `cambricon-memory-benchmark/build/mlu_perf_suite` | Run each build command from its benchmark directory. All binaries use the same test selectors and common arguments: @@ -44,8 +45,8 @@ example, Moore Threads STREAM uses: } ``` -The aliases `nvidia`, `musa`, and `mthreads` are also accepted as explicit -device values. A selected non-CUDA platform is recorded in the result +The aliases `nvidia`, `musa`, `mthreads`, and `mlu` are also accepted as +explicit device values. A selected non-CUDA platform is recorded in the result configuration as `platform`; metric names remain compatible with CUDA results. ## Device Visibility @@ -58,12 +59,14 @@ The selected physical device is renumbered to device 0 inside the process. | NVIDIA, MetaX, Iluvatar | `CUDA_VISIBLE_DEVICES` | | Hygon | `HIP_VISIBLE_DEVICES` and `ROCR_VISIBLE_DEVICES` | | Moore Threads | `MUSA_VISIBLE_DEVICES` | +| Cambricon | `MLU_VISIBLE_DEVICES` | For example: ```bash CUDA_VISIBLE_DEVICES=2 ./build/cuda_perf_suite --stream --device 0 MUSA_VISIBLE_DEVICES=0 ./build/cuda_perf_suite --stream --device 0 +MLU_VISIBLE_DEVICES=3 ./build/mlu_perf_suite --stream --device 0 ``` ## Container Notes diff --git a/infinibench/hardware/cambricon-memory-benchmark/CMakeLists.txt b/infinibench/hardware/cambricon-memory-benchmark/CMakeLists.txt new file mode 100644 index 00000000..c3265954 --- /dev/null +++ b/infinibench/hardware/cambricon-memory-benchmark/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.18) +project(MluPerfSuite VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Find NEUWARE / CNToolkit +if(DEFINED ENV{NEUWARE_HOME}) + set(NEUWARE_HOME $ENV{NEUWARE_HOME}) +else() + set(NEUWARE_HOME "/usr/local/neuware") +endif() +message(STATUS "NEUWARE_HOME: ${NEUWARE_HOME}") + +# Find cncc compiler (used as C++ compiler for Cambricon) +find_program(CNCC cncc HINTS ${NEUWARE_HOME}/bin) +if(NOT CNCC) + message(FATAL_ERROR "cncc not found. Set NEUWARE_HOME.") +endif() +message(STATUS "Found cncc: ${CNCC}") + +# Find CNRT library +find_library(CNRT_LIB cnrt HINTS ${NEUWARE_HOME}/lib64 ${NEUWARE_HOME}/lib) +if(NOT CNRT_LIB) + message(FATAL_ERROR "libcnrt not found in ${NEUWARE_HOME}") +endif() + +# Find CNRT headers +find_path(CNRT_INCLUDE_DIR NAMES cnrt.h HINTS ${NEUWARE_HOME}/include) +if(NOT CNRT_INCLUDE_DIR) + message(FATAL_ERROR "cnrt.h not found in ${NEUWARE_HOME}/include") +endif() + +add_executable(mlu_perf_suite src/main.mlu) +target_include_directories(mlu_perf_suite PRIVATE + ${CNRT_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include +) +target_link_libraries(mlu_perf_suite ${CNRT_LIB} pthread) + +# Use cncc as compiler +set_source_files_properties(src/main.mlu PROPERTIES LANGUAGE CXX) +set_target_properties(mlu_perf_suite PROPERTIES + CXX_COMPILER_LAUNCHER ${CNCC} + RULE_LAUNCH_COMPILE "${CNCC}" +) + +install(TARGETS mlu_perf_suite RUNTIME DESTINATION bin) + +message(STATUS "") +message(STATUS "Configuration Summary:") +message(STATUS " Project: ${PROJECT_NAME} v${PROJECT_VERSION}") +message(STATUS " Build: ${CMAKE_BUILD_TYPE}") +message(STATUS " cncc: ${CNCC}") +message(STATUS " CNRT: ${CNRT_LIB}") +message(STATUS "") diff --git a/infinibench/hardware/cambricon-memory-benchmark/build.sh b/infinibench/hardware/cambricon-memory-benchmark/build.sh new file mode 100755 index 00000000..17517f1a --- /dev/null +++ b/infinibench/hardware/cambricon-memory-benchmark/build.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -e + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo "==========================================" +echo " MLU Performance Suite - Build Script" +echo "==========================================" +echo "" + +if ! command -v cncc &> /dev/null; then + echo -e "${RED}ERROR: cncc not found. Install CNToolkit.${NC}" + exit 1 +fi + +if [ -z "${NEUWARE_HOME}" ]; then + export NEUWARE_HOME="/usr/local/neuware" +fi + +# MLU architecture - must be set for device kernel compilation +if [ -z "${MLU_ARCH}" ]; then + MLU_ARCH="mtp_592" +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BUILD_DIR="${SCRIPT_DIR}/build" + +mkdir -p "${BUILD_DIR}" + +echo -e "${YELLOW}Compiling (arch=${MLU_ARCH})...${NC}" +cncc "${SCRIPT_DIR}/src/main.mlu" \ + -o "${BUILD_DIR}/mlu_perf_suite" \ + -O3 -std=c++17 \ + --bang-mlu-arch="${MLU_ARCH}" \ + -I"${NEUWARE_HOME}/include" \ + -I"${SCRIPT_DIR}/include" \ + -L"${NEUWARE_HOME}/lib64" \ + -lcnrt -lstdc++ -lm + +echo "" +echo -e "${GREEN}Build succeeded!${NC}" +echo "" +echo "Executable: ${BUILD_DIR}/mlu_perf_suite" +echo "" +echo "Usage:" +echo " ${BUILD_DIR}/mlu_perf_suite --all" +echo " ${BUILD_DIR}/mlu_perf_suite --memory" +echo " ${BUILD_DIR}/mlu_perf_suite --stream" +echo " ${BUILD_DIR}/mlu_perf_suite --cache" +echo " MLU_ARCH=mtp_592 ./build.sh # override arch" +echo "" diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h b/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h new file mode 100644 index 00000000..b2e0c56a --- /dev/null +++ b/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h @@ -0,0 +1,306 @@ +#pragma once + +#include "cnrt_utils.h" + +namespace mlu_perf { + +#define NRAM_MAX_CB (1024 * 240) +#define ALIGN_CB 128 + +// ============================================================ +// NRAM Bandwidth Kernel (对标 CUDA L1 Cache) +// ============================================================ +// 数据加载到 NRAM 后,用 __bang_add 反复做 NRAM 内的向量加。 +// 和 CUDA L1 测试对齐:只计 reads,用大量 repeat 放大时间。 +// 每个 __bang_add(dst, src0, src1, n) = 2 reads + 1 write + +template +__mlu_global__ void nram_add_kernel(T* dst, const T* src, size_t n, + int repeat) { + __nram__ char nram_raw[NRAM_MAX_CB]; + char* aligned = (char*)(((size_t)nram_raw + ALIGN_CB - 1) & ~(ALIGN_CB - 1)); + size_t usable = NRAM_MAX_CB - (aligned - nram_raw); + // 2 buffers: input + output + size_t chunk = usable / (2 * sizeof(T)); + chunk = (chunk / (ALIGN_CB / sizeof(T))) * (ALIGN_CB / sizeof(T)); + if (chunk == 0) return; + + T* buf_a = (T*)aligned; + T* buf_b = buf_a + chunk; + + size_t per_core = (n + taskDim - 1) / taskDim; + size_t start = taskId * per_core; + size_t end = start + per_core > n ? n : start + per_core; + if (start >= end) return; + + size_t cnt = end - start; + if (cnt > chunk) cnt = chunk; + __memcpy(buf_a, src + start, cnt * sizeof(T), GDRAM2NRAM); + + // Repeat __bang_add alternating between two buffers + for (int r = 0; r < repeat; r++) { + __bang_add(buf_b, buf_a, buf_a, cnt); + __bang_add(buf_a, buf_b, buf_b, cnt); + } + + __memcpy(dst + start, buf_a, cnt * sizeof(T), NRAM2GDRAM); +} + +// ============================================================ +// GDRAM<->NRAM Copy Kernel (用于 L2 Cache 测试) +// ============================================================ + +template +__mlu_global__ void cache_rw_kernel(T* dst, const T* src, size_t n, int repeat) { + __nram__ char nram_raw[NRAM_MAX_CB]; + char* aligned = (char*)(((size_t)nram_raw + ALIGN_CB - 1) & ~(ALIGN_CB - 1)); + size_t usable = NRAM_MAX_CB - (aligned - nram_raw); + size_t chunk = usable / sizeof(T); + chunk = (chunk / (ALIGN_CB / sizeof(T))) * (ALIGN_CB / sizeof(T)); + if (chunk == 0) return; + + T* buf = (T*)aligned; + + size_t per_core = (n + taskDim - 1) / taskDim; + size_t start = taskId * per_core; + size_t end = start + per_core > n ? n : start + per_core; + if (start >= end) return; + + for (int r = 0; r < repeat; r++) { + for (size_t off = start; off < end; off += chunk) { + size_t c = off + chunk > end ? end - off : chunk; + __memcpy(buf, src + off, c * sizeof(T), GDRAM2NRAM); + __memcpy(dst + off, buf, c * sizeof(T), NRAM2GDRAM); + } + } +} + +// ============================================================ +// NRAM Bandwidth Sweep Test (对标 CUDA L1 Cache Sweep) +// ============================================================ + +class NRAMBandwidthTest { +public: + void execute(const TestConfig& cfg = TestConfig()) { + MLU_CHECK(cnrtSetDevice(cfg.device_id)); + + cnrtQueue_t queue; + MLU_CHECK(cnrtQueueCreate(&queue)); + + cnrtDeviceProp_t prop; + MLU_CHECK(cnrtGetDeviceProperties(&prop, cfg.device_id)); + int total_cores = prop.clusterCount * prop.McorePerCluster; + + cnrtDim3_t dim; + dim.x = prop.McorePerCluster; + dim.y = prop.clusterCount; + dim.z = 1; + cnrtFunctionType_t k_type = cnrtFuncTypeUnion1; + + using T = float; + int warmup = cfg.warmup_iterations; + int measure = cfg.measure_iterations; + + std::cout << "\n===================================================\n"; + std::cout << "NRAM Bandwidth Test (BANG Kernel)\n"; + std::cout << "Cores: " << total_cores << "\n"; + std::cout << "===================================================\n\n"; + + // Use max NRAM chunk: 2 buffers in 240KB → ~120KB each + size_t nram_bytes = NRAM_MAX_CB - ALIGN_CB; + size_t chunk = nram_bytes / (2 * sizeof(T)); + chunk = (chunk / (ALIGN_CB / sizeof(T))) * (ALIGN_CB / sizeof(T)); + size_t chunk_bytes = chunk * sizeof(T); + + // Large repeat to amortize per-call overhead + // Aligned with CUDA L1: ~1e9 / ARRAY_N + 2 + size_t repeat_count = 1000000000ULL / chunk + 2; + + void* src = nullptr; + void* dst = nullptr; + MLU_CHECK(cnrtMalloc(&src, chunk_bytes)); + MLU_CHECK(cnrtMalloc(&dst, chunk_bytes)); + + std::cout << "Chunk size per core: " << (chunk_bytes / 1024) << " kB\n"; + std::cout << "Repeat count: " << repeat_count << "\n\n"; + + // Warmup + for (int i = 0; i < warmup; ++i) { + nram_add_kernel<<>>( + (T*)dst, (const T*)src, chunk, (int)repeat_count); + MLU_CHECK(cnrtQueueSync(queue)); + } + + // Measure + PerfMetrics bw_metrics; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + nram_add_kernel<<>>( + (T*)dst, (const T*)src, chunk, (int)repeat_count); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + double sec = us / 1e6; + + // Aligned with CUDA L1: data_volume × grid_count × repeat_count / time + // data_volume = 2 reads × chunk_bytes per iter + double data_volume = 2.0 * chunk_bytes; + double total_bw = data_volume * total_cores * repeat_count / sec / 1e9; + bw_metrics.add(total_bw); + + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + + double avg_bw = bw_metrics.trimmed_mean(); + double avg_time_sec = (2.0 * chunk_bytes * total_cores * repeat_count / 1e9) + / avg_bw; + + // Also compute TFLOPS: 2 adds per iter, each is 1 FLOP per element + double total_flops = 2.0 * chunk * total_cores * repeat_count; + double tflops = total_flops / avg_time_sec / 1e12; + + std::cout << std::left << std::setw(20) << "NRAM chunk/core" + << std::right << std::setw(12) << "Time (ms)" + << std::setw(15) << "Eff. BW (GB/s)" + << std::setw(12) << "TFLOPS" + << std::setw(10) << "Spread\n"; + std::cout << std::string(69, '-') << "\n"; + + std::cout << std::fixed << std::setprecision(1); + std::cout << std::left << std::setw(20) + << std::to_string(chunk_bytes / 1024) + " kB"; + std::cout << std::right << std::setw(12) << std::setprecision(1) + << avg_time_sec * 1000; + std::cout << std::setw(15) << std::setprecision(1) << avg_bw; + std::cout << std::setw(12) << std::setprecision(1) << tflops; + std::cout << std::setw(10) << std::setprecision(1) + << (bw_metrics.cv() * 100.0) << "%\n"; + + cnrtFree(src); + cnrtFree(dst); + MLU_CHECK(cnrtQueueDestroy(queue)); + std::cout << "\n"; + } +}; + +// ============================================================ +// L2 Cache Bandwidth Sweep Test (对标 CUDA L2 Cache Sweep) +// ============================================================ + +class L2CacheBandwidthTest { +public: + void execute(const TestConfig& cfg = TestConfig()) { + MLU_CHECK(cnrtSetDevice(cfg.device_id)); + + cnrtQueue_t queue; + MLU_CHECK(cnrtQueueCreate(&queue)); + + cnrtDeviceProp_t prop; + MLU_CHECK(cnrtGetDeviceProperties(&prop, cfg.device_id)); + int total_cores = prop.clusterCount * prop.McorePerCluster; + size_t l2_bytes = prop.maxL2CacheSize; + + cnrtDim3_t dim; + dim.x = prop.McorePerCluster; + dim.y = prop.clusterCount; + dim.z = 1; + cnrtFunctionType_t k_type = cnrtFuncTypeUnion1; + + using T = float; + const int repeat = 10; + int warmup = cfg.warmup_iterations; + int measure = cfg.measure_iterations; + + std::cout << "\n===================================================\n"; + std::cout << "L2 Cache Bandwidth Sweep Test (BANG Kernel)\n"; + std::cout << "Cores: " << total_cores + << ", L2 Cache: " << (l2_bytes / 1024) << " kB\n"; + std::cout << "===================================================\n\n"; + + std::cout << std::left << std::setw(13) << "data set" + << std::setw(12) << "exec data" + << std::right << std::setw(12) << "exec time" + << std::setw(11) << "spread" + << std::setw(15) << "Eff. bw\n"; + std::cout << std::string(63, '-') << "\n"; + + // Sweep from 256KB to 128MB + // L2 is ~40MB, so <40MB should show high bandwidth (L2 hit) + // >40MB should show lower bandwidth (L2 miss → DRAM) + std::vector sizes_kb; + for (size_t s = 256; s <= 8192; s *= 2) sizes_kb.push_back(s); + for (size_t s = 10240; s <= 65536; s += 4096) sizes_kb.push_back(s); + for (size_t s = 65536; s <= 131072; s *= 2) sizes_kb.push_back(s); + + size_t max_bytes = 256ULL * 1024 * 1024; + void* src = nullptr; + void* dst = nullptr; + MLU_CHECK(cnrtMalloc(&src, max_bytes)); + MLU_CHECK(cnrtMalloc(&dst, max_bytes)); + + for (size_t skb : sizes_kb) { + size_t bytes = skb * 1024; + if (bytes > max_bytes) break; + size_t n = bytes / sizeof(T); + + // Warmup + for (int i = 0; i < warmup; ++i) { + cache_rw_kernel<<>>( + (T*)dst, (const T*)src, n, repeat); + MLU_CHECK(cnrtQueueSync(queue)); + } + + // Measure + PerfMetrics time_metrics; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + cache_rw_kernel<<>>( + (T*)dst, (const T*)src, n, repeat); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + time_metrics.add(us / 1e3); // ms + + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + + double avg_time_ms = time_metrics.trimmed_mean(); + double total_data = 2.0 * bytes * repeat; + double bw_gbps = total_data / (avg_time_ms / 1e3) / 1e9; + + std::cout << std::fixed << std::setprecision(0); + std::cout << std::left << std::setw(13) + << std::to_string(bytes / 1024) + " kB"; + std::cout << std::setw(12) + << std::to_string(bytes * repeat / 1024) + " kB"; + std::cout << std::right << std::setw(12) + << std::setprecision(0) << avg_time_ms << "ms"; + std::cout << std::setprecision(1) << std::setw(11) + << (time_metrics.cv() * 100.0) << "%"; + std::cout << std::setprecision(1) << std::setw(15) + << bw_gbps << " GB/s"; + std::cout << "\n"; + } + + cnrtFree(src); + cnrtFree(dst); + MLU_CHECK(cnrtQueueDestroy(queue)); + std::cout << "\n"; + } +}; + +} // namespace mlu_perf diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/cnrt_utils.h b/infinibench/hardware/cambricon-memory-benchmark/include/cnrt_utils.h new file mode 100644 index 00000000..fee1b383 --- /dev/null +++ b/infinibench/hardware/cambricon-memory-benchmark/include/cnrt_utils.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mlu_perf { + +// Own check macro with exceptions (cnrt.h's CNRT_CHECK calls exit) +#define MLU_CHECK(call) \ + do { \ + cnrtRet_t ret = call; \ + if (ret != cnrtSuccess) { \ + std::ostringstream oss; \ + oss << "CNRT error at " << __FILE__ << ":" << __LINE__ \ + << ": " << cnrtGetErrorStr(ret) \ + << " (code=" << ret << ")"; \ + throw std::runtime_error(oss.str()); \ + } \ + } while(0) + +// Host-side high resolution timer +class Timer { +public: + using Clock = std::chrono::high_resolution_clock; + using TimePoint = std::chrono::time_point; + + Timer() : start_(Clock::now()) {} + void reset() { start_ = Clock::now(); } + + double elapsed_seconds() const { + return std::chrono::duration(Clock::now() - start_).count(); + } + double elapsed_ms() const { return elapsed_seconds() * 1000.0; } + +private: + TimePoint start_; +}; + +// Statistics +class PerfMetrics { +public: + void add(double v) { samples_.push_back(v); } + + double mean() const { + if (samples_.empty()) return 0.0; + return std::accumulate(samples_.begin(), samples_.end(), 0.0) / samples_.size(); + } + + double trimmed_mean() const { + if (samples_.size() <= 2) return mean(); + auto s = samples_; + std::sort(s.begin(), s.end()); + return std::accumulate(s.begin() + 1, s.end() - 1, 0.0) / (s.size() - 2); + } + + double cv() const { + double avg = mean(); + if (avg == 0.0) return 0.0; + double var = 0.0; + for (double v : samples_) var += (v - avg) * (v - avg); + var /= samples_.size(); + return std::sqrt(var) / avg; + } + +private: + std::vector samples_; +}; + +struct TestConfig { + int warmup_iterations = 5; + int measure_iterations = 10; + int device_id = 0; + bool verbose = true; +}; + +// Device info +struct MluDeviceInfo { + static void print(int device_id = 0) { + cnrtDeviceProp_t prop; + MLU_CHECK(cnrtGetDeviceProperties(&prop, device_id)); + + std::cout << "Device " << device_id << ": " << prop.name << "\n"; + std::cout << " Total Memory: " + << prop.totalMem << " MB\n"; + std::cout << " L2 Cache Size: " + << (prop.maxL2CacheSize / 1024.0) << " KB\n"; + std::cout << " Clusters: " << prop.clusterCount << "\n"; + std::cout << " Cores per Cluster: " << prop.McorePerCluster << "\n"; + std::cout << " Max Block Dim X: " << prop.maxDim[0] << "\n"; + } +}; + +inline int get_device_count() { + unsigned int count; + MLU_CHECK(cnrtGetDeviceCount(&count)); + return static_cast(count); +} + +} // namespace mlu_perf diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h b/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h new file mode 100644 index 00000000..6dc6ef07 --- /dev/null +++ b/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h @@ -0,0 +1,258 @@ +#pragma once + +#include "cnrt_utils.h" + +namespace mlu_perf { + +class MemoryBandwidthTest { +public: + void execute(const TestConfig& cfg = TestConfig()) { + MLU_CHECK(cnrtSetDevice(cfg.device_id)); + + const size_t max_bytes = 2ULL * 1024 * 1024 * 1024; // 2 GB + const int warmup = cfg.warmup_iterations; + const int measure = cfg.measure_iterations; + + cnrtQueue_t queue; + MLU_CHECK(cnrtQueueCreate(&queue)); + + void* host_buf; + void* dev1; + void* dev2; + MLU_CHECK(cnrtHostMalloc(&host_buf, max_bytes)); + MLU_CHECK(cnrtMalloc(&dev1, max_bytes)); + MLU_CHECK(cnrtMalloc(&dev2, max_bytes)); + + memset(host_buf, 0xAB, max_bytes); + MLU_CHECK(cnrtMemcpy(dev1, host_buf, max_bytes, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtMemcpy(dev2, host_buf, max_bytes, cnrtMemcpyHostToDev)); + + std::vector sizes_kb = { + 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, + 32768, 65536, 131072, 262144, 524288, 1048576 + }; + + auto print_table_header = [&]() { + std::cout << std::left << std::setw(15) << "Size (MB)" + << std::right << std::setw(12) << "Time (ms)" + << std::setw(18) << "Bandwidth (GB/s)" + << std::setw(10) << "CV (%)\n"; + std::cout << std::string(55, '-') << "\n"; + }; + + // ---- H2D ---- + std::cout << "\n===================================================\n"; + std::cout << "Memory Copy Bandwidth Sweep Test\n"; + std::cout << "Direction: Host to Device\n"; + std::cout << "===================================================\n\n"; + print_table_header(); + for (size_t skb : sizes_kb) { + size_t bytes = skb * 1024; + if (bytes > max_bytes) break; + + for (int i = 0; i < warmup; ++i) { + MLU_CHECK(cnrtMemcpyAsync(dev1, host_buf, bytes, queue, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtQueueSync(queue)); + } + + PerfMetrics bw; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + MLU_CHECK(cnrtMemcpyAsync(dev1, host_buf, bytes, queue, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + double sec = us / 1e6; + bw.add((bytes / 1e9) / sec); + + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + + double avg_bw = bw.trimmed_mean(); + double avg_time_ms = (bytes / 1e9) / avg_bw * 1000; + std::cout << std::fixed << std::setprecision(2); + std::cout << std::left << std::setw(15) << (bytes / 1024.0 / 1024.0); + std::cout << std::right << std::setw(12) << std::setprecision(3) << avg_time_ms; + std::cout << std::setw(18) << std::setprecision(2) << avg_bw; + std::cout << std::setw(10) << std::setprecision(1) + << (bw.cv() * 100.0) << "\n"; + } + std::cout << "\n"; + + // ---- D2H ---- + std::cout << "===================================================\n"; + std::cout << "Memory Copy Bandwidth Sweep Test\n"; + std::cout << "Direction: Device to Host\n"; + std::cout << "===================================================\n\n"; + print_table_header(); + for (size_t skb : sizes_kb) { + size_t bytes = skb * 1024; + if (bytes > max_bytes) break; + + for (int i = 0; i < warmup; ++i) { + MLU_CHECK(cnrtMemcpyAsync(host_buf, dev1, bytes, queue, cnrtMemcpyDevToHost)); + MLU_CHECK(cnrtQueueSync(queue)); + } + + PerfMetrics bw; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + MLU_CHECK(cnrtMemcpyAsync(host_buf, dev1, bytes, queue, cnrtMemcpyDevToHost)); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + double sec = us / 1e6; + bw.add((bytes / 1e9) / sec); + + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + + double avg_bw = bw.trimmed_mean(); + double avg_time_ms = (bytes / 1e9) / avg_bw * 1000; + std::cout << std::fixed << std::setprecision(2); + std::cout << std::left << std::setw(15) << (bytes / 1024.0 / 1024.0); + std::cout << std::right << std::setw(12) << std::setprecision(3) << avg_time_ms; + std::cout << std::setw(18) << std::setprecision(2) << avg_bw; + std::cout << std::setw(10) << std::setprecision(1) + << (bw.cv() * 100.0) << "\n"; + } + std::cout << "\n"; + + // ---- D2D ---- + std::cout << "===================================================\n"; + std::cout << "Memory Copy Bandwidth Sweep Test\n"; + std::cout << "Direction: Device to Device\n"; + std::cout << "===================================================\n\n"; + std::cout << "NOTE: Small sizes may reflect cache bandwidth, not DRAM bandwidth.\n\n"; + print_table_header(); + for (size_t skb : sizes_kb) { + size_t bytes = skb * 1024; + if (bytes > max_bytes) break; + + for (int i = 0; i < warmup; ++i) { + MLU_CHECK(cnrtMemcpyAsync(dev2, dev1, bytes, queue, cnrtMemcpyDevToDev)); + MLU_CHECK(cnrtQueueSync(queue)); + } + + PerfMetrics bw; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + MLU_CHECK(cnrtMemcpyAsync(dev2, dev1, bytes, queue, cnrtMemcpyDevToDev)); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + double sec = us / 1e6; + bw.add((bytes / 1e9) / sec); + + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + + double avg_bw = bw.trimmed_mean(); + double avg_time_ms = (bytes / 1e9) / avg_bw * 1000; + std::cout << std::fixed << std::setprecision(2); + std::cout << std::left << std::setw(15) << (bytes / 1024.0 / 1024.0); + std::cout << std::right << std::setw(12) << std::setprecision(3) << avg_time_ms; + std::cout << std::setw(18) << std::setprecision(2) << avg_bw; + std::cout << std::setw(10) << std::setprecision(1) + << (bw.cv() * 100.0) << "\n"; + } + std::cout << "\n"; + + // ---- Bidirectional ---- + std::cout << "===================================================\n"; + std::cout << "Memory Copy Bandwidth Sweep Test\n"; + std::cout << "Direction: Bidirectional\n"; + std::cout << "===================================================\n\n"; + print_table_header(); + { + cnrtQueue_t q1, q2; + MLU_CHECK(cnrtQueueCreate(&q1)); + MLU_CHECK(cnrtQueueCreate(&q2)); + + for (size_t skb : sizes_kb) { + size_t bytes = skb * 1024; + if (bytes > max_bytes) break; + + for (int i = 0; i < warmup; ++i) { + MLU_CHECK(cnrtMemcpyAsync(dev1, host_buf, bytes, q1, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtMemcpyAsync(host_buf, dev2, bytes, q2, cnrtMemcpyDevToHost)); + MLU_CHECK(cnrtQueueSync(q1)); + MLU_CHECK(cnrtQueueSync(q2)); + } + + PerfMetrics bw; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns1, ne1, ns2, ne2; + MLU_CHECK(cnrtNotifierCreate(&ns1)); + MLU_CHECK(cnrtNotifierCreate(&ne1)); + MLU_CHECK(cnrtNotifierCreate(&ns2)); + MLU_CHECK(cnrtNotifierCreate(&ne2)); + + MLU_CHECK(cnrtPlaceNotifier(ns1, q1)); + MLU_CHECK(cnrtMemcpyAsync(dev1, host_buf, bytes, q1, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtPlaceNotifier(ne1, q1)); + + MLU_CHECK(cnrtPlaceNotifier(ns2, q2)); + MLU_CHECK(cnrtMemcpyAsync(host_buf, dev2, bytes, q2, cnrtMemcpyDevToHost)); + MLU_CHECK(cnrtPlaceNotifier(ne2, q2)); + + MLU_CHECK(cnrtQueueSync(q1)); + MLU_CHECK(cnrtQueueSync(q2)); + + float us1, us2; + MLU_CHECK(cnrtNotifierDuration(ns1, ne1, &us1)); + MLU_CHECK(cnrtNotifierDuration(ns2, ne2, &us2)); + + double sec = std::max(us1, us2) / 1e6; + bw.add((2.0 * bytes / 1e9) / sec); + + MLU_CHECK(cnrtNotifierDestroy(ns1)); + MLU_CHECK(cnrtNotifierDestroy(ne1)); + MLU_CHECK(cnrtNotifierDestroy(ns2)); + MLU_CHECK(cnrtNotifierDestroy(ne2)); + } + + double avg_bw = bw.trimmed_mean(); + double avg_time_ms = (2.0 * bytes / 1e9) / avg_bw * 1000; + std::cout << std::fixed << std::setprecision(2); + std::cout << std::left << std::setw(15) << (bytes / 1024.0 / 1024.0); + std::cout << std::right << std::setw(12) << std::setprecision(3) << avg_time_ms; + std::cout << std::setw(18) << std::setprecision(2) << avg_bw; + std::cout << std::setw(10) << std::setprecision(1) + << (bw.cv() * 100.0) << "\n"; + } + + MLU_CHECK(cnrtQueueDestroy(q1)); + MLU_CHECK(cnrtQueueDestroy(q2)); + } + + cnrtFreeHost(host_buf); + cnrtFree(dev1); + cnrtFree(dev2); + MLU_CHECK(cnrtQueueDestroy(queue)); + std::cout << "\n"; + } +}; + +} // namespace mlu_perf diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/stream_benchmark.h b/infinibench/hardware/cambricon-memory-benchmark/include/stream_benchmark.h new file mode 100644 index 00000000..b19c1f9f --- /dev/null +++ b/infinibench/hardware/cambricon-memory-benchmark/include/stream_benchmark.h @@ -0,0 +1,318 @@ +#pragma once + +#include "cnrt_utils.h" +#include + +namespace mlu_perf { + +// NRAM: 240KB per core, single buffer manually partitioned +#define NRAM_MAX (1024 * 240) +#define ALIGN 128 + +// ---- init kernel ---- +template +__mlu_global__ void init_kernel(T* a, T* b, T* c, size_t n, + T va, T vb, T vc) { + __nram__ char nram_raw[NRAM_MAX]; + char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); + size_t usable = NRAM_MAX - (aligned - nram_raw); + size_t chunk = usable / (3 * sizeof(T)); + chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + if (chunk == 0) return; + + T* na = (T*)aligned; + T* nb = na + chunk; + T* nc = nb + chunk; + + size_t per_core = (n + taskDim - 1) / taskDim; + size_t start = taskId * per_core; + size_t end = start + per_core > n ? n : start + per_core; + + for (size_t off = start; off < end; off += chunk) { + size_t cnt = off + chunk > end ? end - off : chunk; + __bang_write_value(na, cnt, va); + __bang_write_value(nb, cnt, vb); + __bang_write_value(nc, cnt, vc); + __memcpy(a + off, na, cnt * sizeof(T), NRAM2GDRAM); + __memcpy(b + off, nb, cnt * sizeof(T), NRAM2GDRAM); + __memcpy(c + off, nc, cnt * sizeof(T), NRAM2GDRAM); + } +} + +// ---- STREAM Copy: dst[i] = src[i] (2 * N * sizeof(T) bytes moved) ---- +template +__mlu_global__ void stream_copy_kernel(T* dst, const T* src, size_t n) { + __nram__ char nram_raw[NRAM_MAX]; + char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); + size_t usable = NRAM_MAX - (aligned - nram_raw); + size_t chunk = usable / sizeof(T); + chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + if (chunk == 0) return; + + T* buf = (T*)aligned; + + size_t per_core = (n + taskDim - 1) / taskDim; + size_t start = taskId * per_core; + size_t end = start + per_core > n ? n : start + per_core; + + for (size_t off = start; off < end; off += chunk) { + size_t cnt = off + chunk > end ? end - off : chunk; + __memcpy(buf, src + off, cnt * sizeof(T), GDRAM2NRAM); + __memcpy(dst + off, buf, cnt * sizeof(T), NRAM2GDRAM); + } +} + +// ---- STREAM Scale: dst[i] = scalar * src[i] (2 * N * sizeof(T)) ---- +template +__mlu_global__ void stream_scale_kernel(T* dst, const T* src, T scalar, size_t n) { + __nram__ char nram_raw[NRAM_MAX]; + char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); + size_t usable = NRAM_MAX - (aligned - nram_raw); + size_t chunk = usable / (2 * sizeof(T)); + chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + if (chunk == 0) return; + + T* ns = (T*)aligned; + T* nd = ns + chunk; + + size_t per_core = (n + taskDim - 1) / taskDim; + size_t start = taskId * per_core; + size_t end = start + per_core > n ? n : start + per_core; + + for (size_t off = start; off < end; off += chunk) { + size_t cnt = off + chunk > end ? end - off : chunk; + __memcpy(ns, src + off, cnt * sizeof(T), GDRAM2NRAM); + __bang_mul_scalar(nd, ns, scalar, cnt); + __memcpy(dst + off, nd, cnt * sizeof(T), NRAM2GDRAM); + } +} + +// ---- STREAM Add: dst[i] = src1[i] + src2[i] (3 * N * sizeof(T)) ---- +template +__mlu_global__ void stream_add_kernel(T* dst, const T* src1, const T* src2, size_t n) { + __nram__ char nram_raw[NRAM_MAX]; + char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); + size_t usable = NRAM_MAX - (aligned - nram_raw); + size_t chunk = usable / (3 * sizeof(T)); + chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + if (chunk == 0) return; + + T* na = (T*)aligned; + T* nb = na + chunk; + T* nc = nb + chunk; + + size_t per_core = (n + taskDim - 1) / taskDim; + size_t start = taskId * per_core; + size_t end = start + per_core > n ? n : start + per_core; + + for (size_t off = start; off < end; off += chunk) { + size_t cnt = off + chunk > end ? end - off : chunk; + __memcpy(na, src1 + off, cnt * sizeof(T), GDRAM2NRAM); + __memcpy(nb, src2 + off, cnt * sizeof(T), GDRAM2NRAM); + __bang_add(nc, na, nb, cnt); + __memcpy(dst + off, nc, cnt * sizeof(T), NRAM2GDRAM); + } +} + +// ---- STREAM Triad: dst[i] = src1[i] + scalar * src2[i] (3 * N * sizeof(T)) ---- +template +__mlu_global__ void stream_triad_kernel(T* dst, const T* src1, const T* src2, + T scalar, size_t n) { + __nram__ char nram_raw[NRAM_MAX]; + char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); + size_t usable = NRAM_MAX - (aligned - nram_raw); + size_t chunk = usable / (3 * sizeof(T)); + chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + if (chunk == 0) return; + + T* na = (T*)aligned; + T* nb = na + chunk; + T* nc = nb + chunk; + + size_t per_core = (n + taskDim - 1) / taskDim; + size_t start = taskId * per_core; + size_t end = start + per_core > n ? n : start + per_core; + + for (size_t off = start; off < end; off += chunk) { + size_t cnt = off + chunk > end ? end - off : chunk; + __memcpy(na, src1 + off, cnt * sizeof(T), GDRAM2NRAM); + __memcpy(nb, src2 + off, cnt * sizeof(T), GDRAM2NRAM); + __bang_mul_scalar(nb, nb, scalar, cnt); + __bang_add(nc, na, nb, cnt); + __memcpy(dst + off, nc, cnt * sizeof(T), NRAM2GDRAM); + } +} + +// ============================================================ +// Benchmark suite +// ============================================================ + +class StreamBenchmarkTest { +public: + void execute(size_t array_size, const TestConfig& cfg = TestConfig()) { + MLU_CHECK(cnrtSetDevice(cfg.device_id)); + + cnrtQueue_t queue; + MLU_CHECK(cnrtQueueCreate(&queue)); + + cnrtDeviceProp_t prop; + MLU_CHECK(cnrtGetDeviceProperties(&prop, cfg.device_id)); + int total_cores = prop.clusterCount * prop.McorePerCluster; + + using T = float; + + cnrtDim3_t dim; + dim.x = prop.McorePerCluster; + dim.y = prop.clusterCount; + dim.z = 1; + cnrtFunctionType_t k_type = cnrtFuncTypeUnion1; + + int warmup = cfg.warmup_iterations; + int measure = cfg.measure_iterations; + + std::cout << "\n===================================================\n"; + std::cout << "STREAM Benchmark Suite\n"; + std::cout << "Array size: " << (array_size * sizeof(T) / 1024.0 / 1024.0) + << " MB (" << array_size << " elements)\n"; + std::cout << "===================================================\n\n"; + + T* d_a; T* d_b; T* d_c; + MLU_CHECK(cnrtMalloc(reinterpret_cast(&d_a), array_size * sizeof(T))); + MLU_CHECK(cnrtMalloc(reinterpret_cast(&d_b), array_size * sizeof(T))); + MLU_CHECK(cnrtMalloc(reinterpret_cast(&d_c), array_size * sizeof(T))); + + init_kernel<<>>(d_a, d_b, d_c, array_size, + (T)1.0, (T)2.0, (T)0.0); + MLU_CHECK(cnrtQueueSync(queue)); + + struct Result { std::string name; double bw; double ms; double cv; }; + std::vector results; + + // --- STREAM Copy --- + { + for (int i = 0; i < warmup; ++i) { + stream_copy_kernel<<>>(d_c, d_b, array_size); + MLU_CHECK(cnrtQueueSync(queue)); + } + PerfMetrics bw_m; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + stream_copy_kernel<<>>(d_c, d_b, array_size); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + bw_m.add(((double)2 * sizeof(T) * array_size / 1e9) / (us / 1e6)); + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + double avg = bw_m.trimmed_mean(); + results.push_back({"STREAM_Copy", avg, + ((double)2 * sizeof(T) * array_size / 1e9) / avg * 1000, + bw_m.cv() * 100.0}); + } + + // --- STREAM Scale --- + { + for (int i = 0; i < warmup; ++i) { + stream_scale_kernel<<>>(d_c, d_b, (T)3.5, array_size); + MLU_CHECK(cnrtQueueSync(queue)); + } + PerfMetrics bw_m; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + stream_scale_kernel<<>>(d_c, d_b, (T)3.5, array_size); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + bw_m.add(((double)2 * sizeof(T) * array_size / 1e9) / (us / 1e6)); + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + double avg = bw_m.trimmed_mean(); + results.push_back({"STREAM_Scale", avg, + ((double)2 * sizeof(T) * array_size / 1e9) / avg * 1000, + bw_m.cv() * 100.0}); + } + + // --- STREAM Add --- + { + for (int i = 0; i < warmup; ++i) { + stream_add_kernel<<>>(d_c, d_a, d_b, array_size); + MLU_CHECK(cnrtQueueSync(queue)); + } + PerfMetrics bw_m; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + stream_add_kernel<<>>(d_c, d_a, d_b, array_size); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + bw_m.add(((double)3 * sizeof(T) * array_size / 1e9) / (us / 1e6)); + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + double avg = bw_m.trimmed_mean(); + results.push_back({"STREAM_Add", avg, + ((double)3 * sizeof(T) * array_size / 1e9) / avg * 1000, + bw_m.cv() * 100.0}); + } + + // --- STREAM Triad --- + { + for (int i = 0; i < warmup; ++i) { + stream_triad_kernel<<>>(d_c, d_a, d_b, (T)3.5, array_size); + MLU_CHECK(cnrtQueueSync(queue)); + } + PerfMetrics bw_m; + for (int i = 0; i < measure; ++i) { + cnrtNotifier_t ns, ne; + MLU_CHECK(cnrtNotifierCreate(&ns)); + MLU_CHECK(cnrtNotifierCreate(&ne)); + MLU_CHECK(cnrtPlaceNotifier(ns, queue)); + stream_triad_kernel<<>>(d_c, d_a, d_b, (T)3.5, array_size); + MLU_CHECK(cnrtPlaceNotifier(ne, queue)); + MLU_CHECK(cnrtQueueSync(queue)); + float us; + MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); + bw_m.add(((double)3 * sizeof(T) * array_size / 1e9) / (us / 1e6)); + MLU_CHECK(cnrtNotifierDestroy(ns)); + MLU_CHECK(cnrtNotifierDestroy(ne)); + } + double avg = bw_m.trimmed_mean(); + results.push_back({"STREAM_Triad", avg, + ((double)3 * sizeof(T) * array_size / 1e9) / avg * 1000, + bw_m.cv() * 100.0}); + } + + std::cout << std::left << std::setw(16) << "Operation" + << std::right << std::setw(18) << "Bandwidth (GB/s)" + << std::setw(14) << "Time (ms)" + << std::setw(10) << "CV (%)\n"; + std::cout << std::string(58, '-') << "\n"; + for (const auto& r : results) { + std::cout << std::fixed << std::setprecision(2); + std::cout << std::left << std::setw(16) << r.name; + std::cout << std::right << std::setw(18) << r.bw; + std::cout << std::setw(14) << r.ms; + std::cout << std::setw(10) << std::setprecision(2) << r.cv << "\n"; + } + std::cout << "\n"; + + cnrtFree(d_a); cnrtFree(d_b); cnrtFree(d_c); + MLU_CHECK(cnrtQueueDestroy(queue)); + } +}; + +} // namespace mlu_perf diff --git a/infinibench/hardware/cambricon-memory-benchmark/src/main.mlu b/infinibench/hardware/cambricon-memory-benchmark/src/main.mlu new file mode 100644 index 00000000..c8295b34 --- /dev/null +++ b/infinibench/hardware/cambricon-memory-benchmark/src/main.mlu @@ -0,0 +1,124 @@ +#include +#include +#include "cnrt_utils.h" +#include "memory_bandwidth_test.h" +#include "stream_benchmark.h" +#include "cache_benchmark.h" + +using namespace mlu_perf; + +void print_banner() { + std::cout << R"( +================================================================ + MLU Performance Benchmark Suite v1.0 + Cambricon Memory & Cache Testing +================================================================ +)" << std::endl; +} + +void print_usage(const char* prog) { + std::cout << "Usage: " << prog << " [OPTIONS]\n\n" + << "Options:\n" + << " --all Run all tests (default)\n" + << " --memory Run memory bandwidth tests only\n" + << " --stream Run STREAM benchmark only\n" + << " --cache Run NRAM + L2 cache tests\n" + << " --nram Run NRAM bandwidth test only\n" + << " --l2cache Run L2 cache bandwidth test only\n" + << " --device Specify MLU device ID (default: 0)\n" + << " --iterations Number of measurement iterations (default: 10)\n" + << " --array-size Array size for STREAM test (default: 67108864)\n" + << " --help Show this help\n"; +} + +struct Config { + bool run_all = true; + bool run_memory = false; + bool run_stream = false; + bool run_cache = false; + bool run_nram = false; + bool run_l2cache = false; + int device_id = 0; + int iterations = 10; + size_t array_size = 67108864; +}; + +Config parse_args(int argc, char* argv[]) { + Config cfg; + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if (arg == "--help" || arg == "-h") { print_usage(argv[0]); exit(0); } + else if (arg == "--all") { cfg.run_all = true; } + else if (arg == "--memory") { cfg.run_all = false; cfg.run_memory = true; } + else if (arg == "--stream") { cfg.run_all = false; cfg.run_stream = true; } + else if (arg == "--cache") { cfg.run_all = false; cfg.run_cache = true; } + else if (arg == "--nram") { cfg.run_all = false; cfg.run_nram = true; } + else if (arg == "--l2cache") { cfg.run_all = false; cfg.run_l2cache = true; } + else if (arg == "--device" && i + 1 < argc) { cfg.device_id = std::atoi(argv[++i]); } + else if (arg == "--iterations" && i + 1 < argc) { cfg.iterations = std::atoi(argv[++i]); } + else if (arg == "--array-size" && i + 1 < argc) { cfg.array_size = std::atoll(argv[++i]); } + else { std::cerr << "Unknown option: " << arg << "\n"; print_usage(argv[0]); exit(1); } + } + return cfg; +} + +int main(int argc, char* argv[]) { + try { + print_banner(); + Config cfg = parse_args(argc, argv); + + // System info + std::cout << "=== System Information ===\n"; + int dev_count = get_device_count(); + std::cout << "MLU Devices: " << dev_count << "\n"; + for (int i = 0; i < dev_count; ++i) { + std::cout << "\n"; + MluDeviceInfo::print(i); + } + std::cout << "\n"; + + if (cfg.device_id >= dev_count) { + std::cerr << "Error: Device ID " << cfg.device_id << " not available\n"; + return 1; + } + MLU_CHECK(cnrtSetDevice(cfg.device_id)); + + TestConfig tc; + tc.warmup_iterations = 5; + tc.measure_iterations = cfg.iterations; + tc.device_id = cfg.device_id; + + std::cout << "=== Test Configuration ===\n" + << "Device ID: " << cfg.device_id << "\n" + << "Iterations: " << cfg.iterations << "\n" + << "Stream array size: " << cfg.array_size + << " elements (" << cfg.array_size * sizeof(float) / 1024.0 / 1024.0 << " MB)\n"; + + if (cfg.run_all || cfg.run_memory) { + MemoryBandwidthTest test; + test.execute(tc); + } + + if (cfg.run_all || cfg.run_stream) { + StreamBenchmarkTest test; + test.execute(cfg.array_size, tc); + } + + if (cfg.run_all || cfg.run_cache || cfg.run_nram) { + NRAMBandwidthTest test; + test.execute(tc); + } + + if (cfg.run_all || cfg.run_cache || cfg.run_l2cache) { + L2CacheBandwidthTest test; + test.execute(tc); + } + + std::cout << "\nAll tests completed successfully.\n\n"; + return 0; + + } catch (const std::exception& e) { + std::cerr << "\nERROR: " << e.what() << "\n"; + return 1; + } +} diff --git a/infinibench/hardware/constants.py b/infinibench/hardware/constants.py index e2b117f8..d3203438 100644 --- a/infinibench/hardware/constants.py +++ b/infinibench/hardware/constants.py @@ -11,6 +11,8 @@ "moore": "moore", "mthreads": "moore", "musa": "moore", + "cambricon": "cambricon", + "mlu": "cambricon", } PLATFORM_CONFIGS = { @@ -44,4 +46,10 @@ "build_platform": "moore", "cache_parser": "cuda", }, + "cambricon": { + "binary_name": "mlu_perf_suite", + "benchmark_subdir": "cambricon-memory-benchmark", + "build_platform": None, + "cache_parser": "cambricon", + }, } diff --git a/infinibench/hardware/hardware_adapter.py b/infinibench/hardware/hardware_adapter.py index d2d0c232..bfa93353 100644 --- a/infinibench/hardware/hardware_adapter.py +++ b/infinibench/hardware/hardware_adapter.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Hardware test adapter for CUDA-compatible accelerators.""" +"""Hardware test adapter for native and CUDA-compatible accelerators.""" import logging import re @@ -33,6 +33,8 @@ def detect_platform() -> str: """Detect the installed accelerator toolchain.""" + if shutil.which("cncc") or Path("/usr/local/neuware").exists(): + return "cambricon" if shutil.which("mcc") or shutil.which("mthreads-gmi"): return "moore" @@ -382,10 +384,65 @@ def _parse_stream_benchmark(self, output: str, run_id: str = None) -> List[Dict] def _parse_cache_for_platform( self, output: str, run_id: str, parser_name: str ) -> List[Dict]: + if parser_name == "cambricon": + return self._parse_cambricon_cache(output, run_id) if parser_name == "cuda": return self._parse_cache_bandwidth(output, run_id) raise ValueError(f"Unknown cache parser: {parser_name}") + def _parse_cambricon_cache(self, output: str, run_id: str) -> List[Dict]: + """Map Cambricon NRAM and L2 results to the existing cache schema.""" + metrics = [] + nram_match = re.search( + r"NRAM Bandwidth Test.*?Spread\s*-+\s*\n(.*?)(?=\n\s*=+|" r"\nL2 Cache|\Z)", + output, + re.DOTALL, + ) + if nram_match: + rows = [] + for line in nram_match.group(1).strip().splitlines(): + parts = line.split() + if len(parts) >= 6: + try: + rows.append( + { + "data_set": f"{parts[0]} {parts[1]}", + "_sort_key": float(parts[0]), + "exec_time": parts[2], + "spread": parts[5], + "eff_bw": float(parts[3]), + } + ) + except (ValueError, IndexError): + pass + if rows: + metrics.append( + self._create_timeseries_metric( + "hardware.gpu_cache_l1", + rows, + f"cache_l1_bandwidth_{run_id}", + L1_CACHE_CSV_FIELDS, + ) + ) + + l2_match = re.search( + r"L2 Cache Bandwidth Sweep Test.*?Eff\. bw\s*-+\s*\n(.*?)(?=\Z)", + output, + re.DOTALL, + ) + if l2_match: + rows = self._parse_cache_lines(l2_match.group(1), "l2") + if rows: + metrics.append( + self._create_timeseries_metric( + "hardware.gpu_cache_l2", + rows, + f"cache_l2_bandwidth_{run_id}", + L2_CACHE_CSV_FIELDS, + ) + ) + return metrics + def _parse_cache_bandwidth(self, output: str, run_id: str) -> List[Dict]: """Parse the existing CUDA L1 and L2 cache output.""" metrics = [] diff --git a/tests/test_hardware_adapter.py b/tests/test_hardware_adapter.py index 4262fab2..f365fe13 100644 --- a/tests/test_hardware_adapter.py +++ b/tests/test_hardware_adapter.py @@ -153,6 +153,8 @@ def test_parse_unknown_test_type_returns_no_metrics(tmp_path): ("hygon", "hygon"), ("moore", "moore"), ("musa", "moore"), + ("cambricon", "cambricon"), + ("mlu", "cambricon"), ("legacy-unknown-device", "cuda"), ], ) @@ -183,6 +185,18 @@ def test_cuda_compatible_platforms_share_binary(tmp_path, device): assert adapter._get_binary_path(device) == str(cuda_binary) +def test_cambricon_uses_native_binary_path(tmp_path): + cuda_binary = tmp_path / "cuda_perf_suite" + adapter = HardwareTestAdapter(str(cuda_binary), output_dir=str(tmp_path)) + + assert Path(adapter._get_binary_path("cambricon")).parts[-3:] == ( + "cambricon-memory-benchmark", + "build", + "mlu_perf_suite", + ) + assert adapter._get_binary_path("cuda") == str(cuda_binary) + + def test_build_cuda_project_preserves_runtime_platform_detection(tmp_path, monkeypatch): adapter = HardwareTestAdapter(output_dir=str(tmp_path)) built_platforms = [] @@ -209,8 +223,54 @@ def test_dispatcher_registers_cudaunified_hardware_framework(): "iluvatar", "hygon", "moore", + "cambricon", ], ) def test_dispatcher_does_not_register_devices_as_frameworks(device): with pytest.raises(ValueError, match="Adapter not registered"): Dispatcher()._create_adapter("hardware", device) + + +def test_cambricon_cache_maps_to_existing_metric_names(tmp_path): + output = """ +NRAM Bandwidth Test (BANG Kernel) +NRAM chunk/core Time (ms) Eff. BW (GB/s) TFLOPS Spread +--------------------------------------------------------------------- +120 kB 1.0 200.0 1.2 0.5% + +=================================================== +L2 Cache Bandwidth Sweep Test (BANG Kernel) +data set exec data exec time spread Eff. bw +--------------------------------------------------------------- +256 kB 2560 kB 1ms 0.5% 300 GB/s +""" + adapter = HardwareTestAdapter(output_dir=str(tmp_path)) + + metrics = adapter._parse_output(output, "Cache", "cam-run", "cambricon") + + assert [metric["name"] for metric in metrics] == [ + "hardware.gpu_cache_l1", + "hardware.gpu_cache_l2", + ] + + +def test_cambricon_benchmark_uses_selected_device_id(): + hardware_dir = Path(hardware_adapter.__file__).parent + native_files = list(hardware_dir.glob("cambricon-memory-benchmark/include/*.h")) + + assert native_files + for path in native_files: + source = path.read_text(encoding="utf-8") + assert "SetDevice(0)" not in source, path + + +def test_cambricon_uses_current_cnrt_success_enum(): + source = ( + Path(hardware_adapter.__file__).parent + / "cambricon-memory-benchmark" + / "include" + / "cnrt_utils.h" + ).read_text(encoding="utf-8") + + assert "cnrtSuccess" in source + assert "CNRT_RET_SUCCESS" not in source From 142b2716464358dcba38277249ed5c72e7adc295 Mon Sep 17 00:00:00 2001 From: Florent Li <1508269885@qq.com> Date: Wed, 5 Aug 2026 17:15:50 +0800 Subject: [PATCH 2/4] fix: correct Cambricon benchmark metrics --- infinibench/hardware/README.md | 7 ++-- .../include/cache_benchmark.h | 34 +++++++++---------- .../include/memory_bandwidth_test.h | 32 +++++++++-------- infinibench/hardware/hardware_adapter.py | 6 ++-- tests/test_hardware_adapter.py | 29 ++++++++++++++-- 5 files changed, 69 insertions(+), 39 deletions(-) diff --git a/infinibench/hardware/README.md b/infinibench/hardware/README.md index 80ddd795..e0a4ed73 100644 --- a/infinibench/hardware/README.md +++ b/infinibench/hardware/README.md @@ -1,8 +1,8 @@ # Hardware Benchmarks InfiniBench provides one hardware adapter for NVIDIA CUDA and five additional -accelerator platforms. Existing CUDA command shapes and metric -names are kept unchanged. +accelerator platforms. Existing CUDA command shapes, behavior, and metric names +are kept unchanged. Platform-specific memory levels use distinct metric names. ## Platforms @@ -47,7 +47,8 @@ example, Moore Threads STREAM uses: The aliases `nvidia`, `musa`, `mthreads`, and `mlu` are also accepted as explicit device values. A selected non-CUDA platform is recorded in the result -configuration as `platform`; metric names remain compatible with CUDA results. +configuration as `platform`. Cambricon NRAM bandwidth is published as +`hardware.nram_bandwidth`; it is not relabeled as a CUDA L1 cache metric. ## Device Visibility diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h b/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h index b2e0c56a..b5ef4c2c 100644 --- a/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h +++ b/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h @@ -8,11 +8,10 @@ namespace mlu_perf { #define ALIGN_CB 128 // ============================================================ -// NRAM Bandwidth Kernel (对标 CUDA L1 Cache) +// NRAM Bandwidth Kernel // ============================================================ -// 数据加载到 NRAM 后,用 __bang_add 反复做 NRAM 内的向量加。 -// 和 CUDA L1 测试对齐:只计 reads,用大量 repeat 放大时间。 -// 每个 __bang_add(dst, src0, src1, n) = 2 reads + 1 write +// Load data into each core's explicitly managed NRAM, then repeatedly run +// vector additions. Each __bang_add performs two reads and one write. template __mlu_global__ void nram_add_kernel(T* dst, const T* src, size_t n, @@ -47,7 +46,7 @@ __mlu_global__ void nram_add_kernel(T* dst, const T* src, size_t n, } // ============================================================ -// GDRAM<->NRAM Copy Kernel (用于 L2 Cache 测试) +// GDRAM<->NRAM Copy Kernel (used by the L2 cache test) // ============================================================ template @@ -76,7 +75,7 @@ __mlu_global__ void cache_rw_kernel(T* dst, const T* src, size_t n, int repeat) } // ============================================================ -// NRAM Bandwidth Sweep Test (对标 CUDA L1 Cache Sweep) +// NRAM Bandwidth Test // ============================================================ class NRAMBandwidthTest { @@ -106,11 +105,13 @@ class NRAMBandwidthTest { std::cout << "Cores: " << total_cores << "\n"; std::cout << "===================================================\n\n"; - // Use max NRAM chunk: 2 buffers in 240KB → ~120KB each + // Use the maximum NRAM chunk: two buffers in 240 KB, about 120 KB each. size_t nram_bytes = NRAM_MAX_CB - ALIGN_CB; size_t chunk = nram_bytes / (2 * sizeof(T)); chunk = (chunk / (ALIGN_CB / sizeof(T))) * (ALIGN_CB / sizeof(T)); size_t chunk_bytes = chunk * sizeof(T); + size_t total_elements = chunk * total_cores; + size_t total_bytes = total_elements * sizeof(T); // Large repeat to amortize per-call overhead // Aligned with CUDA L1: ~1e9 / ARRAY_N + 2 @@ -118,8 +119,8 @@ class NRAMBandwidthTest { void* src = nullptr; void* dst = nullptr; - MLU_CHECK(cnrtMalloc(&src, chunk_bytes)); - MLU_CHECK(cnrtMalloc(&dst, chunk_bytes)); + MLU_CHECK(cnrtMalloc(&src, total_bytes)); + MLU_CHECK(cnrtMalloc(&dst, total_bytes)); std::cout << "Chunk size per core: " << (chunk_bytes / 1024) << " kB\n"; std::cout << "Repeat count: " << repeat_count << "\n\n"; @@ -127,7 +128,7 @@ class NRAMBandwidthTest { // Warmup for (int i = 0; i < warmup; ++i) { nram_add_kernel<<>>( - (T*)dst, (const T*)src, chunk, (int)repeat_count); + (T*)dst, (const T*)src, total_elements, (int)repeat_count); MLU_CHECK(cnrtQueueSync(queue)); } @@ -140,7 +141,7 @@ class NRAMBandwidthTest { MLU_CHECK(cnrtPlaceNotifier(ns, queue)); nram_add_kernel<<>>( - (T*)dst, (const T*)src, chunk, (int)repeat_count); + (T*)dst, (const T*)src, total_elements, (int)repeat_count); MLU_CHECK(cnrtPlaceNotifier(ne, queue)); MLU_CHECK(cnrtQueueSync(queue)); @@ -148,9 +149,8 @@ class NRAMBandwidthTest { MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); double sec = us / 1e6; - // Aligned with CUDA L1: data_volume × grid_count × repeat_count / time - // data_volume = 2 reads × chunk_bytes per iter - double data_volume = 2.0 * chunk_bytes; + // Two __bang_add calls per repeat, each reading two NRAM inputs. + double data_volume = 4.0 * chunk_bytes; double total_bw = data_volume * total_cores * repeat_count / sec / 1e9; bw_metrics.add(total_bw); @@ -159,7 +159,7 @@ class NRAMBandwidthTest { } double avg_bw = bw_metrics.trimmed_mean(); - double avg_time_sec = (2.0 * chunk_bytes * total_cores * repeat_count / 1e9) + double avg_time_sec = (4.0 * chunk_bytes * total_cores * repeat_count / 1e9) / avg_bw; // Also compute TFLOPS: 2 adds per iter, each is 1 FLOP per element @@ -191,7 +191,7 @@ class NRAMBandwidthTest { }; // ============================================================ -// L2 Cache Bandwidth Sweep Test (对标 CUDA L2 Cache Sweep) +// L2 Cache Bandwidth Sweep Test // ============================================================ class L2CacheBandwidthTest { @@ -233,7 +233,7 @@ class L2CacheBandwidthTest { // Sweep from 256KB to 128MB // L2 is ~40MB, so <40MB should show high bandwidth (L2 hit) - // >40MB should show lower bandwidth (L2 miss → DRAM) + // Data sets larger than L2 should show lower DRAM-backed bandwidth. std::vector sizes_kb; for (size_t s = 256; s <= 8192; s *= 2) sizes_kb.push_back(s); for (size_t s = 10240; s <= 65536; s += 4096) sizes_kb.push_back(s); diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h b/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h index 6dc6ef07..250a3955 100644 --- a/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h +++ b/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h @@ -16,16 +16,19 @@ class MemoryBandwidthTest { cnrtQueue_t queue; MLU_CHECK(cnrtQueueCreate(&queue)); - void* host_buf; + void* host_src; + void* host_dst; void* dev1; void* dev2; - MLU_CHECK(cnrtHostMalloc(&host_buf, max_bytes)); + MLU_CHECK(cnrtHostMalloc(&host_src, max_bytes)); + MLU_CHECK(cnrtHostMalloc(&host_dst, max_bytes)); MLU_CHECK(cnrtMalloc(&dev1, max_bytes)); MLU_CHECK(cnrtMalloc(&dev2, max_bytes)); - memset(host_buf, 0xAB, max_bytes); - MLU_CHECK(cnrtMemcpy(dev1, host_buf, max_bytes, cnrtMemcpyHostToDev)); - MLU_CHECK(cnrtMemcpy(dev2, host_buf, max_bytes, cnrtMemcpyHostToDev)); + memset(host_src, 0xAB, max_bytes); + memset(host_dst, 0, max_bytes); + MLU_CHECK(cnrtMemcpy(dev1, host_src, max_bytes, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtMemcpy(dev2, host_src, max_bytes, cnrtMemcpyHostToDev)); std::vector sizes_kb = { 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, @@ -51,7 +54,7 @@ class MemoryBandwidthTest { if (bytes > max_bytes) break; for (int i = 0; i < warmup; ++i) { - MLU_CHECK(cnrtMemcpyAsync(dev1, host_buf, bytes, queue, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtMemcpyAsync(dev1, host_src, bytes, queue, cnrtMemcpyHostToDev)); MLU_CHECK(cnrtQueueSync(queue)); } @@ -62,7 +65,7 @@ class MemoryBandwidthTest { MLU_CHECK(cnrtNotifierCreate(&ne)); MLU_CHECK(cnrtPlaceNotifier(ns, queue)); - MLU_CHECK(cnrtMemcpyAsync(dev1, host_buf, bytes, queue, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtMemcpyAsync(dev1, host_src, bytes, queue, cnrtMemcpyHostToDev)); MLU_CHECK(cnrtPlaceNotifier(ne, queue)); MLU_CHECK(cnrtQueueSync(queue)); @@ -97,7 +100,7 @@ class MemoryBandwidthTest { if (bytes > max_bytes) break; for (int i = 0; i < warmup; ++i) { - MLU_CHECK(cnrtMemcpyAsync(host_buf, dev1, bytes, queue, cnrtMemcpyDevToHost)); + MLU_CHECK(cnrtMemcpyAsync(host_dst, dev1, bytes, queue, cnrtMemcpyDevToHost)); MLU_CHECK(cnrtQueueSync(queue)); } @@ -108,7 +111,7 @@ class MemoryBandwidthTest { MLU_CHECK(cnrtNotifierCreate(&ne)); MLU_CHECK(cnrtPlaceNotifier(ns, queue)); - MLU_CHECK(cnrtMemcpyAsync(host_buf, dev1, bytes, queue, cnrtMemcpyDevToHost)); + MLU_CHECK(cnrtMemcpyAsync(host_dst, dev1, bytes, queue, cnrtMemcpyDevToHost)); MLU_CHECK(cnrtPlaceNotifier(ne, queue)); MLU_CHECK(cnrtQueueSync(queue)); @@ -195,8 +198,8 @@ class MemoryBandwidthTest { if (bytes > max_bytes) break; for (int i = 0; i < warmup; ++i) { - MLU_CHECK(cnrtMemcpyAsync(dev1, host_buf, bytes, q1, cnrtMemcpyHostToDev)); - MLU_CHECK(cnrtMemcpyAsync(host_buf, dev2, bytes, q2, cnrtMemcpyDevToHost)); + MLU_CHECK(cnrtMemcpyAsync(dev1, host_src, bytes, q1, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtMemcpyAsync(host_dst, dev2, bytes, q2, cnrtMemcpyDevToHost)); MLU_CHECK(cnrtQueueSync(q1)); MLU_CHECK(cnrtQueueSync(q2)); } @@ -210,11 +213,11 @@ class MemoryBandwidthTest { MLU_CHECK(cnrtNotifierCreate(&ne2)); MLU_CHECK(cnrtPlaceNotifier(ns1, q1)); - MLU_CHECK(cnrtMemcpyAsync(dev1, host_buf, bytes, q1, cnrtMemcpyHostToDev)); + MLU_CHECK(cnrtMemcpyAsync(dev1, host_src, bytes, q1, cnrtMemcpyHostToDev)); MLU_CHECK(cnrtPlaceNotifier(ne1, q1)); MLU_CHECK(cnrtPlaceNotifier(ns2, q2)); - MLU_CHECK(cnrtMemcpyAsync(host_buf, dev2, bytes, q2, cnrtMemcpyDevToHost)); + MLU_CHECK(cnrtMemcpyAsync(host_dst, dev2, bytes, q2, cnrtMemcpyDevToHost)); MLU_CHECK(cnrtPlaceNotifier(ne2, q2)); MLU_CHECK(cnrtQueueSync(q1)); @@ -247,7 +250,8 @@ class MemoryBandwidthTest { MLU_CHECK(cnrtQueueDestroy(q2)); } - cnrtFreeHost(host_buf); + cnrtFreeHost(host_src); + cnrtFreeHost(host_dst); cnrtFree(dev1); cnrtFree(dev2); MLU_CHECK(cnrtQueueDestroy(queue)); diff --git a/infinibench/hardware/hardware_adapter.py b/infinibench/hardware/hardware_adapter.py index bfa93353..7e6fa4d4 100644 --- a/infinibench/hardware/hardware_adapter.py +++ b/infinibench/hardware/hardware_adapter.py @@ -391,7 +391,7 @@ def _parse_cache_for_platform( raise ValueError(f"Unknown cache parser: {parser_name}") def _parse_cambricon_cache(self, output: str, run_id: str) -> List[Dict]: - """Map Cambricon NRAM and L2 results to the existing cache schema.""" + """Parse Cambricon NRAM and L2 results without conflating their levels.""" metrics = [] nram_match = re.search( r"NRAM Bandwidth Test.*?Spread\s*-+\s*\n(.*?)(?=\n\s*=+|" r"\nL2 Cache|\Z)", @@ -418,9 +418,9 @@ def _parse_cambricon_cache(self, output: str, run_id: str) -> List[Dict]: if rows: metrics.append( self._create_timeseries_metric( - "hardware.gpu_cache_l1", + "hardware.nram_bandwidth", rows, - f"cache_l1_bandwidth_{run_id}", + f"nram_bandwidth_{run_id}", L1_CACHE_CSV_FIELDS, ) ) diff --git a/tests/test_hardware_adapter.py b/tests/test_hardware_adapter.py index f365fe13..2439eed9 100644 --- a/tests/test_hardware_adapter.py +++ b/tests/test_hardware_adapter.py @@ -231,7 +231,7 @@ def test_dispatcher_does_not_register_devices_as_frameworks(device): Dispatcher()._create_adapter("hardware", device) -def test_cambricon_cache_maps_to_existing_metric_names(tmp_path): +def test_cambricon_nram_uses_platform_specific_metric_name(tmp_path): output = """ NRAM Bandwidth Test (BANG Kernel) NRAM chunk/core Time (ms) Eff. BW (GB/s) TFLOPS Spread @@ -249,7 +249,7 @@ def test_cambricon_cache_maps_to_existing_metric_names(tmp_path): metrics = adapter._parse_output(output, "Cache", "cam-run", "cambricon") assert [metric["name"] for metric in metrics] == [ - "hardware.gpu_cache_l1", + "hardware.nram_bandwidth", "hardware.gpu_cache_l2", ] @@ -274,3 +274,28 @@ def test_cambricon_uses_current_cnrt_success_enum(): assert "cnrtSuccess" in source assert "CNRT_RET_SUCCESS" not in source + + +def test_cambricon_nram_workload_and_read_volume_cover_every_core(): + source = ( + Path(hardware_adapter.__file__).parent + / "cambricon-memory-benchmark" + / "include" + / "cache_benchmark.h" + ).read_text(encoding="utf-8") + + assert "size_t total_elements = chunk * total_cores;" in source + assert "(T*)dst, (const T*)src, total_elements" in source + assert "double data_volume = 4.0 * chunk_bytes;" in source + + +def test_cambricon_bidirectional_copy_uses_distinct_host_buffers(): + source = ( + Path(hardware_adapter.__file__).parent + / "cambricon-memory-benchmark" + / "include" + / "memory_bandwidth_test.h" + ).read_text(encoding="utf-8") + + assert "cnrtMemcpyAsync(dev1, host_src, bytes, q1" in source + assert "cnrtMemcpyAsync(host_dst, dev2, bytes, q2" in source From 6b7bfc1a41c59f7c7c6673900fecd1553d8f0c0c Mon Sep 17 00:00:00 2001 From: Florent Li <1508269885@qq.com> Date: Thu, 6 Aug 2026 11:04:58 +0800 Subject: [PATCH 3/4] fix: tighten Cambricon benchmark resources --- .../cambricon-memory-benchmark/CMakeLists.txt | 33 +++++++++++-------- .../include/memory_bandwidth_test.h | 11 +++---- tests/test_hardware_adapter.py | 27 +++++++++++++++ 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/infinibench/hardware/cambricon-memory-benchmark/CMakeLists.txt b/infinibench/hardware/cambricon-memory-benchmark/CMakeLists.txt index c3265954..c9d84e34 100644 --- a/infinibench/hardware/cambricon-memory-benchmark/CMakeLists.txt +++ b/infinibench/hardware/cambricon-memory-benchmark/CMakeLists.txt @@ -1,12 +1,4 @@ cmake_minimum_required(VERSION 3.18) -project(MluPerfSuite VERSION 1.0.0 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) -endif() # Find NEUWARE / CNToolkit if(DEFINED ENV{NEUWARE_HOME}) @@ -23,6 +15,22 @@ if(NOT CNCC) endif() message(STATUS "Found cncc: ${CNCC}") +set(CMAKE_CXX_COMPILER "${CNCC}") +project(MluPerfSuite VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +if(DEFINED ENV{MLU_ARCH}) + set(MLU_ARCH $ENV{MLU_ARCH}) +else() + set(MLU_ARCH "mtp_592") +endif() + # Find CNRT library find_library(CNRT_LIB cnrt HINTS ${NEUWARE_HOME}/lib64 ${NEUWARE_HOME}/lib) if(NOT CNRT_LIB) @@ -40,14 +48,10 @@ target_include_directories(mlu_perf_suite PRIVATE ${CNRT_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include ) -target_link_libraries(mlu_perf_suite ${CNRT_LIB} pthread) +target_compile_options(mlu_perf_suite PRIVATE "--bang-mlu-arch=${MLU_ARCH}") +target_link_libraries(mlu_perf_suite ${CNRT_LIB} stdc++ m pthread) -# Use cncc as compiler set_source_files_properties(src/main.mlu PROPERTIES LANGUAGE CXX) -set_target_properties(mlu_perf_suite PROPERTIES - CXX_COMPILER_LAUNCHER ${CNCC} - RULE_LAUNCH_COMPILE "${CNCC}" -) install(TARGETS mlu_perf_suite RUNTIME DESTINATION bin) @@ -56,5 +60,6 @@ message(STATUS "Configuration Summary:") message(STATUS " Project: ${PROJECT_NAME} v${PROJECT_VERSION}") message(STATUS " Build: ${CMAKE_BUILD_TYPE}") message(STATUS " cncc: ${CNCC}") +message(STATUS " MLU arch: ${MLU_ARCH}") message(STATUS " CNRT: ${CNRT_LIB}") message(STATUS "") diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h b/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h index 250a3955..0a499f88 100644 --- a/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h +++ b/infinibench/hardware/cambricon-memory-benchmark/include/memory_bandwidth_test.h @@ -9,7 +9,11 @@ class MemoryBandwidthTest { void execute(const TestConfig& cfg = TestConfig()) { MLU_CHECK(cnrtSetDevice(cfg.device_id)); - const size_t max_bytes = 2ULL * 1024 * 1024 * 1024; // 2 GB + const std::vector sizes_kb = { + 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, + 32768, 65536, 131072, 262144, 524288, 1048576 + }; + const size_t max_bytes = sizes_kb.back() * 1024; const int warmup = cfg.warmup_iterations; const int measure = cfg.measure_iterations; @@ -30,11 +34,6 @@ class MemoryBandwidthTest { MLU_CHECK(cnrtMemcpy(dev1, host_src, max_bytes, cnrtMemcpyHostToDev)); MLU_CHECK(cnrtMemcpy(dev2, host_src, max_bytes, cnrtMemcpyHostToDev)); - std::vector sizes_kb = { - 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, - 32768, 65536, 131072, 262144, 524288, 1048576 - }; - auto print_table_header = [&]() { std::cout << std::left << std::setw(15) << "Size (MB)" << std::right << std::setw(12) << "Time (ms)" diff --git a/tests/test_hardware_adapter.py b/tests/test_hardware_adapter.py index 2439eed9..6682e831 100644 --- a/tests/test_hardware_adapter.py +++ b/tests/test_hardware_adapter.py @@ -299,3 +299,30 @@ def test_cambricon_bidirectional_copy_uses_distinct_host_buffers(): assert "cnrtMemcpyAsync(dev1, host_src, bytes, q1" in source assert "cnrtMemcpyAsync(host_dst, dev2, bytes, q2" in source + + +def test_cambricon_bandwidth_buffers_match_largest_sweep_case(): + source = ( + Path(hardware_adapter.__file__).parent + / "cambricon-memory-benchmark" + / "include" + / "memory_bandwidth_test.h" + ).read_text(encoding="utf-8") + + assert "const size_t max_bytes = sizes_kb.back() * 1024;" in source + assert "2ULL * 1024 * 1024 * 1024" not in source + + +def test_cambricon_cmake_uses_cncc_as_the_compiler(): + source = ( + Path(hardware_adapter.__file__).parent + / "cambricon-memory-benchmark" + / "CMakeLists.txt" + ).read_text(encoding="utf-8") + + assert 'set(CMAKE_CXX_COMPILER "${CNCC}")' in source + assert ( + "target_link_libraries(mlu_perf_suite ${CNRT_LIB} stdc++ m pthread)" in source + ) + assert "CXX_COMPILER_LAUNCHER" not in source + assert "RULE_LAUNCH_COMPILE" not in source From be7f00fb7d7707393094a8624f83136dd27b3ec7 Mon Sep 17 00:00:00 2001 From: Florent Li <1508269885@qq.com> Date: Thu, 6 Aug 2026 15:28:43 +0800 Subject: [PATCH 4/4] refactor: deduplicate Cambricon benchmark setup --- .../include/cache_benchmark.h | 29 ++-- .../include/nram_utils.h | 22 +++ .../include/stream_benchmark.h | 162 +++++------------- tests/test_hardware_adapter.py | 44 ++++- tests/test_hardware_detection.py | 8 +- 5 files changed, 118 insertions(+), 147 deletions(-) create mode 100644 infinibench/hardware/cambricon-memory-benchmark/include/nram_utils.h diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h b/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h index b5ef4c2c..522577d2 100644 --- a/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h +++ b/infinibench/hardware/cambricon-memory-benchmark/include/cache_benchmark.h @@ -1,12 +1,10 @@ #pragma once #include "cnrt_utils.h" +#include "nram_utils.h" namespace mlu_perf { -#define NRAM_MAX_CB (1024 * 240) -#define ALIGN_CB 128 - // ============================================================ // NRAM Bandwidth Kernel // ============================================================ @@ -16,15 +14,11 @@ namespace mlu_perf { template __mlu_global__ void nram_add_kernel(T* dst, const T* src, size_t n, int repeat) { - __nram__ char nram_raw[NRAM_MAX_CB]; - char* aligned = (char*)(((size_t)nram_raw + ALIGN_CB - 1) & ~(ALIGN_CB - 1)); - size_t usable = NRAM_MAX_CB - (aligned - nram_raw); - // 2 buffers: input + output - size_t chunk = usable / (2 * sizeof(T)); - chunk = (chunk / (ALIGN_CB / sizeof(T))) * (ALIGN_CB / sizeof(T)); + __nram__ char nram_raw[kNramBytes]; + T* buf_a; + size_t chunk = prepare_nram_layout(nram_raw, 2, &buf_a); if (chunk == 0) return; - T* buf_a = (T*)aligned; T* buf_b = buf_a + chunk; size_t per_core = (n + taskDim - 1) / taskDim; @@ -51,15 +45,11 @@ __mlu_global__ void nram_add_kernel(T* dst, const T* src, size_t n, template __mlu_global__ void cache_rw_kernel(T* dst, const T* src, size_t n, int repeat) { - __nram__ char nram_raw[NRAM_MAX_CB]; - char* aligned = (char*)(((size_t)nram_raw + ALIGN_CB - 1) & ~(ALIGN_CB - 1)); - size_t usable = NRAM_MAX_CB - (aligned - nram_raw); - size_t chunk = usable / sizeof(T); - chunk = (chunk / (ALIGN_CB / sizeof(T))) * (ALIGN_CB / sizeof(T)); + __nram__ char nram_raw[kNramBytes]; + T* buf; + size_t chunk = prepare_nram_layout(nram_raw, 1, &buf); if (chunk == 0) return; - T* buf = (T*)aligned; - size_t per_core = (n + taskDim - 1) / taskDim; size_t start = taskId * per_core; size_t end = start + per_core > n ? n : start + per_core; @@ -106,9 +96,10 @@ class NRAMBandwidthTest { std::cout << "===================================================\n\n"; // Use the maximum NRAM chunk: two buffers in 240 KB, about 120 KB each. - size_t nram_bytes = NRAM_MAX_CB - ALIGN_CB; + size_t nram_bytes = kNramBytes - kNramAlignment; size_t chunk = nram_bytes / (2 * sizeof(T)); - chunk = (chunk / (ALIGN_CB / sizeof(T))) * (ALIGN_CB / sizeof(T)); + chunk = (chunk / (kNramAlignment / sizeof(T))) + * (kNramAlignment / sizeof(T)); size_t chunk_bytes = chunk * sizeof(T); size_t total_elements = chunk * total_cores; size_t total_bytes = total_elements * sizeof(T); diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/nram_utils.h b/infinibench/hardware/cambricon-memory-benchmark/include/nram_utils.h new file mode 100644 index 00000000..74a4737d --- /dev/null +++ b/infinibench/hardware/cambricon-memory-benchmark/include/nram_utils.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace mlu_perf { + +constexpr size_t kNramBytes = 1024 * 240; +constexpr size_t kNramAlignment = 128; + +template +__mlu_device__ inline size_t prepare_nram_layout( + char* raw, size_t buffer_count, T** base) { + char* aligned = (char*)(((size_t)raw + kNramAlignment - 1) + & ~(kNramAlignment - 1)); + size_t usable = kNramBytes - (aligned - raw); + size_t chunk = usable / (buffer_count * sizeof(T)); + size_t alignment_elements = kNramAlignment / sizeof(T); + *base = (T*)aligned; + return (chunk / alignment_elements) * alignment_elements; +} + +} // namespace mlu_perf diff --git a/infinibench/hardware/cambricon-memory-benchmark/include/stream_benchmark.h b/infinibench/hardware/cambricon-memory-benchmark/include/stream_benchmark.h index b19c1f9f..2526b498 100644 --- a/infinibench/hardware/cambricon-memory-benchmark/include/stream_benchmark.h +++ b/infinibench/hardware/cambricon-memory-benchmark/include/stream_benchmark.h @@ -1,26 +1,20 @@ #pragma once #include "cnrt_utils.h" +#include "nram_utils.h" #include namespace mlu_perf { -// NRAM: 240KB per core, single buffer manually partitioned -#define NRAM_MAX (1024 * 240) -#define ALIGN 128 - // ---- init kernel ---- template __mlu_global__ void init_kernel(T* a, T* b, T* c, size_t n, T va, T vb, T vc) { - __nram__ char nram_raw[NRAM_MAX]; - char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); - size_t usable = NRAM_MAX - (aligned - nram_raw); - size_t chunk = usable / (3 * sizeof(T)); - chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + __nram__ char nram_raw[kNramBytes]; + T* na; + size_t chunk = prepare_nram_layout(nram_raw, 3, &na); if (chunk == 0) return; - T* na = (T*)aligned; T* nb = na + chunk; T* nc = nb + chunk; @@ -42,15 +36,11 @@ __mlu_global__ void init_kernel(T* a, T* b, T* c, size_t n, // ---- STREAM Copy: dst[i] = src[i] (2 * N * sizeof(T) bytes moved) ---- template __mlu_global__ void stream_copy_kernel(T* dst, const T* src, size_t n) { - __nram__ char nram_raw[NRAM_MAX]; - char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); - size_t usable = NRAM_MAX - (aligned - nram_raw); - size_t chunk = usable / sizeof(T); - chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + __nram__ char nram_raw[kNramBytes]; + T* buf; + size_t chunk = prepare_nram_layout(nram_raw, 1, &buf); if (chunk == 0) return; - T* buf = (T*)aligned; - size_t per_core = (n + taskDim - 1) / taskDim; size_t start = taskId * per_core; size_t end = start + per_core > n ? n : start + per_core; @@ -65,14 +55,11 @@ __mlu_global__ void stream_copy_kernel(T* dst, const T* src, size_t n) { // ---- STREAM Scale: dst[i] = scalar * src[i] (2 * N * sizeof(T)) ---- template __mlu_global__ void stream_scale_kernel(T* dst, const T* src, T scalar, size_t n) { - __nram__ char nram_raw[NRAM_MAX]; - char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); - size_t usable = NRAM_MAX - (aligned - nram_raw); - size_t chunk = usable / (2 * sizeof(T)); - chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + __nram__ char nram_raw[kNramBytes]; + T* ns; + size_t chunk = prepare_nram_layout(nram_raw, 2, &ns); if (chunk == 0) return; - T* ns = (T*)aligned; T* nd = ns + chunk; size_t per_core = (n + taskDim - 1) / taskDim; @@ -90,14 +77,11 @@ __mlu_global__ void stream_scale_kernel(T* dst, const T* src, T scalar, size_t n // ---- STREAM Add: dst[i] = src1[i] + src2[i] (3 * N * sizeof(T)) ---- template __mlu_global__ void stream_add_kernel(T* dst, const T* src1, const T* src2, size_t n) { - __nram__ char nram_raw[NRAM_MAX]; - char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); - size_t usable = NRAM_MAX - (aligned - nram_raw); - size_t chunk = usable / (3 * sizeof(T)); - chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + __nram__ char nram_raw[kNramBytes]; + T* na; + size_t chunk = prepare_nram_layout(nram_raw, 3, &na); if (chunk == 0) return; - T* na = (T*)aligned; T* nb = na + chunk; T* nc = nb + chunk; @@ -118,14 +102,11 @@ __mlu_global__ void stream_add_kernel(T* dst, const T* src1, const T* src2, size template __mlu_global__ void stream_triad_kernel(T* dst, const T* src1, const T* src2, T scalar, size_t n) { - __nram__ char nram_raw[NRAM_MAX]; - char* aligned = (char*)(((size_t)nram_raw + ALIGN - 1) & ~(ALIGN - 1)); - size_t usable = NRAM_MAX - (aligned - nram_raw); - size_t chunk = usable / (3 * sizeof(T)); - chunk = (chunk / (ALIGN / sizeof(T))) * (ALIGN / sizeof(T)); + __nram__ char nram_raw[kNramBytes]; + T* na; + size_t chunk = prepare_nram_layout(nram_raw, 3, &na); if (chunk == 0) return; - T* na = (T*)aligned; T* nb = na + chunk; T* nc = nb + chunk; @@ -188,10 +169,9 @@ class StreamBenchmarkTest { struct Result { std::string name; double bw; double ms; double cv; }; std::vector results; - // --- STREAM Copy --- - { + auto benchmark = [&](const char* name, double bytes, auto&& launch) { for (int i = 0; i < warmup; ++i) { - stream_copy_kernel<<>>(d_c, d_b, array_size); + launch(); MLU_CHECK(cnrtQueueSync(queue)); } PerfMetrics bw_m; @@ -200,101 +180,41 @@ class StreamBenchmarkTest { MLU_CHECK(cnrtNotifierCreate(&ns)); MLU_CHECK(cnrtNotifierCreate(&ne)); MLU_CHECK(cnrtPlaceNotifier(ns, queue)); - stream_copy_kernel<<>>(d_c, d_b, array_size); + launch(); MLU_CHECK(cnrtPlaceNotifier(ne, queue)); MLU_CHECK(cnrtQueueSync(queue)); float us; MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); - bw_m.add(((double)2 * sizeof(T) * array_size / 1e9) / (us / 1e6)); + bw_m.add((bytes / 1e9) / (us / 1e6)); MLU_CHECK(cnrtNotifierDestroy(ns)); MLU_CHECK(cnrtNotifierDestroy(ne)); } double avg = bw_m.trimmed_mean(); - results.push_back({"STREAM_Copy", avg, - ((double)2 * sizeof(T) * array_size / 1e9) / avg * 1000, + results.push_back({name, avg, + (bytes / 1e9) / avg * 1000, bw_m.cv() * 100.0}); - } + }; - // --- STREAM Scale --- - { - for (int i = 0; i < warmup; ++i) { - stream_scale_kernel<<>>(d_c, d_b, (T)3.5, array_size); - MLU_CHECK(cnrtQueueSync(queue)); - } - PerfMetrics bw_m; - for (int i = 0; i < measure; ++i) { - cnrtNotifier_t ns, ne; - MLU_CHECK(cnrtNotifierCreate(&ns)); - MLU_CHECK(cnrtNotifierCreate(&ne)); - MLU_CHECK(cnrtPlaceNotifier(ns, queue)); - stream_scale_kernel<<>>(d_c, d_b, (T)3.5, array_size); - MLU_CHECK(cnrtPlaceNotifier(ne, queue)); - MLU_CHECK(cnrtQueueSync(queue)); - float us; - MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); - bw_m.add(((double)2 * sizeof(T) * array_size / 1e9) / (us / 1e6)); - MLU_CHECK(cnrtNotifierDestroy(ns)); - MLU_CHECK(cnrtNotifierDestroy(ne)); - } - double avg = bw_m.trimmed_mean(); - results.push_back({"STREAM_Scale", avg, - ((double)2 * sizeof(T) * array_size / 1e9) / avg * 1000, - bw_m.cv() * 100.0}); - } + double element_bytes = (double)sizeof(T) * array_size; - // --- STREAM Add --- - { - for (int i = 0; i < warmup; ++i) { - stream_add_kernel<<>>(d_c, d_a, d_b, array_size); - MLU_CHECK(cnrtQueueSync(queue)); - } - PerfMetrics bw_m; - for (int i = 0; i < measure; ++i) { - cnrtNotifier_t ns, ne; - MLU_CHECK(cnrtNotifierCreate(&ns)); - MLU_CHECK(cnrtNotifierCreate(&ne)); - MLU_CHECK(cnrtPlaceNotifier(ns, queue)); - stream_add_kernel<<>>(d_c, d_a, d_b, array_size); - MLU_CHECK(cnrtPlaceNotifier(ne, queue)); - MLU_CHECK(cnrtQueueSync(queue)); - float us; - MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); - bw_m.add(((double)3 * sizeof(T) * array_size / 1e9) / (us / 1e6)); - MLU_CHECK(cnrtNotifierDestroy(ns)); - MLU_CHECK(cnrtNotifierDestroy(ne)); - } - double avg = bw_m.trimmed_mean(); - results.push_back({"STREAM_Add", avg, - ((double)3 * sizeof(T) * array_size / 1e9) / avg * 1000, - bw_m.cv() * 100.0}); - } + benchmark("STREAM_Copy", 2.0 * element_bytes, [&]() { + stream_copy_kernel<<>>(d_c, d_b, array_size); + }); - // --- STREAM Triad --- - { - for (int i = 0; i < warmup; ++i) { - stream_triad_kernel<<>>(d_c, d_a, d_b, (T)3.5, array_size); - MLU_CHECK(cnrtQueueSync(queue)); - } - PerfMetrics bw_m; - for (int i = 0; i < measure; ++i) { - cnrtNotifier_t ns, ne; - MLU_CHECK(cnrtNotifierCreate(&ns)); - MLU_CHECK(cnrtNotifierCreate(&ne)); - MLU_CHECK(cnrtPlaceNotifier(ns, queue)); - stream_triad_kernel<<>>(d_c, d_a, d_b, (T)3.5, array_size); - MLU_CHECK(cnrtPlaceNotifier(ne, queue)); - MLU_CHECK(cnrtQueueSync(queue)); - float us; - MLU_CHECK(cnrtNotifierDuration(ns, ne, &us)); - bw_m.add(((double)3 * sizeof(T) * array_size / 1e9) / (us / 1e6)); - MLU_CHECK(cnrtNotifierDestroy(ns)); - MLU_CHECK(cnrtNotifierDestroy(ne)); - } - double avg = bw_m.trimmed_mean(); - results.push_back({"STREAM_Triad", avg, - ((double)3 * sizeof(T) * array_size / 1e9) / avg * 1000, - bw_m.cv() * 100.0}); - } + benchmark("STREAM_Scale", 2.0 * element_bytes, [&]() { + stream_scale_kernel<<>>( + d_c, d_b, (T)3.5, array_size); + }); + + benchmark("STREAM_Add", 3.0 * element_bytes, [&]() { + stream_add_kernel<<>>( + d_c, d_a, d_b, array_size); + }); + + benchmark("STREAM_Triad", 3.0 * element_bytes, [&]() { + stream_triad_kernel<<>>( + d_c, d_a, d_b, (T)3.5, array_size); + }); std::cout << std::left << std::setw(16) << "Operation" << std::right << std::setw(18) << "Bandwidth (GB/s)" diff --git a/tests/test_hardware_adapter.py b/tests/test_hardware_adapter.py index 6682e831..31586efa 100644 --- a/tests/test_hardware_adapter.py +++ b/tests/test_hardware_adapter.py @@ -2,9 +2,9 @@ import pytest -from infinimetrics.dispatcher import Dispatcher -from infinimetrics.hardware import hardware_adapter -from infinimetrics.hardware.hardware_adapter import HardwareTestAdapter +from infinibench.dispatcher import Dispatcher +from infinibench.hardware import hardware_adapter +from infinibench.hardware.hardware_adapter import HardwareTestAdapter CUDA_OUTPUT = """ @@ -326,3 +326,41 @@ def test_cambricon_cmake_uses_cncc_as_the_compiler(): ) assert "CXX_COMPILER_LAUNCHER" not in source assert "RULE_LAUNCH_COMPILE" not in source + + +def test_cambricon_kernels_share_nram_layout_calculation(): + benchmark_dir = ( + Path(hardware_adapter.__file__).parent / "cambricon-memory-benchmark" + ) + utility = (benchmark_dir / "include" / "nram_utils.h").read_text(encoding="utf-8") + stream = (benchmark_dir / "include" / "stream_benchmark.h").read_text( + encoding="utf-8" + ) + cache = (benchmark_dir / "include" / "cache_benchmark.h").read_text( + encoding="utf-8" + ) + + assert "prepare_nram_layout" in utility + assert stream.count("prepare_nram_layout") == 5 + assert cache.count("prepare_nram_layout") == 2 + assert "#define NRAM_MAX" not in stream + cache + + +def test_cambricon_stream_measurement_uses_shared_control_flow(): + source = ( + Path(hardware_adapter.__file__).parent + / "cambricon-memory-benchmark" + / "include" + / "stream_benchmark.h" + ).read_text(encoding="utf-8") + + expected_cases = [ + 'benchmark("STREAM_Copy", 2.0 * element_bytes', + 'benchmark("STREAM_Scale", 2.0 * element_bytes', + 'benchmark("STREAM_Add", 3.0 * element_bytes', + 'benchmark("STREAM_Triad", 3.0 * element_bytes', + ] + positions = [source.index(case) for case in expected_cases] + + assert positions == sorted(positions) + assert source.count("cnrtNotifierCreate") == 2 diff --git a/tests/test_hardware_detection.py b/tests/test_hardware_detection.py index 723cbf6d..1ef5c982 100644 --- a/tests/test_hardware_detection.py +++ b/tests/test_hardware_detection.py @@ -1,9 +1,9 @@ from types import SimpleNamespace -from infinimetrics.common import hardware_info -from infinimetrics.common.hardware_info import HardwareCollector -from infinimetrics.utils import hardware_detector -from infinimetrics.utils.hardware_detector import HardwareDetector +from infinibench.common import hardware_info +from infinibench.common.hardware_info import HardwareCollector +from infinibench.utils import hardware_detector +from infinibench.utils.hardware_detector import HardwareDetector MTHREADS_OUTPUT = """