I/O Library

The I/O library (schedsim::io) handles JSON loading, trace output, scenario generation, and post-simulation metrics computation.

Platform Loading

void schedsim::io::load_platform(core::Engine &engine, const std::filesystem::path &path)

Load a platform definition from a JSON file.

Reads the JSON file at path, auto-detects the format, and populates the engine’s platform with processor types, clock domains, power domains, and processor instances.

Note

Does not call Platform::finalize() — the caller must do that after optionally adding tasks or further configuration.

Parameters:
  • engine – The simulation engine whose platform will be populated.

  • path – Filesystem path to the JSON platform description.

Throws:

LoaderError – If the file cannot be read or contains invalid JSON.

void schedsim::io::load_platform_from_string(core::Engine &engine, std::string_view json)

Load a platform definition from a JSON string.

Parses json directly and populates the engine’s platform. Behaves identically to load_platform except the input comes from a string rather than a file.

Parameters:
  • engine – The simulation engine whose platform will be populated.

  • json – JSON content describing the platform.

Throws:

LoaderError – If the JSON is malformed or fails validation.

Scenario Loading

Data Structures

struct JobParams

Parameters for a single job instance within a task.

Public Members

core::TimePoint arrival = {}

Absolute arrival time of the job.

core::Duration duration = {}

Actual execution demand (at reference speed).

struct TaskParams

Parameters describing a periodic real-time task.

A task produces a series of jobs according to its period. The relative_deadline defaults to period when not specified in JSON.

Public Members

uint64_t id

Unique task identifier (1-based in JSON).

core::Duration period

Inter-arrival period.

core::Duration relative_deadline

Relative deadline (defaults to period if unspecified).

core::Duration wcet

Worst-case execution time.

std::vector<JobParams> jobs

Concrete job instances to simulate.

struct ScenarioData

Complete scenario definition: a collection of tasks and their jobs.

Loaded from JSON via load_scenario or built programmatically with the generation utilities in scenario_generation.hpp.

Public Members

std::vector<TaskParams> tasks

All tasks in the scenario.

Loading Functions

ScenarioData schedsim::io::load_scenario(const std::filesystem::path &path)

Load a scenario from a JSON file.

Auto-detects the JSON format and returns a ScenarioData with all tasks and their pre-computed job arrivals.

Parameters:

path – Filesystem path to the JSON scenario file.

Throws:

LoaderError – If the file cannot be read or contains invalid JSON.

Returns:

Parsed scenario data.

ScenarioData schedsim::io::load_scenario_from_string(std::string_view json)

Load a scenario from a JSON string.

Parses json directly and returns the resulting ScenarioData.

See also

load_scenario

Parameters:

json – JSON content describing the scenario.

Throws:

LoaderError – If the JSON is malformed or fails validation.

Returns:

Parsed scenario data.

Writing Functions

void schedsim::io::write_scenario(const ScenarioData &scenario, const std::filesystem::path &path)

Write a scenario to a JSON file.

Serialises scenario to the canonical JSON format and writes it to the file at path.

Parameters:
  • scenario – The scenario data to serialise.

  • path – Destination file path.

void schedsim::io::write_scenario_to_stream(const ScenarioData &scenario, std::ostream &out)

Write a scenario to an output stream.

Serialises scenario as JSON and writes it to out.

See also

write_scenario

Parameters:
  • scenario – The scenario data to serialise.

  • out – Output stream (file, stringstream, stdout, etc.).

Scenario Injection

std::vector<core::Task*> schedsim::io::inject_scenario(core::Engine &engine, const ScenarioData &scenario)

Add tasks to the platform and schedule their job arrivals.

For each task in scenario, creates a corresponding core::Task in the engine’s platform and registers timed events for every job arrival.

Note

Must be called before Platform::finalize().

Parameters:
  • engine – The simulation engine to populate.

  • scenario – Scenario data containing tasks and job arrival lists.

Throws:

LoaderError – If a task ID conflict or invalid parameter is detected.

Returns:

Pointers to the created core::Task objects, in the same order as scenario.tasks.

void schedsim::io::schedule_arrivals(core::Engine &engine, core::Task &task, const std::vector<JobParams> &jobs)

Schedule job arrivals for a single existing task.

Registers timed arrival events for every job in jobs on the given task. Useful when tasks are created manually rather than through inject_scenario.

Note

Must be called after Platform::finalize().

Parameters:
  • engine – The simulation engine that owns the timer queue.

  • task – The task to receive job arrivals.

  • jobs – Job arrival parameters (times and durations).

Scenario Generation

