The instructions for creating a checkpoint library are explained in detail here. Please find below a more elaborate sequence of instructions to build a checkpoint library.

  1. First, download the QFlex Linux image.
cd QPoints/scripts/qflex
./download_image.sh
mv root.qcow2 /your/desired/folder

Note that the downloaded image has already several snapshots inside.

# make sure you have already run setup.sh
qemu-img snapshot -l root.qcow2
# you will find many snapshots already included in the image
  1. To start from a clean image without any internal snapshots, run the commands below, which will create a new image, CLEAN from the BASE image with its content set to that of SNAPSHOT. Please set the parameters used in the cleanup script before running it.
cd scipts/qflex
# set parameters used in cleanup.sh, i.e., BASE, SNAPSHOT, CLEAN
./cleanup.sh

qemu-img snapshot -l ${CLEAN}
# no output
  1. The instructions for creating a checkpoint library are explained in detail here. Please find below a more elaborate sequence of instructions to build a checkpoint library.
  2. Here is the args file I used for my working example.
--core-count 1
--no-double-cores
--quantum-size-ns 2000
--llc-size-per-tile-mb 1
--parallel
--network user
--memory-gb 16
--host-name SAPHIRE_SMT
--workload-name test
--population-seconds 1
--no-consolidated
--primary-ipc 2.0
--primary-core-start 0
--phantom-cpu-ipc 4
--experiment-name test
--image-name clean.qcow2
--image-folder /where/clean.qcow2/is
--mounting-folder /where/emulation/files/written
--check-period-quantum-coeff 53.0
--no-unique --use-image-directly
  1. Depending on the snapshot you used to retrieve a clean image, you might find the web search benchmark from CloudSuite already included in the clean image. For simplifying the test process, I decided to use a simple microbenchmark, shown below. It creates an array and generates random accesses with a given skew toward a range of addresses.
// skew_access.c
// Allocate ~100 MiB of ints, then endlessly access random indices.
// 90% of accesses go to a 1 MiB "hot" region, with extra skew inside that region.

#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

#ifndef TOTAL_BYTES
#define TOTAL_BYTES (100ULL * 1024ULL * 1024ULL)   // 100 MiB
#endif

#ifndef HOT_BYTES
#define HOT_BYTES   (1ULL * 1024ULL * 1024ULL)     // 1 MiB hot region
#endif

#ifndef HOT_PROB_PERCENT
#define HOT_PROB_PERCENT 90                        // 90% hot, 10% cold
#endif

#ifndef HOT_ALPHA
#define HOT_ALPHA 2.5                              // >1 => more skew toward loww
 indices inside hot region
#endif

#ifndef PRINT_EVERY
#define PRINT_EVERY 1000000                              // print every access ((
WARNING: very slow / huge output)
#endif

// SplitMix64 RNG (fast, decent quality)
static inline uint64_t splitmix64(uint64_t *state) {
    uint64_t z = (*state += 0x9e3779b97f4a7c15ULL);
    z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
    z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
    return z ^ (z >> 31);
}

// Uniform double in [0,1)
static inline double rng_double01(uint64_t *state) {
    // Use top 53 bits to build a double
    return (splitmix64(state) >> 11) * (1.0 / 9007199254740992.0); // 2^53
}

int main(void) {
    const size_t total_count = (size_t)(TOTAL_BYTES / sizeof(int));
    const size_t hot_count   = (size_t)(HOT_BYTES   / sizeof(int));

    if (hot_count == 0 || total_count == 0 || hot_count >= total_count) {
        fprintf(stderr, "Invalid sizes: hot_count=%zu total_count=%zu\\n", hot_coo
unt, total_count);
        return 1;
    }

    // Allocate the array
    int *a = (int*)malloc(total_count * sizeof(int));
    if (!a) {
        perror("malloc");
        return 1;
    }
    
    // Initialize (optional, but touches pages so allocation is real)
    for (size_t i = 0; i < total_count; i++) a[i] = (int)i;

    // Seed RNG
    uint64_t rng = (uint64_t)time(NULL) ^ ((uint64_t)getpid() << 32);

    volatile int sink = 0; // prevents the compiler from optimizing away reads
    uint64_t iter = 0;

    while (1) {
        // Decide hot vs cold
        uint64_t r = splitmix64(&rng);
        int pick_hot = (int)(r % 100) < HOT_PROB_PERCENT;

        size_t idx;

        if (pick_hot) {
            // Skewed pick within [0, hot_count)
            // u^HOT_ALPHA biases toward 0 when HOT_ALPHA > 1
            double u = rng_double01(&rng);
            double biased = pow(u, HOT_ALPHA);
            idx = (size_t)(biased * (double)hot_count);
            if (idx >= hot_count) idx = hot_count - 1;
        } else {
            // Uniform pick within [hot_count, total_count)
            // (cold region excludes the hot region)
            size_t cold_span = total_count - hot_count;
            idx = hot_count + (size_t)(splitmix64(&rng) % cold_span);
        }

        sink ^= a[idx]; // actual access

        if ((iter % PRINT_EVERY) == 0) {
            printf("%zu\\n", idx);
            // Optional: fflush(stdout); // uncomment if you want immediate outpu
        }
        iter++;
    }

    // Unreachable
    // free(a);
    // return 0;
}

To compile the code, you first need to install gcc:

su
# just press Enter for password
apk update
apk add gcc
exit

Compile the code:

gcc -O2 -std=c++11 ubench.c -lm -o ubench

run the microbenchmark:

./ubench