Files
263-0007-00L-Advanced-Syste…/main.cpp
T

180 lines
5.1 KiB
C++

#include "board.hpp"
#include "constants.hpp"
#include "pedestrian.hpp"
#include "social_force.hpp"
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <vector>
#ifndef BENCHMARK
// Benchmarking is disabled by default.
#define BENCHMARK 0
#endif
#ifdef __x86_64__
#include "tsc_x86.h"
#endif
#ifdef __aarch64__
#include "kperf.h"
#endif
#define N_OBSTACLES 2
#define WARMUP_ITERS 5
#define NUM_REPS 11
int main(int argc, const char *argv[]) {
// Check that the correct number of arguments has been passed.
//
// Expected usage:
//
// ./main <number_of_pedestrians> <number_of_iterations> [csv_output_path]
//
// The optional third argument enables CSV dumping for visualization.
if (argc < 3) {
printf("Usage: ./main <number_of_pedestrians> <number_of_iterations> [csv_output_path]\n");
printf("Each iteration takes %.2f seconds in the simulation.\n", dt);
return 1;
}
// Parse command-line arguments.
int n_p = atoi(argv[1]);
int n_iter = atoi(argv[2]);
if (n_p <= 0) {
printf("Error: <number_of_pedestrians> must be a positive integer, got '%s' instead\n", argv[1]);
return 1;
}
if (n_iter <= 0) {
printf("Error: <number_of_iterations> must be a positive integer, got '%s' instead\n", argv[2]);
return 1;
}
// If provided, dump_path is used to write simulation states to CSV.
// In benchmark mode, this path is intentionally ignored to avoid measuring
// file I/O overhead.
const char *dump_path = (argc >= 4) ? argv[3] : nullptr;
#if BENCHMARK
#ifdef __aarch64__
// Initialize ARM performance counters.
kperf_init();
struct performance_counters startperf, endperf;
#endif
// Warmup runs are not measured.
// Each run gets a fresh initialized state because run_simulation mutates the
// pedestrians and board state.
for (int i = 0; i < WARMUP_ITERS; i++) {
Pedestrian *P = init_pedestrians(n_p);
Board board = init_board_without_obstacles();
run_simulation(P, board, n_p, n_iter, nullptr);
delete[] P;
}
// Store the cycle count of each measured repetition.
// The median is then used as the final timing result.
std::vector<long double> cycles_v;
cycles_v.reserve(NUM_REPS);
for (int i = 0; i < NUM_REPS; i++) {
Pedestrian *P = init_pedestrians(n_p);
Board board = init_board_without_obstacles();
#ifdef __x86_64__
myInt64 start = start_tsc();
#endif
#ifdef __aarch64__
startperf = kperf_get_counters();
#endif
// Disable CSV dumping during benchmarking to avoid measuring file I/O
// overhead. Initialization is also excluded from the measured region.
run_simulation(P, board, n_p, n_iter, nullptr);
#ifdef __x86_64__
long double cycles = stop_tsc(start);
#endif
#ifdef __aarch64__
endperf = kperf_get_counters();
long double cycles = endperf.cycles - startperf.cycles;
#endif
delete[] P;
cycles_v.push_back(cycles);
}
// Use the median cycle count to reduce the effect of noisy measurements.
std::sort(cycles_v.begin(), cycles_v.end());
long double T = cycles_v[NUM_REPS / 2];
#else
// Normal execution mode. If dump_path is provided, the simulation writes a CSV
// file that can later be used to generate a video of the simulation.
Pedestrian *P = init_pedestrians(n_p);
Board board = init_board_without_obstacles();
run_simulation(P, board, n_p, n_iter, dump_path);
delete[] P;
#endif
#if BENCHMARK
// Flop-count per simulation iteration.
//
// The total work W(n) is estimated from the number of additions,
// multiplications, divisions, square roots, and exponentials performed by the
// simulation kernels.
long long add_count_per_iter = n_p * (4 + (n_p - 1) * 14 + N_OBSTACLES * 12) + n_p * 7;
long long mult_count_per_iter = n_p * (4 + (n_p - 1) * 23 + N_OBSTACLES * 18) + n_p * 16;
long long div_count_per_iter = n_p * ((n_p - 1) * 1 + N_OBSTACLES * 1) + n_p * 2;
long long sqrt_count_per_iter = n_p * ((n_p - 1) * 4 + N_OBSTACLES * 2) + n_p * 3;
long long exp_count_per_iter = n_p * ((n_p - 1) * 1 + N_OBSTACLES * 1);
long double W_per_iter = add_count_per_iter * cost_add + mult_count_per_iter * cost_mult + div_count_per_iter * cost_div + sqrt_count_per_iter * cost_sqrt + exp_count_per_iter * cost_exp;
// Estimated memory traffic per iteration.
// TODO: refine this model to account for actual memory accesses, cache reuse,
// and the full interaction loop.
long long Q_per_iter = sizeof(Pedestrian) * n_p + sizeof(Obstacle) * N_OBSTACLES;
// Operational intensity:
//
// I(n) = W(n) / Q(n)
//
// where W is estimated work in flops and Q is estimated memory traffic in
// bytes.
long double I = W_per_iter / Q_per_iter;
// Total estimated work over all iterations.
long double W = W_per_iter * n_iter;
// Performance in estimated flops per cycle.
long double P = W / T;
printf("W(n) per iteration: %.2Lf flops\n", W_per_iter);
printf("Q(n) per iteration: %lld bytes\n", Q_per_iter);
printf("I(n): %.2Lf flops/byte\n", I);
printf("W(n): %.2Lf flops\n", W);
printf("T(n): %.2Lf cycles\n", T);
printf("P(n): %.2Lf flops/cycle\n", P);
#endif
return 0;
}