ScenarioData schedsim::io::generate_scenario(std::size_t num_tasks, double target_utilization, PeriodDistribution period_dist, core::Duration simulation_duration, std::mt19937 &rng, double exec_ratio = 1.0)

Generate a complete scenario (tasks + job arrivals) in one call.

Convenience wrapper that calls generate_task_set followed by generate_arrivals and returns the result as a ScenarioData.

Parameters:
  • num_tasks – Number of tasks to generate.

  • target_utilization – Desired total utilization.

  • period_dist – Period sampling configuration.

  • simulation_duration – Duration for job arrival generation.

  • rng – Mersenne Twister PRNG instance.

  • exec_ratio – Ratio of actual execution time to WCET.

Returns:

A ready-to-use scenario.

std::vector<TaskParams> schedsim::io::generate_task_set(std::size_t num_tasks, double target_utilization, PeriodDistribution period_dist, std::mt19937 &rng)

Generate task parameters with a target total utilization.

Splits target_utilization across num_tasks tasks using UUniFast, samples periods from period_dist, and derives WCETs accordingly. Does not generate job arrivals; call generate_arrivals afterwards.

Parameters:
  • num_tasks – Number of tasks to generate.

  • target_utilization – Desired total utilization.

  • period_dist – Period sampling configuration.

  • rng – Mersenne Twister PRNG instance.

Returns:

Vector of generated task parameters (no jobs yet).

void schedsim::io::generate_arrivals(std::vector<TaskParams> &tasks, core::Duration simulation_duration, std::mt19937 &rng, double exec_ratio = 1.0)

Generate job arrivals for every task in tasks.

Fills each task’s jobs vector with periodic arrivals spanning simulation_duration. The actual execution demand of each job is exec_ratio times the task’s WCET.

Parameters:
  • tasks – Tasks whose job lists will be populated (modified in place).

  • simulation_duration – Total duration over which to generate arrivals.

  • rng – Mersenne Twister PRNG instance.

  • exec_ratio – Ratio of actual execution time to WCET; 1.0 means worst-case execution on every job.

std::vector<double> schedsim::io::uunifast(std::size_t num_tasks, double target_utilization, std::mt19937 &rng)

UUniFast algorithm: generate num_tasks utilizations summing to target_utilization.

Implements the unbiased random utilization-splitting algorithm by Bini and Buttazzo.

See also

uunifast_discard

Parameters:
  • num_tasks – Number of utilizations to generate.

  • target_utilization – Desired sum of all utilizations.

  • rng – Mersenne Twister PRNG instance.

Returns:

Vector of per-task utilizations (size == num_tasks).

std::vector<double> schedsim::io::uunifast_discard(std::size_t num_tasks, double target_utilization, double umin, double umax, std::mt19937 &rng)

UUniFast-Discard: utilization splitting with per-task bounds.

Repeatedly invokes UUniFast and discards sets where any utilization falls outside [umin, umax]. Guarantees bounded individual utilizations at the cost of additional retries.

See also

uunifast

Parameters:
  • num_tasks – Number of utilizations to generate.

  • target_utilization – Desired sum of all utilizations.

  • umin – Minimum per-task utilization (inclusive).

  • umax – Maximum per-task utilization (inclusive).

  • rng – Mersenne Twister PRNG instance.

Throws:

std::runtime_error – After 2 billion failed attempts if the bounds are infeasible.

Returns:

Vector of per-task utilizations within the requested bounds.

std::vector<JobParams> schedsim::io::generate_weibull_jobs(core::Duration period, core::Duration wcet, std::size_t hyperperiod_jobs, const WeibullJobConfig &config, std::mt19937 &rng)

Generate jobs with Weibull-distributed execution durations.

Produces hyperperiod_jobs jobs spaced one period apart. Durations are drawn from a truncated Weibull distribution whose lower bound is compression_rate * wcet. The success_rate quantile is mapped to wcet, so lower success rates permit execution-time overruns. When success_rate == 1, every duration is at most wcet.

Parameters:
  • period – Task period (inter-arrival spacing).

  • wcet – Worst-case execution time (upper clamp).

  • hyperperiod_jobs – Number of jobs to generate.

  • config – Weibull distribution parameters.

  • rng – Mersenne Twister PRNG instance.

Throws:

std::invalid_argument – If success_rate is outside (0, 1] or compression_rate is outside [0, 1].

Returns:

Vector of generated job parameters.

core::Duration schedsim::io::pick_harmonic_period(std::mt19937 &rng)

Pick a random period from the fixed candidate set.

Uniformly selects one of the entries in HARMONIC_PERIODS_US and returns it as a core::Duration.

See also

HARMONIC_PERIODS_US, HYPERPERIOD_US

Parameters:

