From c1cd0b60e9fae29f6afb0367701b3af3e46d8149 Mon Sep 17 00:00:00 2001 From: "Ravindran, Binuraj" Date: Fri, 10 Jul 2026 00:36:34 +0530 Subject: [PATCH 1/2] [added madvise updates --- tests/madvise/README.md | 7 +- tests/madvise/madvise_test.c | 67 ++++ tests/madvise/madvise_test_mt.c | 310 ++++++++++++++++++ tests/madvise/zram_microbench.sh | 503 ++++++++++++++++++++++++++++++ tests/madvise/zswap_microbench.sh | 488 +++++++++++++++++++++++++++++ 5 files changed, 1374 insertions(+), 1 deletion(-) create mode 100644 tests/madvise/madvise_test_mt.c create mode 100755 tests/madvise/zram_microbench.sh create mode 100755 tests/madvise/zswap_microbench.sh diff --git a/tests/madvise/README.md b/tests/madvise/README.md index 2187492..2d118dd 100644 --- a/tests/madvise/README.md +++ b/tests/madvise/README.md @@ -4,7 +4,12 @@ ## Prerequisite This framework depends on IAA RFC kernel patches. Please see more instructions here. https://github.com/intel-innersource/applications.benchmarking.memory-usage-analyzer/wiki/Integration-of-IAA-RFC-patches-to-upstream-kernel -## Run in Baremetal + +## Quick swap-in/swap-out test +This test will show the swap-in and swap-out latency to do a sanity check for lzo and deflate-iaa +./benchmark.sh + +## Detailed Analysis for batching (currently broken) 1. Run ``` ./make_swap_space.sh diff --git a/tests/madvise/madvise_test.c b/tests/madvise/madvise_test.c index 52989c3..281c78c 100644 --- a/tests/madvise/madvise_test.c +++ b/tests/madvise/madvise_test.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #ifndef MADV_PAGEOUT @@ -22,6 +23,51 @@ extern inline long _now() { return now.tv_sec * 1000000000 + now.tv_nsec; } +static inline long tv_to_ns(const struct timeval *tv) { + return tv->tv_sec * 1000000000L + tv->tv_usec * 1000L; +} + +long read_sysfs_long(const char *path) { + FILE *fp = fopen(path, "r"); + if (!fp) return 0; + char buf[64]; + if (!fgets(buf, sizeof(buf), fp)) { fclose(fp); return 0; } + fclose(fp); + return atol(buf); +} + +/* + * Read zram mm_stat: "orig_data_size compr_data_size mem_used_total ..." + * Also try zswap sysfs as fallback. + */ +void read_zpool_stats(long *orig_data, long *mem_used) { + *orig_data = 0; + *mem_used = 0; + + const char *zpool_source = getenv("ZPOOL_SOURCE"); + int force_zswap = (zpool_source && strcmp(zpool_source, "zswap") == 0); + int force_zram = (zpool_source && strcmp(zpool_source, "zram") == 0); + + if (!force_zswap) { + /* Try zram first unless zswap is explicitly requested. */ + FILE *fp = fopen("/sys/block/zram0/mm_stat", "r"); + if (fp) { + fscanf(fp, "%ld %*ld %ld", orig_data, mem_used); + fclose(fp); + return; + } + + if (force_zram) + return; + } + + /* Fallback to zswap */ + long stored = read_sysfs_long("/sys/kernel/debug/zswap/stored_pages"); + long pool = read_sysfs_long("/sys/kernel/debug/zswap/pool_total_size"); + *orig_data = stored * 4096; + *mem_used = pool; +} + int main(int argc, char **argv) { int i, nr_pages = 1; @@ -45,18 +91,35 @@ int main(int argc, char **argv) } /* Tell kernel to swap it out */ + struct rusage ru0_out, ru1_out; + getrusage(RUSAGE_SELF, &ru0_out); long time_ns = _now(); int status = madvise(addr, nr_pages * PAGE_SIZE, MADV_PAGEOUT); long swap_out_time_total_ns=_now() - time_ns; + getrusage(RUSAGE_SELF, &ru1_out); + long swap_out_sys_total_ns = tv_to_ns(&ru1_out.ru_stime) - tv_to_ns(&ru0_out.ru_stime); long swap_out_time_avg_ns= swap_out_time_total_ns/nr_pages; + long swap_out_sys_avg_ns = swap_out_sys_total_ns/nr_pages; printf("Swapping out %d pages from %lx, ret = %d\n", nr_pages, addr, status); printf("swap_out: total=%d average=%d\n", swap_out_time_total_ns, swap_out_time_avg_ns); + printf("swap_out_sys: total=%d average=%d\n", swap_out_sys_total_ns, swap_out_sys_avg_ns); /* Wait for swap out to finish */ sleep(2); + /* Report zpool stats while pages are still compressed */ + long orig_data = 0, mem_used = 0; + read_zpool_stats(&orig_data, &mem_used); + long stored_pages = orig_data / 4096; + double comp_ratio = (mem_used > 0) ? (double)orig_data / mem_used : 0; + printf("zpool_stored_pages: %ld\n", stored_pages); + printf("zpool_total_size: %ld\n", mem_used); + printf("zpool_comp_ratio: %.2f\n", comp_ratio); + printf("Swapping in %d pages\n", nr_pages); + struct rusage ru0_in, ru1_in; + getrusage(RUSAGE_SELF, &ru0_in); time_ns = _now(); a = addr; /* Access the page ... this will swap it back in again */ @@ -66,7 +129,11 @@ int main(int argc, char **argv) a += PAGE_SIZE; } long swap_in_time_total_ns=_now() - time_ns; + getrusage(RUSAGE_SELF, &ru1_in); + long swap_in_sys_total_ns = tv_to_ns(&ru1_in.ru_stime) - tv_to_ns(&ru0_in.ru_stime); long swap_in_time_avg_ns= swap_in_time_total_ns/nr_pages; + long swap_in_sys_avg_ns = swap_in_sys_total_ns/nr_pages; printf("swap_in: total=%d average=%d\n", swap_in_time_total_ns, swap_in_time_avg_ns); + printf("swap_in_sys: total=%d average=%d\n", swap_in_sys_total_ns, swap_in_sys_avg_ns); } diff --git a/tests/madvise/madvise_test_mt.c b/tests/madvise/madvise_test_mt.c new file mode 100644 index 0000000..a3dda85 --- /dev/null +++ b/tests/madvise/madvise_test_mt.c @@ -0,0 +1,310 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2026, Intel Corporation + * + * madvise_test_mt — Multi-threaded zswap latency benchmark + * + * Spawns N threads, each pinned to a different CPU core, each performing + * MADV_PAGEOUT (swap-out) followed by page-fault swap-in on its own + * private memory region. Measures per-thread and aggregate latency to + * quantify IAA device contention under concurrent reclaim. + * + * Usage: + * madvise_test_mt + * + * Output (machine-parseable): + * swap_out_avg_ns= + * swap_in_avg_ns= + * threads= + * pages_per_thread= + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MADV_PAGEOUT +#define MADV_PAGEOUT 21 +#endif + +#define PAGE_SIZE 4096 + +static inline long now_ns(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000L + ts.tv_nsec; +} + +static inline long tv_to_ns(const struct timeval *tv) +{ + return tv->tv_sec * 1000000000L + tv->tv_usec * 1000L; +} + +/* + * Read zram mm_stat: "orig_data_size compr_data_size mem_used_total ..." + * Falls back to zswap sysfs if zram not found. + */ +static void read_zpool_stats(long *orig_data, long *mem_used) +{ + *orig_data = 0; + *mem_used = 0; + + const char *zpool_source = getenv("ZPOOL_SOURCE"); + int force_zswap = (zpool_source && strcmp(zpool_source, "zswap") == 0); + int force_zram = (zpool_source && strcmp(zpool_source, "zram") == 0); + + if (!force_zswap) { + FILE *fp = fopen("/sys/block/zram0/mm_stat", "r"); + if (fp) { + fscanf(fp, "%ld %*ld %ld", orig_data, mem_used); + fclose(fp); + return; + } + + if (force_zram) + return; + } + + /* Fallback to zswap */ + FILE *fp; + fp = fopen("/sys/kernel/debug/zswap/stored_pages", "r"); + if (fp) { + long stored = 0; + fscanf(fp, "%ld", &stored); + fclose(fp); + *orig_data = stored * 4096; + } + fp = fopen("/sys/kernel/debug/zswap/pool_total_size", "r"); + if (fp) { + fscanf(fp, "%ld", mem_used); + fclose(fp); + } +} + +struct thread_args { + int thread_id; + int cpu_id; + int nr_pages; + const char *datafile; + long swap_out_ns; + long swap_in_ns; + long swap_out_sys_ns; + long swap_in_sys_ns; + long zpool_orig_data; + long zpool_mem_used; +}; + +static void *worker(void *arg) +{ + struct thread_args *ta = (struct thread_args *)arg; + int nr_pages = ta->nr_pages; + + /* Pin to specific CPU */ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(ta->cpu_id, &cpuset); + pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); + + /* Allocate private anonymous pages */ + char *addr = mmap(NULL, (size_t)nr_pages * PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) { + perror("mmap"); + ta->swap_out_ns = -1; + ta->swap_in_ns = -1; + return NULL; + } + + /* Fill pages with data from file (for realistic compressibility) */ + FILE *fp = fopen(ta->datafile, "r"); + if (fp) { + /* Seek to a unique offset per thread to get different data patterns */ + long offset = (long)ta->thread_id * nr_pages * PAGE_SIZE; + fseek(fp, offset % (1024 * 1024 * 200), SEEK_SET); /* wrap within file */ + size_t read_pages = fread(addr, PAGE_SIZE, nr_pages, fp); + /* If file is smaller, fill remainder with pattern */ + if ((int)read_pages < nr_pages) { + for (int i = (int)read_pages; i < nr_pages; i++) { + memset(addr + i * PAGE_SIZE, (i * 7 + ta->thread_id) & 0xFF, PAGE_SIZE); + } + } + fclose(fp); + } else { + /* Fill with semi-compressible pattern */ + for (int i = 0; i < nr_pages; i++) { + memset(addr + i * PAGE_SIZE, (i * 7 + ta->thread_id) & 0xFF, PAGE_SIZE); + } + } + + /* Barrier: wait for all threads to be ready (handled by pthread_barrier in main) */ + + /* === Swap Out === */ + struct rusage ru0_out, ru1_out; + getrusage(RUSAGE_THREAD, &ru0_out); + long t0 = now_ns(); + int ret = madvise(addr, (size_t)nr_pages * PAGE_SIZE, MADV_PAGEOUT); + long t1 = now_ns(); + getrusage(RUSAGE_THREAD, &ru1_out); + ta->swap_out_ns = t1 - t0; + ta->swap_out_sys_ns = tv_to_ns(&ru1_out.ru_stime) - tv_to_ns(&ru0_out.ru_stime); + + if (ret != 0) { + perror("madvise MADV_PAGEOUT"); + } + + /* Brief pause to let kernel complete async pageout */ + usleep(500000); + + /* Thread 0 reads zpool stats while data is still compressed */ + if (ta->thread_id == 0) { + read_zpool_stats(&ta->zpool_orig_data, &ta->zpool_mem_used); + } + + /* === Swap In (page faults) === */ + struct rusage ru0_in, ru1_in; + getrusage(RUSAGE_THREAD, &ru0_in); + long t2 = now_ns(); + volatile char v; + char *a = addr; + for (int i = 0; i < nr_pages; i++) { + v = a[0]; + a += PAGE_SIZE; + } + long t3 = now_ns(); + getrusage(RUSAGE_THREAD, &ru1_in); + ta->swap_in_ns = t3 - t2; + ta->swap_in_sys_ns = tv_to_ns(&ru1_in.ru_stime) - tv_to_ns(&ru0_in.ru_stime); + (void)v; + + munmap(addr, (size_t)nr_pages * PAGE_SIZE); + return NULL; +} + +int main(int argc, char **argv) +{ + if (argc < 4) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + const char *datafile = argv[1]; + int nr_pages = atoi(argv[2]); + int num_threads = atoi(argv[3]); + + if (nr_pages <= 0 || num_threads <= 0) { + fprintf(stderr, "Invalid arguments: pages=%d threads=%d\n", nr_pages, num_threads); + return 1; + } + + /* Get available CPUs */ + int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); + if (num_threads > num_cpus) { + fprintf(stderr, "Warning: %d threads > %d CPUs, some will share cores\n", + num_threads, num_cpus); + } + + struct thread_args *args = calloc(num_threads, sizeof(struct thread_args)); + pthread_t *threads = calloc(num_threads, sizeof(pthread_t)); + + /* Assign each thread to a different CPU (spread evenly) */ + for (int i = 0; i < num_threads; i++) { + args[i].thread_id = i; + args[i].cpu_id = (i * (num_cpus / num_threads)) % num_cpus; + args[i].nr_pages = nr_pages; + args[i].datafile = datafile; + } + + /* Launch all threads */ + for (int i = 0; i < num_threads; i++) { + if (pthread_create(&threads[i], NULL, worker, &args[i]) != 0) { + perror("pthread_create"); + return 1; + } + } + + /* Wait for completion */ + for (int i = 0; i < num_threads; i++) { + pthread_join(threads[i], NULL); + } + + /* Compute aggregate statistics */ + long total_out = 0, total_in = 0; + long total_out_sys = 0, total_in_sys = 0; + long max_out = 0, max_in = 0; + int valid = 0; + + for (int i = 0; i < num_threads; i++) { + if (args[i].swap_out_ns < 0) + continue; + valid++; + total_out += args[i].swap_out_ns; + total_in += args[i].swap_in_ns; + total_out_sys += args[i].swap_out_sys_ns; + total_in_sys += args[i].swap_in_sys_ns; + if (args[i].swap_out_ns > max_out) + max_out = args[i].swap_out_ns; + if (args[i].swap_in_ns > max_in) + max_in = args[i].swap_in_ns; + } + + if (valid == 0) { + fprintf(stderr, "All threads failed\n"); + free(args); + free(threads); + return 1; + } + + long avg_out_per_page = total_out / (valid * nr_pages); + long avg_in_per_page = total_in / (valid * nr_pages); + long avg_out_sys_per_page = total_out_sys / (valid * nr_pages); + long avg_in_sys_per_page = total_in_sys / (valid * nr_pages); + long max_out_per_page = max_out / nr_pages; + long max_in_per_page = max_in / nr_pages; + + /* Machine-parseable output */ + printf("threads=%d\n", num_threads); + printf("pages_per_thread=%d\n", nr_pages); + printf("swap_out_avg_ns=%ld\n", avg_out_per_page); + printf("swap_out_max_ns=%ld\n", max_out_per_page); + printf("swap_in_avg_ns=%ld\n", avg_in_per_page); + printf("swap_in_max_ns=%ld\n", max_in_per_page); + printf("swap_out_sys_avg_ns=%ld\n", avg_out_sys_per_page); + printf("swap_in_sys_avg_ns=%ld\n", avg_in_sys_per_page); + + /* Zpool stats from thread 0 */ + long orig_data = args[0].zpool_orig_data; + long mem_used = args[0].zpool_mem_used; + long stored_pages = orig_data / 4096; + double comp_ratio = (mem_used > 0) ? (double)orig_data / mem_used : 0; + printf("zpool_stored_pages=%ld\n", stored_pages); + printf("zpool_total_size=%ld\n", mem_used); + printf("zpool_comp_ratio=%.2f\n", comp_ratio); + + /* Per-thread detail */ + fprintf(stderr, "\nPer-thread results (ns/page):\n"); + fprintf(stderr, "%4s %8s %8s %10s %10s\n", + "TID", "CPU", "Pages", "Out(ns/pg)", "In(ns/pg)"); + for (int i = 0; i < num_threads; i++) { + if (args[i].swap_out_ns < 0) { + fprintf(stderr, "%4d %8d %8d %10s %10s\n", + i, args[i].cpu_id, nr_pages, "FAIL", "FAIL"); + } else { + fprintf(stderr, "%4d %8d %8d %10ld %10ld\n", + i, args[i].cpu_id, nr_pages, + args[i].swap_out_ns / nr_pages, + args[i].swap_in_ns / nr_pages); + } + } + + free(args); + free(threads); + return 0; +} diff --git a/tests/madvise/zram_microbench.sh b/tests/madvise/zram_microbench.sh new file mode 100755 index 0000000..a502437 --- /dev/null +++ b/tests/madvise/zram_microbench.sh @@ -0,0 +1,503 @@ +#!/usr/bin/env bash +#SPDX-License-Identifier: BSD-3-Clause +#Copyright (c) 2026, Intel Corporation + +# zram_microbench.sh — Measure zram per-page latency under varying concurrency +# and batching configurations (matching redis/benchmark.sh profile naming). +# +# Compressor profiles use the convention: _r_p +# +# Usage: +# sudo ./zram_microbench.sh [options] +# +# Options: +# --compressor, -c Compressor profile or 'all' (default: all) +# --threads, -t Comma-separated thread counts (default: 1,2,4,8,16,32) +# --frequency, -f Core frequency for IAA (default: 3200) +# --pages, -p Pages per thread (default: from dataset) +# --dataset, -d Data file for page content (default: silesia.tar) +# --help, -h Show this help + +set -euo pipefail + +THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# ─── Defaults ───────────────────────────────────────────────────────── +DATASET="silesia.tar" +CORE_FREQUENCY=3200 +COMPRESSOR="all" +THREAD_LIST_STR="1,2,4,8,16,32,64" +PAGES_OVERRIDE="" +BASELINE_ONLY=1 + +# ─── Usage ──────────────────────────────────────────────────────────── +print_usage() { + cat <<'EOF' +Usage: + zram_microbench.sh [options] + +Options: + --compressor, -c Compressor profile(s) (default: all) + Examples: deflate-iaa_r1_p3, lz4_r1_p3, all + --threads, -t Comma-separated thread counts (default: 1,2,4,8,16,32) + --multi-thread, -m Run multi-threaded sweep (default: baseline only) + --frequency, -f Core frequency (default: 3200) + --pages, -p Pages per thread (0 = use full dataset, default) + --dataset, -d Data file for page content (default: silesia.tar) + --help, -h Show this help + +Compressor profile naming: _r_p + deflate-iaa_r1_p3 IAA deflate, batchsize=1, page_cluster=3 + deflate-iaa_r64_p5 IAA deflate, batchsize=64, page_cluster=5 + deflate-iaa-dynamic_r64_p5 IAA dynamic mode, batchsize=64, page_cluster=5 + lz4_r1_p3 lz4 CPU, batchsize=1, page_cluster=3 + lzo_r1_p3 lzo CPU, batchsize=1, page_cluster=3 + zstd_r1_p3 zstd CPU, batchsize=1, page_cluster=3 + +Flow: + For each compressor profile: + 1. Configure zram compressor, reclaim_batchsize, page_cluster + 2. Run single-threaded baseline + 3. Run multi-threaded tests at each thread count + 4. Report per-page latency table +EOF +} + +# ─── Parse arguments ────────────────────────────────────────────────── +while [[ $# -gt 0 ]]; do + case "$1" in + --compressor|-c) COMPRESSOR="$2"; shift 2 ;; + --threads|-t) THREAD_LIST_STR="$2"; shift 2 ;; + --multi-thread|-m) BASELINE_ONLY=0; shift ;; + --frequency|-f) CORE_FREQUENCY="$2"; shift 2 ;; + --pages|-p) PAGES_OVERRIDE="$2"; shift 2 ;; + --dataset|-d) DATASET="$2"; shift 2 ;; + --help|-h) print_usage; exit 0 ;; + *) echo "Unknown option: $1"; print_usage; exit 1 ;; + esac +done + +# Parse thread list +IFS=',' read -ra THREAD_LIST <<< "$THREAD_LIST_STR" + +# ─── Helpers ────────────────────────────────────────────────────────── +handle_error() { + echo "Error: $1" + exit 1 +} + +# Format integer values with thousands separators for readable console tables. +fmt_int_commas() { + local n="$1" + if [[ ! "$n" =~ ^-?[0-9]+$ ]]; then + echo "$n" + return + fi + + local sign="" + if [[ "$n" == -* ]]; then + sign="-" + n="${n#-}" + fi + + local out="" + while (( ${#n} > 3 )); do + out=",${n: -3}${out}" + n="${n:0:${#n}-3}" + done + echo "${sign}${n}${out}" +} + +# Configure zram/kernel params for a compressor profile +configure_compressor() { + local profile="$1" + local comp_algo="$profile" + local reclaim_batchsize=1 + local page_cluster=3 + + # Parse profile: _r_p + if [[ "$profile" =~ ^(.+)_r([0-9]+)_p([0-9]+)$ ]]; then + comp_algo="${BASH_REMATCH[1]}" + reclaim_batchsize="${BASH_REMATCH[2]}" + page_cluster="${BASH_REMATCH[3]}" + fi + + "${THIS_DIR}/../scripts/config_sys_zram.sh" \ + -c "$comp_algo" \ + -r "$reclaim_batchsize" \ + -p "$page_cluster" \ + -f "$CORE_FREQUENCY" > /dev/null 2>&1 +} + +# ─── Build compressor list ──────────────────────────────────────────── +declare -a compressor_list=() +if [[ "$COMPRESSOR" == "all" ]]; then + if [ -f /proc/sys/vm/reclaim-batchsize ]; then + compressor_list=( + "lzo-rle_r1_p3" + "lz4_r1_p3" + "zstd_r1_p3" + "deflate-iaa_r1_p3" + "deflate-iaa_r64_p5" + "deflate-iaa-dynamic_r1_p3" + "deflate-iaa-dynamic_r64_p5" + ) + else + compressor_list=( + "lzo-rle_r1_p3" + "lz4_r1_p3" + "zstd_r1_p3" + "deflate-iaa_r1_p3" + "deflate-iaa-dynamic_r1_p3" + ) + fi +else + compressor_list=("$COMPRESSOR") +fi + +# ─── Setup ──────────────────────────────────────────────────────────── + +# Check if the dataset exists, if not download it +if [ ! -f "$DATASET" ] ; then + echo "Downloading $DATASET from http://wanos.co/assets/silesia.tar" + wget --no-check-certificate "http://wanos.co/assets/silesia.tar" || handle_error "Failed to download $DATASET" +fi + +# Calculate pages from dataset +SZ=$(ls -s "$DATASET" | awk '{print $1}') +NPGS=$(echo "scale=0; $SZ/4-1" | bc -l) + +if [[ -n "$PAGES_OVERRIDE" ]] && (( PAGES_OVERRIDE > 0 )); then + NPGS_PER_THREAD=$PAGES_OVERRIDE +else + NPGS_PER_THREAD=$NPGS +fi + +# Build executables +echo "Building madvise_test..." +gcc -O2 -o madvise_test madvise_test.c || handle_error "Failed to build madvise_test" + +echo "Building madvise_test_mt..." +gcc -O2 -pthread -o madvise_test_mt madvise_test_mt.c || handle_error "Failed to build madvise_test_mt" + +# Disable THP (reduces measurement noise) +echo 'never' > /sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled 2>/dev/null || true +echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null || true + +echo "=========================================" +echo " zram Microbenchmark" +echo "=========================================" +echo "Dataset: $DATASET ($SZ KB)" +echo "Pages/thread: $NPGS_PER_THREAD ($(( NPGS_PER_THREAD * 4 / 1024 )) MB)" +echo "Thread list: ${THREAD_LIST[*]}" +echo "Frequency: ${CORE_FREQUENCY} MHz" +echo "Compressors: ${compressor_list[*]}" +echo "=========================================" +echo "" + +# ─── Run benchmarks ────────────────────────────────────────────────── + +# Collect all results first, then print clean table +declare -a ALL_PROFILES=() +declare -a ALL_BASELINE_OUT=() +declare -a ALL_BASELINE_OUT_TOTAL=() +declare -a ALL_BASELINE_OUT_SYS_TOTAL=() +declare -a ALL_BASELINE_IN=() +declare -a ALL_BASELINE_IN_TOTAL=() +declare -a ALL_BASELINE_IN_SYS_TOTAL=() +declare -a ALL_RATIO=() +declare -a ALL_STORED_PAGES=() +declare -a ALL_POOL_PAGES=() +declare -a ALL_MT_OUT=() +declare -a ALL_MT_IN=() +declare -a ALL_MT_OUT_SYS=() +declare -a ALL_MT_IN_SYS=() +declare -a ALL_MT_RATIO=() +declare -a ALL_MT_ZPOOL_PGS=() + +for profile in "${compressor_list[@]}"; do + echo "Testing: $profile ..." >&2 + configure_compressor "$profile" + + # Single-threaded baseline + baseline_result=$(./madvise_test "$DATASET" "$NPGS_PER_THREAD" 2>&1) + b_out=$(echo "$baseline_result" | grep "swap_out:" | grep -oP 'average=\K[0-9]+') + b_out_total=$(echo "$baseline_result" | grep "swap_out:" | grep -oP 'total=\K[0-9]+') + b_out_sys_total=$(echo "$baseline_result" | grep "swap_out_sys:" | grep -oP 'total=\K[0-9]+') + b_in=$(echo "$baseline_result" | grep "swap_in:" | grep -oP 'average=\K[0-9]+') + b_in_total=$(echo "$baseline_result" | grep "swap_in:" | grep -oP 'total=\K[0-9]+') + b_in_sys_total=$(echo "$baseline_result" | grep "swap_in_sys:" | grep -oP 'total=\K[0-9]+') + + # Capture compression ratio and zpool pages from madvise_test output + b_ratio=$(echo "$baseline_result" | grep -oP 'zpool_comp_ratio:\s*\K[0-9.]+') + b_stored=$(echo "$baseline_result" | grep -oP 'zpool_stored_pages:\s*\K[0-9]+') + b_pool_size=$(echo "$baseline_result" | grep -oP 'zpool_total_size:\s*\K[0-9]+') + b_pool_pages=0 + if [[ -n "$b_pool_size" ]] && [[ "$b_pool_size" -gt 0 ]]; then + b_pool_pages=$(( b_pool_size / 4096 )) + fi + # Multi-threaded sweep + mt_out="" + mt_in="" + mt_out_sys="" + mt_in_sys="" + mt_ratio="" + mt_zpool_pgs="" + if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + result=$(./madvise_test_mt "$DATASET" "$NPGS_PER_THREAD" "$nthreads" 2>/dev/null) + avg_out=$(echo "$result" | grep "swap_out_avg_ns" | awk -F= '{print $2}') + avg_in=$(echo "$result" | grep "swap_in_avg_ns" | awk -F= '{print $2}') + avg_out_sys=$(echo "$result" | grep "swap_out_sys_avg_ns" | awk -F= '{print $2}') + avg_in_sys=$(echo "$result" | grep "swap_in_sys_avg_ns" | awk -F= '{print $2}') + ratio=$(echo "$result" | grep "zpool_comp_ratio" | awk -F= '{print $2}') + pool_size=$(echo "$result" | grep "zpool_total_size" | awk -F= '{print $2}') + pool_pgs=0 + if [[ -n "$pool_size" ]] && [[ "$pool_size" -gt 0 ]]; then + pool_pgs=$(( pool_size / 4096 )) + fi + mt_out+="${avg_out:-err} " + mt_in+="${avg_in:-err} " + mt_out_sys+="${avg_out_sys:-err} " + mt_in_sys+="${avg_in_sys:-err} " + mt_ratio+="${ratio:-0} " + mt_zpool_pgs+="${pool_pgs:-0} " + done + fi + + ALL_PROFILES+=("$profile") + ALL_BASELINE_OUT+=("${b_out:-err}") + ALL_BASELINE_OUT_TOTAL+=("${b_out_total:-0}") + ALL_BASELINE_OUT_SYS_TOTAL+=("${b_out_sys_total:-0}") + ALL_BASELINE_IN+=("${b_in:-err}") + ALL_BASELINE_IN_TOTAL+=("${b_in_total:-0}") + ALL_BASELINE_IN_SYS_TOTAL+=("${b_in_sys_total:-0}") + ALL_RATIO+=("${b_ratio:-0}") + ALL_STORED_PAGES+=("${b_stored:-0}") + ALL_POOL_PAGES+=("${b_pool_pages:-0}") + ALL_MT_OUT+=("$mt_out") + ALL_MT_IN+=("$mt_in") + ALL_MT_OUT_SYS+=("$mt_out_sys") + ALL_MT_IN_SYS+=("$mt_in_sys") + ALL_MT_RATIO+=("$mt_ratio") + ALL_MT_ZPOOL_PGS+=("$mt_zpool_pgs") +done + +# ─── Print clean report ────────────────────────────────────────────── +echo "" +echo "=========================================" +echo " Swap-Out (Compression) ns/page" +echo "=========================================" +echo "" +printf "%-32s %10s" "Compressor" "1T(base)" +if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + printf " %8s" "${nthreads}T" + done +fi +echo "" +printf "%-32s %10s" "────────────────────────────────" "────────" +if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + printf " %8s" "────────" + done +fi +echo "" + +for i in "${!ALL_PROFILES[@]}"; do + base_out_fmt="$(fmt_int_commas "${ALL_BASELINE_OUT[$i]}")" + printf "%-32s %10s" "${ALL_PROFILES[$i]}" "$base_out_fmt" + if (( BASELINE_ONLY == 0 )); then + for val in ${ALL_MT_OUT[$i]}; do + val_fmt="$(fmt_int_commas "$val")" + printf " %8s" "$val_fmt" + done + fi + echo "" +done + +echo "" +echo "=========================================" +echo " Swap-In (Decompression) ns/page" +echo "=========================================" +echo "" +printf "%-32s %10s" "Compressor" "1T(base)" +if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + printf " %8s" "${nthreads}T" + done +fi +echo "" +printf "%-32s %10s" "────────────────────────────────" "────────" +if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + printf " %8s" "────────" + done +fi +echo "" + +for i in "${!ALL_PROFILES[@]}"; do + base_in_fmt="$(fmt_int_commas "${ALL_BASELINE_IN[$i]}")" + printf "%-32s %10s" "${ALL_PROFILES[$i]}" "$base_in_fmt" + if (( BASELINE_ONLY == 0 )); then + for val in ${ALL_MT_IN[$i]}; do + val_fmt="$(fmt_int_commas "$val")" + printf " %8s" "$val_fmt" + done + fi + echo "" +done + +# ─── Single-thread: Memory, Time ─────────────────────────────────────── +total_pages_1t="$NPGS_PER_THREAD" +total_mb_1t=$(( total_pages_1t * 4 / 1024 )) +total_pages_1t_fmt="$(fmt_int_commas "$total_pages_1t")" +total_mb_1t_fmt="$(fmt_int_commas "$total_mb_1t")" +echo "" +echo " threads=1T, pages_per_thread=${NPGS_PER_THREAD}, total_pages=${total_pages_1t_fmt}, test_workset_mb=${total_mb_1t_fmt}" +echo "" +printf "%-28s %6s %10s %12s %12s %15s %15s\n" "Compressor" "Ratio" "Zpool-Pgs" "Swap-Out(ms)" "Swap-In(ms)" "Swap-Out-Sys(ms)" "Swap-In-Sys(ms)" +printf "%-28s %6s %10s %12s %12s %15s %15s\n" "────────────────────────────" "──────" "────────" "──────────" "──────────" "──────────────" "──────────────" +for i in "${!ALL_PROFILES[@]}"; do + pool_pg="${ALL_POOL_PAGES[$i]}" + out_total="${ALL_BASELINE_OUT_TOTAL[$i]}" + out_sys_total="${ALL_BASELINE_OUT_SYS_TOTAL[$i]}" + in_total="${ALL_BASELINE_IN_TOTAL[$i]}" + in_sys_total="${ALL_BASELINE_IN_SYS_TOTAL[$i]}" + out_ms="--" + out_sys_ms="--" + in_ms="--" + in_sys_ms="--" + + if [[ "$out_total" -gt 0 ]]; then + out_ms=$(awk -v t="$out_total" 'BEGIN{printf "%.1f", t/1000000}') + fi + if [[ "$in_total" -gt 0 ]]; then + in_ms=$(awk -v t="$in_total" 'BEGIN{printf "%.1f", t/1000000}') + fi + if [[ "$out_sys_total" -gt 0 ]]; then + out_sys_ms=$(awk -v t="$out_sys_total" 'BEGIN{printf "%.1f", t/1000000}') + fi + if [[ "$in_sys_total" -gt 0 ]]; then + in_sys_ms=$(awk -v t="$in_sys_total" 'BEGIN{printf "%.1f", t/1000000}') + fi + pool_pg_fmt="$(fmt_int_commas "$pool_pg")" + printf "%-28s %6s %10s %12s %12s %15s %15s\n" \ + "${ALL_PROFILES[$i]}" "${ALL_RATIO[$i]}" "$pool_pg_fmt" "$out_ms" "$in_ms" "$out_sys_ms" "$in_sys_ms" +done + +# ─── Multi-threaded: Memory, Time per thread count ─────────────────── +if (( BASELINE_ONLY == 0 )); then + j=0 + for nthreads in "${THREAD_LIST[@]}"; do + total_pages=$(( NPGS_PER_THREAD * nthreads )) + total_mb=$(( total_pages * 4 / 1024 )) + total_pages_fmt="$(fmt_int_commas "$total_pages")" + total_mb_fmt="$(fmt_int_commas "$total_mb")" + echo "" + echo "-----------------------------------------" + echo " threads=${nthreads}T, pages_per_thread=${NPGS_PER_THREAD}, total_pages=${total_pages_fmt}, test_workset_mb=${total_mb_fmt}" + echo "" + printf "%-28s %6s %10s %12s %12s %15s %15s\n" "Compressor" "Ratio" "Zpool-Pgs" "Swap-Out(ns)" "Swap-In(ns)" "Swap-Out-Sys(ns)" "Swap-In-Sys(ns)" + printf "%-28s %6s %10s %12s %12s %15s %15s\n" "────────────────────────────" "──────" "────────" "──────────" "──────────" "──────────────" "──────────────" + + for i in "${!ALL_PROFILES[@]}"; do + # Get the j-th value from each space-separated array + read -ra out_arr <<< "${ALL_MT_OUT[$i]}" + read -ra in_arr <<< "${ALL_MT_IN[$i]}" + read -ra out_sys_arr <<< "${ALL_MT_OUT_SYS[$i]}" + read -ra in_sys_arr <<< "${ALL_MT_IN_SYS[$i]}" + read -ra ratio_arr <<< "${ALL_MT_RATIO[$i]}" + read -ra zpool_arr <<< "${ALL_MT_ZPOOL_PGS[$i]}" + + ns_out="${out_arr[$j]:-err}" + ns_in="${in_arr[$j]:-err}" + ns_out_sys="${out_sys_arr[$j]:-err}" + ns_in_sys="${in_sys_arr[$j]:-err}" + ratio="${ratio_arr[$j]:-0}" + zpool_pgs="${zpool_arr[$j]:-0}" + + ns_out_fmt="$(fmt_int_commas "$ns_out")" + ns_in_fmt="$(fmt_int_commas "$ns_in")" + ns_out_sys_fmt="$(fmt_int_commas "$ns_out_sys")" + ns_in_sys_fmt="$(fmt_int_commas "$ns_in_sys")" + zpool_pgs_fmt="$(fmt_int_commas "$zpool_pgs")" + + printf "%-28s %6s %10s %12s %12s %15s %15s\n" \ + "${ALL_PROFILES[$i]}" "$ratio" "$zpool_pgs_fmt" "$ns_out_fmt" "$ns_in_fmt" "$ns_out_sys_fmt" "$ns_in_sys_fmt" + done + j=$((j+1)) + done +fi +echo "" + +# ─── TSV output for Excel ───────────────────────────────────────────── +TSV_FILE="${THIS_DIR}/results.tsv" +{ + # Single-threaded baseline + printf "Compressor\tRatio\tZpool-Pgs\tSwap-Out(ms)\tSwap-In(ms)\tSwap-Out-Sys(ms)\tSwap-In-Sys(ms)\n" + for i in "${!ALL_PROFILES[@]}"; do + out_ms=$(awk -v t="${ALL_BASELINE_OUT_TOTAL[$i]}" 'BEGIN{printf "%.1f", t/1000000}') + in_ms=$(awk -v t="${ALL_BASELINE_IN_TOTAL[$i]}" 'BEGIN{printf "%.1f", t/1000000}') + out_sys_ms=$(awk -v t="${ALL_BASELINE_OUT_SYS_TOTAL[$i]}" 'BEGIN{printf "%.1f", t/1000000}') + in_sys_ms=$(awk -v t="${ALL_BASELINE_IN_SYS_TOTAL[$i]}" 'BEGIN{printf "%.1f", t/1000000}') + printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n" \ + "${ALL_PROFILES[$i]}" "${ALL_RATIO[$i]}" "${ALL_POOL_PAGES[$i]}" "$out_ms" "$in_ms" "$out_sys_ms" "$in_sys_ms" + done + + if (( BASELINE_ONLY == 0 )); then + # Multi-threaded: one block per thread count + j=0 + for nthreads in "${THREAD_LIST[@]}"; do + printf "\n" + printf "${nthreads}T\tCompressor\tRatio\tZpool-Pgs\tSwap-Out(ns/pg)\tSwap-In(ns/pg)\tSwap-Out-Sys(ns/pg)\tSwap-In-Sys(ns/pg)\n" + for i in "${!ALL_PROFILES[@]}"; do + read -ra out_arr <<< "${ALL_MT_OUT[$i]}" + read -ra in_arr <<< "${ALL_MT_IN[$i]}" + read -ra out_sys_arr <<< "${ALL_MT_OUT_SYS[$i]}" + read -ra in_sys_arr <<< "${ALL_MT_IN_SYS[$i]}" + read -ra ratio_arr <<< "${ALL_MT_RATIO[$i]}" + read -ra zpool_arr <<< "${ALL_MT_ZPOOL_PGS[$i]}" + printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" \ + "${nthreads}T" "${ALL_PROFILES[$i]}" "${ratio_arr[$j]:-0}" "${zpool_arr[$j]:-0}" "${out_arr[$j]:-err}" "${in_arr[$j]:-err}" "${out_sys_arr[$j]:-err}" "${in_sys_arr[$j]:-err}" + done + j=$((j+1)) + done + + # Flat swap-out table (all threads in one sheet-friendly block) + printf "\n" + printf "Swap-Out(ns/pg)\t1T(base)" + for nthreads in "${THREAD_LIST[@]}"; do + printf "\t%sT" "$nthreads" + done + printf "\n" + for i in "${!ALL_PROFILES[@]}"; do + base_out_fmt="$(fmt_int_commas "${ALL_BASELINE_OUT[$i]}")" + printf "%s\t%s" "${ALL_PROFILES[$i]}" "$base_out_fmt" + for val in ${ALL_MT_OUT[$i]}; do + val_fmt="$(fmt_int_commas "$val")" + printf "\t%s" "$val_fmt" + done + printf "\n" + done + + # Flat swap-in table + printf "\n" + printf "Swap-In(ns/pg)\t1T(base)" + for nthreads in "${THREAD_LIST[@]}"; do + printf "\t%sT" "$nthreads" + done + printf "\n" + for i in "${!ALL_PROFILES[@]}"; do + base_in_fmt="$(fmt_int_commas "${ALL_BASELINE_IN[$i]}")" + printf "%s\t%s" "${ALL_PROFILES[$i]}" "$base_in_fmt" + for val in ${ALL_MT_IN[$i]}; do + val_fmt="$(fmt_int_commas "$val")" + printf "\t%s" "$val_fmt" + done + printf "\n" + done + fi +} > "$TSV_FILE" +echo "TSV saved: $TSV_FILE (paste into Excel)" + diff --git a/tests/madvise/zswap_microbench.sh b/tests/madvise/zswap_microbench.sh new file mode 100755 index 0000000..10aa76c --- /dev/null +++ b/tests/madvise/zswap_microbench.sh @@ -0,0 +1,488 @@ +#!/usr/bin/env bash +#SPDX-License-Identifier: BSD-3-Clause +#Copyright (c) 2026, Intel Corporation + +# zswap_microbench.sh - Measure zswap per-page latency under varying concurrency +# and batching configurations (matching redis/benchmark.sh profile naming). +# +# Compressor profiles use the convention: _r_p +# +# Usage: +# sudo ./zswap_microbench.sh [options] +# +# Options: +# --compressor, -c Compressor profile or 'all' (default: all) +# --threads, -t Comma-separated thread counts (default: 1,2,4,8,16,32) +# --frequency, -f Core frequency for IAA (default: 3200) +# --pages, -p Pages per thread (default: from dataset) +# --dataset, -d Data file for page content (default: silesia.tar) +# --help, -h Show this help + +set -euo pipefail + +THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# --- Defaults --------------------------------------------------------- +DATASET="silesia.tar" +CORE_FREQUENCY=3200 +COMPRESSOR="all" +THREAD_LIST_STR="1,2,4,8,16,32,64" +PAGES_OVERRIDE="" +BASELINE_ONLY=1 + +# --- Usage ------------------------------------------------------------ +print_usage() { + cat <<'EOF' +Usage: + zswap_microbench.sh [options] + +Options: + --compressor, -c Compressor profile(s) (default: all) + Examples: deflate-iaa_r1_p3, lz4_r1_p3, all + --threads, -t Comma-separated thread counts (default: 1,2,4,8,16,32) + --multi-thread, -m Run multi-threaded sweep (default: baseline only) + --frequency, -f Core frequency (default: 3200) + --pages, -p Pages per thread (0 = use full dataset, default) + --dataset, -d Data file for page content (default: silesia.tar) + --help, -h Show this help + +Compressor profile naming: _r_p + deflate-iaa_r1_p3 IAA deflate, batchsize=1, page_cluster=3 + deflate-iaa_r64_p5 IAA deflate, batchsize=64, page_cluster=5 + deflate-iaa-dynamic_r64_p5 IAA dynamic mode, batchsize=64, page_cluster=5 + lz4_r1_p3 lz4 CPU, batchsize=1, page_cluster=3 + lzo-rle_r1_p3 lzo-rle CPU, batchsize=1, page_cluster=3 + zstd_r1_p3 zstd CPU, batchsize=1, page_cluster=3 + +Flow: + For each compressor profile: + 1. Configure zswap compressor, reclaim_batchsize, page_cluster + 2. Run single-threaded baseline + 3. Run multi-threaded tests at each thread count + 4. Report per-page latency and zpool metrics +EOF +} + +# --- Parse arguments -------------------------------------------------- +while [[ $# -gt 0 ]]; do + case "$1" in + --compressor|-c) COMPRESSOR="$2"; shift 2 ;; + --threads|-t) THREAD_LIST_STR="$2"; shift 2 ;; + --multi-thread|-m) BASELINE_ONLY=0; shift ;; + --frequency|-f) CORE_FREQUENCY="$2"; shift 2 ;; + --pages|-p) PAGES_OVERRIDE="$2"; shift 2 ;; + --dataset|-d) DATASET="$2"; shift 2 ;; + --help|-h) print_usage; exit 0 ;; + *) echo "Unknown option: $1"; print_usage; exit 1 ;; + esac +done + +IFS=',' read -ra THREAD_LIST <<< "$THREAD_LIST_STR" + +# --- Helpers ---------------------------------------------------------- +handle_error() { + echo "Error: $1" + exit 1 +} + +# Format integer values with thousands separators for readable console tables. +fmt_int_commas() { + local n="$1" + if [[ ! "$n" =~ ^-?[0-9]+$ ]]; then + echo "$n" + return + fi + + local sign="" + if [[ "$n" == -* ]]; then + sign="-" + n="${n#-}" + fi + + local out="" + while (( ${#n} > 3 )); do + out=",${n: -3}${out}" + n="${n:0:${#n}-3}" + done + echo "${sign}${n}${out}" +} + +# Configure zswap/kernel params for a compressor profile. +configure_compressor() { + local profile="$1" + local comp_algo="$profile" + local reclaim_batchsize=1 + local page_cluster=3 + + if [[ "$profile" =~ ^(.+)_r([0-9]+)_p([0-9]+)$ ]]; then + comp_algo="${BASH_REMATCH[1]}" + reclaim_batchsize="${BASH_REMATCH[2]}" + page_cluster="${BASH_REMATCH[3]}" + fi + + "${THIS_DIR}/../scripts/config_sys_zswap.sh" \ + -c "$comp_algo" \ + -r "$reclaim_batchsize" \ + -p "$page_cluster" \ + -f "$CORE_FREQUENCY" > /dev/null 2>&1 +} + +# --- Build compressor list ------------------------------------------- +declare -a compressor_list=() +if [[ "$COMPRESSOR" == "all" ]]; then + if [ -f /proc/sys/vm/reclaim-batchsize ]; then + compressor_list=( + "lzo-rle_r1_p3" + "lz4_r1_p3" + "zstd_r1_p3" + "deflate-iaa_r1_p3" + "deflate-iaa_r64_p5" + "deflate-iaa-dynamic_r1_p3" + "deflate-iaa-dynamic_r64_p5" + ) + else + compressor_list=( + "lzo-rle_r1_p3" + "lz4_r1_p3" + "zstd_r1_p3" + "deflate-iaa_r1_p3" + "deflate-iaa-dynamic_r1_p3" + ) + fi +else + compressor_list=("$COMPRESSOR") +fi + +# --- Setup ------------------------------------------------------------ +if [ ! -f "$DATASET" ] ; then + echo "Downloading $DATASET from http://wanos.co/assets/silesia.tar" + wget --no-check-certificate "http://wanos.co/assets/silesia.tar" || handle_error "Failed to download $DATASET" +fi + +SZ=$(ls -s "$DATASET" | awk '{print $1}') +NPGS=$(echo "scale=0; $SZ/4-1" | bc -l) + +if [[ -n "$PAGES_OVERRIDE" ]] && (( PAGES_OVERRIDE > 0 )); then + NPGS_PER_THREAD=$PAGES_OVERRIDE +else + NPGS_PER_THREAD=$NPGS +fi + +echo "Building madvise_test..." +gcc -O2 -o madvise_test madvise_test.c || handle_error "Failed to build madvise_test" + +echo "Building madvise_test_mt..." +gcc -O2 -pthread -o madvise_test_mt madvise_test_mt.c || handle_error "Failed to build madvise_test_mt" + +# Disable THP (reduces measurement noise) +echo 'never' > /sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled 2>/dev/null || true +echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null || true + +echo "=========================================" +echo " zswap Microbenchmark" +echo "=========================================" +echo "Dataset: $DATASET ($SZ KB)" +echo "Pages/thread: $NPGS_PER_THREAD ($(( NPGS_PER_THREAD * 4 / 1024 )) MB)" +echo "Thread list: ${THREAD_LIST[*]}" +echo "Frequency: ${CORE_FREQUENCY} MHz" +echo "Compressors: ${compressor_list[*]}" +echo "=========================================" +echo "" + +# --- Run benchmarks --------------------------------------------------- +declare -a ALL_PROFILES=() +declare -a ALL_BASELINE_OUT=() +declare -a ALL_BASELINE_OUT_TOTAL=() +declare -a ALL_BASELINE_OUT_SYS_TOTAL=() +declare -a ALL_BASELINE_IN=() +declare -a ALL_BASELINE_IN_TOTAL=() +declare -a ALL_BASELINE_IN_SYS_TOTAL=() +declare -a ALL_RATIO=() +declare -a ALL_STORED_PAGES=() +declare -a ALL_POOL_PAGES=() +declare -a ALL_MT_OUT=() +declare -a ALL_MT_IN=() +declare -a ALL_MT_OUT_SYS=() +declare -a ALL_MT_IN_SYS=() +declare -a ALL_MT_RATIO=() +declare -a ALL_MT_ZPOOL_PGS=() + +for profile in "${compressor_list[@]}"; do + echo "Testing: $profile ..." >&2 + configure_compressor "$profile" + + # Force zswap debugfs metrics for this benchmark. + baseline_result=$(ZPOOL_SOURCE=zswap ./madvise_test "$DATASET" "$NPGS_PER_THREAD" 2>&1) + b_out=$(echo "$baseline_result" | grep "swap_out:" | grep -oP 'average=\K[0-9]+') + b_out_total=$(echo "$baseline_result" | grep "swap_out:" | grep -oP 'total=\K[0-9]+') + b_out_sys_total=$(echo "$baseline_result" | grep "swap_out_sys:" | grep -oP 'total=\K[0-9]+') + b_in=$(echo "$baseline_result" | grep "swap_in:" | grep -oP 'average=\K[0-9]+') + b_in_total=$(echo "$baseline_result" | grep "swap_in:" | grep -oP 'total=\K[0-9]+') + b_in_sys_total=$(echo "$baseline_result" | grep "swap_in_sys:" | grep -oP 'total=\K[0-9]+') + + b_ratio=$(echo "$baseline_result" | grep -oP 'zpool_comp_ratio:\s*\K[0-9.]+') + b_stored=$(echo "$baseline_result" | grep -oP 'zpool_stored_pages:\s*\K[0-9]+') + b_pool_size=$(echo "$baseline_result" | grep -oP 'zpool_total_size:\s*\K[0-9]+') + b_pool_pages=0 + if [[ -n "$b_pool_size" ]] && [[ "$b_pool_size" -gt 0 ]]; then + b_pool_pages=$(( b_pool_size / 4096 )) + fi + + mt_out="" + mt_in="" + mt_out_sys="" + mt_in_sys="" + mt_ratio="" + mt_zpool_pgs="" + if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + result=$(ZPOOL_SOURCE=zswap ./madvise_test_mt "$DATASET" "$NPGS_PER_THREAD" "$nthreads" 2>/dev/null) + avg_out=$(echo "$result" | grep "swap_out_avg_ns" | awk -F= '{print $2}') + avg_in=$(echo "$result" | grep "swap_in_avg_ns" | awk -F= '{print $2}') + avg_out_sys=$(echo "$result" | grep "swap_out_sys_avg_ns" | awk -F= '{print $2}') + avg_in_sys=$(echo "$result" | grep "swap_in_sys_avg_ns" | awk -F= '{print $2}') + ratio=$(echo "$result" | grep "zpool_comp_ratio" | awk -F= '{print $2}') + pool_size=$(echo "$result" | grep "zpool_total_size" | awk -F= '{print $2}') + pool_pgs=0 + if [[ -n "$pool_size" ]] && [[ "$pool_size" -gt 0 ]]; then + pool_pgs=$(( pool_size / 4096 )) + fi + mt_out+="${avg_out:-err} " + mt_in+="${avg_in:-err} " + mt_out_sys+="${avg_out_sys:-err} " + mt_in_sys+="${avg_in_sys:-err} " + mt_ratio+="${ratio:-0} " + mt_zpool_pgs+="${pool_pgs:-0} " + done + fi + + ALL_PROFILES+=("$profile") + ALL_BASELINE_OUT+=("${b_out:-err}") + ALL_BASELINE_OUT_TOTAL+=("${b_out_total:-0}") + ALL_BASELINE_OUT_SYS_TOTAL+=("${b_out_sys_total:-0}") + ALL_BASELINE_IN+=("${b_in:-err}") + ALL_BASELINE_IN_TOTAL+=("${b_in_total:-0}") + ALL_BASELINE_IN_SYS_TOTAL+=("${b_in_sys_total:-0}") + ALL_RATIO+=("${b_ratio:-0}") + ALL_STORED_PAGES+=("${b_stored:-0}") + ALL_POOL_PAGES+=("${b_pool_pages:-0}") + ALL_MT_OUT+=("$mt_out") + ALL_MT_IN+=("$mt_in") + ALL_MT_OUT_SYS+=("$mt_out_sys") + ALL_MT_IN_SYS+=("$mt_in_sys") + ALL_MT_RATIO+=("$mt_ratio") + ALL_MT_ZPOOL_PGS+=("$mt_zpool_pgs") +done + +# --- Print report ----------------------------------------------------- +echo "" +echo "=========================================" +echo " Swap-Out (Compression) ns/page" +echo "=========================================" +echo "" +printf "%-32s %10s" "Compressor" "1T(base)" +if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + printf " %8s" "${nthreads}T" + done +fi +echo "" +printf "%-32s %10s" "────────────────────────────────" "────────" +if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + printf " %8s" "────────" + done +fi +echo "" + +for i in "${!ALL_PROFILES[@]}"; do + base_out_fmt="$(fmt_int_commas "${ALL_BASELINE_OUT[$i]}")" + printf "%-32s %10s" "${ALL_PROFILES[$i]}" "$base_out_fmt" + if (( BASELINE_ONLY == 0 )); then + for val in ${ALL_MT_OUT[$i]}; do + val_fmt="$(fmt_int_commas "$val")" + printf " %8s" "$val_fmt" + done + fi + echo "" +done + +echo "" +echo "=========================================" +echo " Swap-In (Decompression) ns/page" +echo "=========================================" +echo "" +printf "%-32s %10s" "Compressor" "1T(base)" +if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + printf " %8s" "${nthreads}T" + done +fi +echo "" +printf "%-32s %10s" "────────────────────────────────" "────────" +if (( BASELINE_ONLY == 0 )); then + for nthreads in "${THREAD_LIST[@]}"; do + printf " %8s" "────────" + done +fi +echo "" + +for i in "${!ALL_PROFILES[@]}"; do + base_in_fmt="$(fmt_int_commas "${ALL_BASELINE_IN[$i]}")" + printf "%-32s %10s" "${ALL_PROFILES[$i]}" "$base_in_fmt" + if (( BASELINE_ONLY == 0 )); then + for val in ${ALL_MT_IN[$i]}; do + val_fmt="$(fmt_int_commas "$val")" + printf " %8s" "$val_fmt" + done + fi + echo "" +done + +# --- Single-thread: Memory, Time ------------------------------------- +total_pages_1t="$NPGS_PER_THREAD" +total_mb_1t=$(( total_pages_1t * 4 / 1024 )) +total_pages_1t_fmt="$(fmt_int_commas "$total_pages_1t")" +total_mb_1t_fmt="$(fmt_int_commas "$total_mb_1t")" +echo "" +echo " threads=1T, pages_per_thread=${NPGS_PER_THREAD}, total_pages=${total_pages_1t_fmt}, test_workset_mb=${total_mb_1t_fmt}" +echo "" +printf "%-28s %6s %10s %12s %12s %15s %15s\n" "Compressor" "Ratio" "Zpool-Pgs" "Swap-Out(ms)" "Swap-In(ms)" "Swap-Out-Sys(ms)" "Swap-In-Sys(ms)" +printf "%-28s %6s %10s %12s %12s %15s %15s\n" "────────────────────────────" "──────" "────────" "──────────" "──────────" "──────────────" "──────────────" +for i in "${!ALL_PROFILES[@]}"; do + pool_pg="${ALL_POOL_PAGES[$i]}" + out_total="${ALL_BASELINE_OUT_TOTAL[$i]}" + out_sys_total="${ALL_BASELINE_OUT_SYS_TOTAL[$i]}" + in_total="${ALL_BASELINE_IN_TOTAL[$i]}" + in_sys_total="${ALL_BASELINE_IN_SYS_TOTAL[$i]}" + out_ms="--" + out_sys_ms="--" + in_ms="--" + in_sys_ms="--" + + if [[ "$out_total" -gt 0 ]]; then + out_ms=$(awk -v t="$out_total" 'BEGIN{printf "%.1f", t/1000000}') + fi + if [[ "$in_total" -gt 0 ]]; then + in_ms=$(awk -v t="$in_total" 'BEGIN{printf "%.1f", t/1000000}') + fi + if [[ "$out_sys_total" -gt 0 ]]; then + out_sys_ms=$(awk -v t="$out_sys_total" 'BEGIN{printf "%.1f", t/1000000}') + fi + if [[ "$in_sys_total" -gt 0 ]]; then + in_sys_ms=$(awk -v t="$in_sys_total" 'BEGIN{printf "%.1f", t/1000000}') + fi + pool_pg_fmt="$(fmt_int_commas "$pool_pg")" + printf "%-28s %6s %10s %12s %12s %15s %15s\n" \ + "${ALL_PROFILES[$i]}" "${ALL_RATIO[$i]}" "$pool_pg_fmt" "$out_ms" "$in_ms" "$out_sys_ms" "$in_sys_ms" +done + +# --- Multi-threaded: Memory, Time ------------------------------------ +if (( BASELINE_ONLY == 0 )); then + j=0 + for nthreads in "${THREAD_LIST[@]}"; do + total_pages=$(( NPGS_PER_THREAD * nthreads )) + total_mb=$(( total_pages * 4 / 1024 )) + total_pages_fmt="$(fmt_int_commas "$total_pages")" + total_mb_fmt="$(fmt_int_commas "$total_mb")" + echo "" + echo "-----------------------------------------" + echo " threads=${nthreads}T, pages_per_thread=${NPGS_PER_THREAD}, total_pages=${total_pages_fmt}, test_workset_mb=${total_mb_fmt}" + echo "" + printf "%-28s %6s %10s %12s %12s %15s %15s\n" "Compressor" "Ratio" "Zpool-Pgs" "Swap-Out(ns)" "Swap-In(ns)" "Swap-Out-Sys(ns)" "Swap-In-Sys(ns)" + printf "%-28s %6s %10s %12s %12s %15s %15s\n" "────────────────────────────" "──────" "────────" "──────────" "──────────" "──────────────" "──────────────" + + for i in "${!ALL_PROFILES[@]}"; do + read -ra out_arr <<< "${ALL_MT_OUT[$i]}" + read -ra in_arr <<< "${ALL_MT_IN[$i]}" + read -ra out_sys_arr <<< "${ALL_MT_OUT_SYS[$i]}" + read -ra in_sys_arr <<< "${ALL_MT_IN_SYS[$i]}" + read -ra ratio_arr <<< "${ALL_MT_RATIO[$i]}" + read -ra zpool_arr <<< "${ALL_MT_ZPOOL_PGS[$i]}" + + ns_out="${out_arr[$j]:-err}" + ns_in="${in_arr[$j]:-err}" + ns_out_sys="${out_sys_arr[$j]:-err}" + ns_in_sys="${in_sys_arr[$j]:-err}" + ratio="${ratio_arr[$j]:-0}" + zpool_pgs="${zpool_arr[$j]:-0}" + + ns_out_fmt="$(fmt_int_commas "$ns_out")" + ns_in_fmt="$(fmt_int_commas "$ns_in")" + ns_out_sys_fmt="$(fmt_int_commas "$ns_out_sys")" + ns_in_sys_fmt="$(fmt_int_commas "$ns_in_sys")" + zpool_pgs_fmt="$(fmt_int_commas "$zpool_pgs")" + + printf "%-28s %6s %10s %12s %12s %15s %15s\n" \ + "${ALL_PROFILES[$i]}" "$ratio" "$zpool_pgs_fmt" "$ns_out_fmt" "$ns_in_fmt" "$ns_out_sys_fmt" "$ns_in_sys_fmt" + done + j=$((j+1)) + done +fi +echo "" + +# --- TSV output for Excel -------------------------------------------- +TSV_FILE="${THIS_DIR}/results.tsv" +{ + printf "Compressor\tRatio\tZpool-Pgs\tSwap-Out(ms)\tSwap-In(ms)\tSwap-Out-Sys(ms)\tSwap-In-Sys(ms)\n" + for i in "${!ALL_PROFILES[@]}"; do + out_ms=$(awk -v t="${ALL_BASELINE_OUT_TOTAL[$i]}" 'BEGIN{printf "%.1f", t/1000000}') + in_ms=$(awk -v t="${ALL_BASELINE_IN_TOTAL[$i]}" 'BEGIN{printf "%.1f", t/1000000}') + out_sys_ms=$(awk -v t="${ALL_BASELINE_OUT_SYS_TOTAL[$i]}" 'BEGIN{printf "%.1f", t/1000000}') + in_sys_ms=$(awk -v t="${ALL_BASELINE_IN_SYS_TOTAL[$i]}" 'BEGIN{printf "%.1f", t/1000000}') + printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\n" \ + "${ALL_PROFILES[$i]}" "${ALL_RATIO[$i]}" "${ALL_POOL_PAGES[$i]}" "$out_ms" "$in_ms" "$out_sys_ms" "$in_sys_ms" + done + + if (( BASELINE_ONLY == 0 )); then + j=0 + for nthreads in "${THREAD_LIST[@]}"; do + printf "\n" + printf "${nthreads}T\tCompressor\tRatio\tZpool-Pgs\tSwap-Out(ns/pg)\tSwap-In(ns/pg)\tSwap-Out-Sys(ns/pg)\tSwap-In-Sys(ns/pg)\n" + for i in "${!ALL_PROFILES[@]}"; do + read -ra out_arr <<< "${ALL_MT_OUT[$i]}" + read -ra in_arr <<< "${ALL_MT_IN[$i]}" + read -ra out_sys_arr <<< "${ALL_MT_OUT_SYS[$i]}" + read -ra in_sys_arr <<< "${ALL_MT_IN_SYS[$i]}" + read -ra ratio_arr <<< "${ALL_MT_RATIO[$i]}" + read -ra zpool_arr <<< "${ALL_MT_ZPOOL_PGS[$i]}" + printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" \ + "${nthreads}T" "${ALL_PROFILES[$i]}" "${ratio_arr[$j]:-0}" "${zpool_arr[$j]:-0}" "${out_arr[$j]:-err}" "${in_arr[$j]:-err}" "${out_sys_arr[$j]:-err}" "${in_sys_arr[$j]:-err}" + done + j=$((j+1)) + done + + printf "\n" + printf "Swap-Out(ns/pg)\t1T(base)" + for nthreads in "${THREAD_LIST[@]}"; do + printf "\t%sT" "$nthreads" + done + printf "\n" + for i in "${!ALL_PROFILES[@]}"; do + base_out_fmt="$(fmt_int_commas "${ALL_BASELINE_OUT[$i]}")" + printf "%s\t%s" "${ALL_PROFILES[$i]}" "$base_out_fmt" + for val in ${ALL_MT_OUT[$i]}; do + val_fmt="$(fmt_int_commas "$val")" + printf "\t%s" "$val_fmt" + done + printf "\n" + done + + printf "\n" + printf "Swap-In(ns/pg)\t1T(base)" + for nthreads in "${THREAD_LIST[@]}"; do + printf "\t%sT" "$nthreads" + done + printf "\n" + for i in "${!ALL_PROFILES[@]}"; do + base_in_fmt="$(fmt_int_commas "${ALL_BASELINE_IN[$i]}")" + printf "%s\t%s" "${ALL_PROFILES[$i]}" "$base_in_fmt" + for val in ${ALL_MT_IN[$i]}; do + val_fmt="$(fmt_int_commas "$val")" + printf "\t%s" "$val_fmt" + done + printf "\n" + done + fi +} > "$TSV_FILE" +echo "TSV saved: $TSV_FILE (paste into Excel)" From 73ec3e4e087bfce8b96b78a4002b4eb58a1eba95 Mon Sep 17 00:00:00 2001 From: "Ravindran, Binuraj" Date: Fri, 10 Jul 2026 06:47:37 +0530 Subject: [PATCH 2/2] [scripts] updated lspci command to be more generic --- tests/scripts/enable_iaa.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/enable_iaa.sh b/tests/scripts/enable_iaa.sh index f4c9809..b7473c3 100755 --- a/tests/scripts/enable_iaa.sh +++ b/tests/scripts/enable_iaa.sh @@ -92,7 +92,7 @@ LOG="configure_iaa.log" wq_size=$(( 128 / iaa_wqs )) # Take care of the enumeration, if DSA is enabled. -dsa=`lspci | grep -c 0b25` +dsa=`lspci -Dnn | grep -c 0b25` # Set enumeration parameters for iax devices first=0 step=1