rng – Mersenne Twister PRNG instance.

Returns:

A duration corresponding to one of the fixed period candidates.

ScenarioData schedsim::io::generate_uunifast_discard_weibull(std::size_t num_tasks, double target_utilization, double umin, double umax, const WeibullJobConfig &config, std::mt19937 &rng)

Full UUniFast-Discard + Weibull scenario generation.

Combines uunifast_discard for utilization splitting, fixed period selection, and generate_weibull_jobs for job generation into a single call. Task IDs start at 1 (legacy convention).

Parameters:
  • num_tasks – Number of tasks.

  • target_utilization – Desired total utilization.

  • umin – Minimum per-task utilization.

  • umax – Maximum per-task utilization.

  • config – Weibull job configuration.

  • rng – Mersenne Twister PRNG instance.

Throws:

std::runtime_error – If UUniFast-Discard cannot find a valid split.

Returns:

A complete scenario ready for injection.

ScenarioData schedsim::io::merge_scenarios(const ScenarioData &a, const ScenarioData &b)

Merge two scenario task sets into one.

Concatenates the task lists from a and b and reassigns sequential IDs starting at 1.

See also

ScenarioData

Parameters:
  • a – First scenario.

  • b – Second scenario.

Returns:

Merged scenario with renumbered task IDs.

std::vector<TaskParams> schedsim::io::from_utilizations(const std::vector<double> &utilizations, const WeibullJobConfig &config, std::mt19937 &rng, std::size_t num_hyperperiods = 1, std::size_t min_jobs = 0)

Build task parameters from an explicit utilization vector.

Creates one task per entry in utilizations, assigns fixed-grid periods, and generates Weibull-distributed jobs. Useful when utilizations are obtained from an external source rather than UUniFast.

Parameters:
  • utilizations – Per-task utilizations.

  • config – Weibull job configuration.

  • rng – Mersenne Twister PRNG instance.

  • num_hyperperiods – Number of nominal generation windows to simulate (legacy name; default 1).

  • min_jobs – Minimum total jobs across all tasks. Period assignment is retried until this threshold is met (default 0 = no minimum).

Returns:

Vector of fully populated task parameters.

struct PeriodDistribution

Configuration for the period distribution used by generate_task_set.

Public Members

core::Duration min = {}

Minimum period.

core::Duration max = {}

Maximum period.

bool log_uniform = {true}

If true, sample periods log-uniformly (common in RT literature).

struct WeibullJobConfig

Configuration for Weibull-distributed job execution times.

Controls the shape of the execution-time distribution so that generated workloads exhibit realistic variability rather than constant WCET.

Public Members

double success_rate = {1.0}

Fraction of jobs within WCET (0, 1].

double compression_rate = {1.0}

Minimum duration as a fraction of WCET [0, 1].

Trace Writers

struct TraceRecord

A single trace record stored in memory.

Each record captures the simulation time, event type, and an arbitrary set of named fields whose values may be double, uint64_t, or std::string.

Public Members

double time

Simulation time of the event (seconds).

std::string type

Event type identifier (e.g. “job_completed”).

std::unordered_map<std::string, std::variant<double, uint64_t, std::string>> fields

Named fields attached to the event.

Keys are field names; values are one of double, uint64_t, or std::string.

class NullTraceWriter : public schedsim::core::TraceWriter

Trace writer that silently discards all events.

Useful when trace output is not needed and maximum simulation performance is desired (zero overhead per event).

Public Functions

virtual void begin(core::TimePoint time) override

Begin a new trace record at the given simulation time.

Parameters:

time – Current simulation time.

virtual void type(std::string_view name) override

Set the event type name (ignored).

Parameters:

name – Event type identifier.

virtual void field(std::string_view key, double value) override

Record a floating-point field (ignored).

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, uint64_t value) override

Record an unsigned integer field (ignored).

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, std::string_view value) override

Record a string field (ignored).

Parameters:
  • key – Field name.

  • value – Field value.

virtual void end() override

Finalise the current record (no-op).

class JsonTraceWriter : public schedsim::core::TraceWriter

Trace writer that streams JSON array elements to an output stream.

Writes one JSON object per trace event directly to the provided stream (file or stdout). Call finalize to emit the closing bracket once the simulation is complete.

Non-copyable and non-movable because it holds a reference to the output stream.

Public Functions

explicit JsonTraceWriter(std::ostream &output)

Construct a JSON writer targeting output.

Parameters:

output – Destination stream (must outlive this writer).

~JsonTraceWriter() override

Destructor; calls finalize if not already called.

JsonTraceWriter(const JsonTraceWriter&) = delete
JsonTraceWriter &operator=(const JsonTraceWriter&) = delete
JsonTraceWriter(JsonTraceWriter&&) = delete
JsonTraceWriter &operator=(JsonTraceWriter&&) = delete
virtual void begin(core::TimePoint time) override

Begin a new JSON trace record.

Parameters:

time – Current simulation time.

virtual void type(std::string_view name) override

Set the event type field in the current JSON object.

Parameters:

name – Event type identifier.

virtual void field(std::string_view key, double value) override

Add a floating-point field to the current JSON object.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, uint64_t value) override

Add an unsigned integer field to the current JSON object.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, std::string_view value) override

Add a string field to the current JSON object.

Parameters:
  • key – Field name.

  • value – Field value (will be JSON-escaped).

virtual void end() override

Close the current JSON object.

void finalize()

Write the closing bracket of the JSON array.

Must be called exactly once after all events have been written. The destructor calls this automatically if it has not been invoked.

class MemoryTraceWriter : public schedsim::core::TraceWriter

Trace writer that buffers all events in memory as TraceRecord objects.

Ideal for unit tests and post-simulation analysis where the full trace must be inspected programmatically.

Public Functions

virtual void begin(core::TimePoint time) override

Begin a new in-memory trace record.

Parameters:

time – Current simulation time.

virtual void type(std::string_view name) override

Set the event type on the current record.

Parameters:

name – Event type identifier.

virtual void field(std::string_view key, double value) override

Attach a floating-point field to the current record.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, uint64_t value) override

Attach an unsigned integer field to the current record.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, std::string_view value) override

Attach a string field to the current record.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void end() override

Finalise the current record and append it to the buffer.

inline const std::vector<TraceRecord> &records() const

Access the accumulated trace records.

Returns:

Const reference to the internal record vector.

inline void clear()

Discard all buffered records.

class TextualTraceWriter : public schedsim::core::TraceWriter

Human-readable textual trace writer with optional ANSI colour.

Formats each event as a single line with aligned columns, matching the legacy simulator’s output style. Colour can be disabled for piping to files or non-terminal sinks.

Non-copyable and non-movable because it holds a reference to the output stream.

Public Functions

explicit TextualTraceWriter(std::ostream &output, bool color_enabled = true)

Construct a textual writer targeting output.

Parameters:
  • output – Destination stream (must outlive this writer).

  • color_enabled – If true, emit ANSI escape codes for colour.

TextualTraceWriter(const TextualTraceWriter&) = delete
TextualTraceWriter &operator=(const TextualTraceWriter&) = delete
TextualTraceWriter(TextualTraceWriter&&) = delete
TextualTraceWriter &operator=(TextualTraceWriter&&) = delete
virtual void begin(core::TimePoint time) override

Begin a new textual trace line.

Parameters:

time – Current simulation time.

virtual void type(std::string_view name) override

Set the event type for the current line.

Parameters:

name – Event type identifier.

virtual void field(std::string_view key, double value) override

Buffer a floating-point field for the current line.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, uint64_t value) override

Buffer an unsigned integer field for the current line.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void field(std::string_view key, std::string_view value) override

Buffer a string field for the current line.

Parameters:
  • key – Field name.

  • value – Field value.

virtual void end() override

Flush the buffered fields and write the formatted line.

Metrics

struct SimulationMetrics

Aggregated metrics computed from a simulation trace.

Covers scheduling quality (deadline misses, preemptions), energy consumption, per-processor utilization, per-task response/waiting times, frequency changes, and migration/transition counts.

Public Members

uint64_t total_jobs = {0}

Total number of job arrivals.

uint64_t completed_jobs = {0}

Jobs that finished before their deadline.

uint64_t deadline_misses = {0}

Jobs that missed their absolute deadline.

uint64_t preemptions = {0}

Number of preemption events.

uint64_t context_switches = {0}

Number of context-switch events.

double total_energy_mj = {0.0}

Total energy consumed (millijoules).

std::unordered_map<uint64_t, double> energy_per_processor

Per-processor energy consumption (processor ID -> millijoules).

double average_utilization = {0.0}

System-wide average utilization [0, 1].

std::unordered_map<uint64_t, double> utilization_per_processor

Per-processor average utilization (processor ID -> utilization).

std::unordered_map<uint64_t, std::vector<double>> response_times_per_task

Per-task response times in seconds (task ID -> list of values).

std::unordered_map<uint64_t, uint64_t> deadline_misses_per_task

Per-task deadline miss counts (task ID -> count).

uint64_t rejected_arrivals = {0}

Number of job arrivals rejected by admission control.

std::unordered_map<uint64_t, std::vector<double>> waiting_times_per_task

Per-task waiting times in seconds (task ID -> list of arrival-to-start delays).

std::vector<FrequencyChange> frequency_changes

Chronological log of all frequency-change events.

uint64_t cluster_migrations = {0}

Number of inter-cluster migration events.

uint64_t transitions = {0}

Number of task-placement events.

uint64_t core_state_requests = {0}

Processor activation/sleep transitions.

uint64_t frequency_requests = {0}

Distinct frequency-change timestamps.

struct FrequencyChange

Single frequency-change event recorded during simulation.

Public Members

double time

Simulation time of the change (seconds).

uint64_t cluster_id

Clock-domain / cluster identifier.

double frequency

New frequency after the change (Hz).

struct ResponseTimeStats

Summary statistics for a collection of response times.

Public Members

double min = {0.0}

Minimum response time.

double max = {0.0}

Maximum response time.

double mean = {0.0}

Arithmetic mean.

double median = {0.0}

Median (50th percentile).

double stddev = {0.0}

Standard deviation.

double percentile_95 = {0.0}

95th percentile.

double percentile_99 = {0.0}

99th percentile.

struct FrequencyInterval

A time interval during which a clock domain held a constant frequency.

Public Members

double start

Start time of the interval (seconds).

double stop

End time of the interval (seconds).

double frequency

Frequency during this interval (Hz).

uint64_t cluster_id

Clock-domain / cluster identifier.

struct CoreCountInterval

A time interval during which a cluster had a constant active-core count.

Public Members

double start

Start time of the interval (seconds).

double stop

End time of the interval (seconds).

uint64_t active_cores

Number of active cores during this interval.

uint64_t cluster_id

Clock-domain / cluster identifier.

struct ConfigInterval

A time interval during which the global configuration (frequency + active cores) remained constant.

Public Members

double start

Start time of the interval (seconds).

double stop

End time of the interval (seconds).

double frequency

Frequency during this interval (Hz).

uint64_t active_cores

Number of active cores during this interval.

SimulationMetrics schedsim::io::compute_metrics(const std::vector<TraceRecord> &traces)

Compute aggregated metrics from in-memory trace records.

Iterates over traces and accumulates scheduling, energy, utilization, response-time, and migration statistics.

Parameters:

traces – Vector of trace records (typically from MemoryTraceWriter).

Returns:

Populated SimulationMetrics.

SimulationMetrics schedsim::io::compute_metrics_from_file(const std::filesystem::path &path)

Compute aggregated metrics from a JSON trace file on disk.

Opens the file at path, parses its trace records, and returns the same metrics as compute_metrics.

Parameters:

path – Filesystem path to a JSON trace file.

Throws:

LoaderError – If the file cannot be read or parsed.

Returns:

Populated SimulationMetrics.

ResponseTimeStats schedsim::io::compute_response_time_stats(const std::vector<double> &response_times)

Compute summary statistics from a vector of response times.

Parameters:

response_times – Response times in seconds (must not be empty for meaningful results).

Returns:

A ResponseTimeStats populated with min/max/mean/median/stddev and tail percentiles.

std::vector<FrequencyInterval> schedsim::io::track_frequency_changes(const std::vector<TraceRecord> &traces)

Extract per-cluster frequency intervals from a trace.

Scans traces for frequency-change events and returns a sorted vector of intervals, one per cluster per constant-frequency span.

Parameters:

traces – In-memory trace records.

Returns:

Sorted vector of FrequencyInterval entries.

std::vector<CoreCountInterval> schedsim::io::track_core_changes(const std::vector<TraceRecord> &traces)

Extract per-cluster active-core-count intervals from a trace.

Scans traces for core activation/sleep events and returns a sorted vector of intervals.

Parameters:

traces – In-memory trace records.

Returns:

Sorted vector of CoreCountInterval entries.

std::vector<ConfigInterval> schedsim::io::track_config_changes(const std::vector<TraceRecord> &traces)

Extract combined configuration intervals from a trace.

Merges frequency and core-count changes into intervals where neither changed, giving a unified view of the system configuration over time.

Parameters:

traces – In-memory trace records.

Returns:

Sorted vector of ConfigInterval entries.

Errors

class LoaderError : public std::runtime_error

Exception for I/O errors (loading, parsing, validation).

Thrown by loader functions when JSON input is malformed, required fields are missing, or values fail semantic validation (e.g. negative periods).

Public Functions

inline LoaderError(const std::string &message, const std::string &context)

Construct a LoaderError with a contextual prefix.

The resulting message is formatted as "context: message".

Parameters:
  • message – Human-readable description of the error.

  • context – Additional context such as the file path or field name.