fix for the two main files
This commit is contained in:
@@ -63,11 +63,21 @@ endif
|
||||
|
||||
VARIANT_SRC := ./variants/variant_$(VARIANT).cpp
|
||||
|
||||
# symmetry variants do half the pairwise work, so they use their own main with the
|
||||
# halved flop-count formula. float_ symmetry variants use the float symmetry main.
|
||||
ifneq ($(filter float_%,$(VARIANT)),)
|
||||
ifneq ($(findstring symmetry,$(VARIANT)),)
|
||||
MAIN_SRC := main-float_symmetry.cpp
|
||||
else
|
||||
MAIN_SRC := main-float.cpp
|
||||
endif
|
||||
else
|
||||
ifneq ($(findstring symmetry,$(VARIANT)),)
|
||||
MAIN_SRC := main-symmetry.cpp
|
||||
else
|
||||
MAIN_SRC := main.cpp
|
||||
endif
|
||||
endif
|
||||
|
||||
SRC := $(MAIN_SRC) $(VARIANT_SRC)
|
||||
HEADERS := ./headers/board.hpp ./headers/pedestrian.hpp ./headers/vector2d.hpp ./headers/constants.hpp ./headers/util.hpp
|
||||
@@ -186,7 +196,7 @@ test: clean
|
||||
|
||||
# make asm : compile and generate assembly files.
|
||||
|
||||
ASM_CPP_SRCS := main.cpp main-float.cpp $(VARIANT_SRC)
|
||||
ASM_CPP_SRCS := main.cpp main-float.cpp main-symmetry.cpp main-float_symmetry.cpp $(VARIANT_SRC)
|
||||
ASM_HPP_SRCS := ./headers/util.hpp
|
||||
ASM_SRCS := $(ASM_CPP_SRCS) $(ASM_HPP_SRCS)
|
||||
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
#include "./headers/board_float.hpp"
|
||||
#include "./headers/constants_float.hpp"
|
||||
#include "./headers/pedestrian_float.hpp"
|
||||
#include "./headers/social_force_float.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
#ifndef BENCHMARK
|
||||
#define BENCHMARK 0
|
||||
#endif
|
||||
|
||||
#ifndef ROOFLINE
|
||||
#define ROOFLINE 0
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "./headers/tsc_x86.h"
|
||||
#include <immintrin.h>
|
||||
#define TRASH_BUFFER_SIZE (16ULL * 1024 * 1024 * 2) // 2x L3 (~16MB) on the x86 machine we use for measurements
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
#include "./headers/kperf.h"
|
||||
#include <arm_acle.h>
|
||||
#define TRASH_BUFFER_SIZE (1502838784ULL * 2) // 2x SLC (~1.5GB) on M3 Pro
|
||||
#define PAGE_SIZE 16384
|
||||
#endif
|
||||
|
||||
#define N_OBSTACLES 2
|
||||
|
||||
// Benchmark parameters (mostly copied from mvm.c in hw1)
|
||||
#define WARMUP_ITERS 3
|
||||
#define NUM_REPS 15
|
||||
#define NUM_RUNS 1
|
||||
#define CYCLES_REQUIRED 1e8
|
||||
#define MAX_NUM_RUNS (1 << 14)
|
||||
|
||||
// Flushes caches by streaming reads through a large persistent buffer.
|
||||
long long flush_cache() {
|
||||
static volatile char *trash_buffer = nullptr;
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
trash_buffer = new volatile char[TRASH_BUFFER_SIZE];
|
||||
|
||||
// Touch one byte per page once so pages are physically mapped. And we avoid paying the cost of page faults during cache trashing below.
|
||||
for (size_t i = 0; i < TRASH_BUFFER_SIZE; i += PAGE_SIZE) {
|
||||
trash_buffer[i] = 1;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
long long acc = 0;
|
||||
|
||||
for (size_t i = 0; i < TRASH_BUFFER_SIZE; i += CACHE_BLOCK_SIZE) {
|
||||
// Access one byte per cache block is enough to populate the entire block
|
||||
acc += trash_buffer[i];
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
_mm_lfence();
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
__isb(0xF);
|
||||
#endif
|
||||
|
||||
asm volatile("" ::: "memory");
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
#ifdef __x86_64__
|
||||
// flush denormals to zero via MXCSR. one-time, per thread.
|
||||
// any subnormal in the j-loop chain stalls fma for 100+ cycles, this kills that.
|
||||
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
|
||||
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
|
||||
#endif
|
||||
|
||||
// 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'\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
if (n_iter <= 0) {
|
||||
printf("Error: <number_of_iterations> must be a positive integer, got '%s'\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
|
||||
|
||||
#if ROOFLINE
|
||||
volatile long long trash_value;
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
// Initialize ARM performance counters.
|
||||
kperf_init();
|
||||
struct performance_counters startperf, endperf;
|
||||
#endif
|
||||
|
||||
// Warmup runs, to bring caches, predictors, frequency to steady state. These 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;
|
||||
}
|
||||
|
||||
// Calibration: finds num_runs such that one batch runs for at least
|
||||
// CYCLES_REQUIRED cycles. A long batch keeps makes the timer and other
|
||||
// noise overhead, negligible relative to the kernel execution cost we're
|
||||
// actually trying to measure.
|
||||
//
|
||||
// Skipped when ROOFLINE is enabled: cold-cache measurements need
|
||||
// exactly one timed simulation per batch, otherwise only the first
|
||||
// call starts cold and subsequent calls measure warm-cache behavior.
|
||||
// We can use large enough inputs so a single call already exceeds CYCLES_REQUIRED,
|
||||
// when measuring for roofline plot.
|
||||
// Note: we implemented the calibration exactly as it is done in mvm.c from hw1, we also
|
||||
// used the same values for NUM_RUNS, etc
|
||||
int num_runs = NUM_RUNS;
|
||||
#if !ROOFLINE
|
||||
while (num_runs < MAX_NUM_RUNS) {
|
||||
Pedestrian *P = init_pedestrians(n_p);
|
||||
Board board = init_board_without_obstacles();
|
||||
|
||||
float cycles;
|
||||
|
||||
#ifdef __x86_64__
|
||||
myInt64 start = start_tsc();
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
startperf = kperf_get_counters();
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < num_runs; j++) {
|
||||
run_simulation(P, board, n_p, n_iter, nullptr);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
cycles = (float)stop_tsc(start);
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
endperf = kperf_get_counters();
|
||||
cycles = (float)(endperf.cycles - startperf.cycles);
|
||||
#endif
|
||||
|
||||
delete[] P;
|
||||
|
||||
if (cycles >= CYCLES_REQUIRED)
|
||||
break;
|
||||
num_runs *= 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Store the cycle count of each measured repetition.
|
||||
// The median is then used as the final timing result.
|
||||
std::vector<float> cycles_v;
|
||||
cycles_v.reserve(NUM_REPS);
|
||||
|
||||
// Measurement phase
|
||||
for (int i = 0; i < NUM_REPS; i++) {
|
||||
Pedestrian *P = init_pedestrians(n_p);
|
||||
Board board = init_board_without_obstacles();
|
||||
|
||||
#if ROOFLINE
|
||||
trash_value = flush_cache();
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
myInt64 start = start_tsc();
|
||||
#endif
|
||||
#ifdef __aarch64__
|
||||
startperf = kperf_get_counters();
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < num_runs; j++) {
|
||||
// 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__
|
||||
float cycles = (float)stop_tsc(start) / num_runs;
|
||||
#endif
|
||||
#ifdef __aarch64__
|
||||
endperf = kperf_get_counters();
|
||||
float cycles = (float)(endperf.cycles - startperf.cycles) / num_runs;
|
||||
#endif
|
||||
|
||||
delete[] P;
|
||||
cycles_v.push_back(cycles);
|
||||
}
|
||||
|
||||
std::sort(cycles_v.begin(), cycles_v.end());
|
||||
// Use the median cycle count to reduce the effect of noisy measurements.
|
||||
float 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.
|
||||
|
||||
// symmetry: each pedestrian-pedestrian pair is computed once instead of twice,
|
||||
// so the pairwise (n_p - 1) terms get halved -> n_p * (n_p - 1) / 2 pairs total.
|
||||
// the self-driving force, obstacle force and update pass are untouched.
|
||||
long long pairs = (long long)n_p * (n_p - 1) / 2;
|
||||
|
||||
long long add_count_per_iter = n_p * (2 + N_OBSTACLES * 10 + 2) + n_p * 7 + pairs * 14;
|
||||
long long mult_count_per_iter = n_p * (4 + N_OBSTACLES * 13) + n_p * 16 + pairs * 24;
|
||||
long long div_count_per_iter = n_p * (N_OBSTACLES * 1) + n_p * 2 + pairs * 1;
|
||||
long long sqrt_count_per_iter = n_p * (N_OBSTACLES * 1) + n_p * 3 + pairs * 3;
|
||||
long long exp_count_per_iter = n_p * (N_OBSTACLES * 1) + pairs * 1;
|
||||
|
||||
float 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.
|
||||
|
||||
float I = W_per_iter / Q_per_iter;
|
||||
float W = W_per_iter * n_iter;
|
||||
|
||||
// Performance in estimated flops per cycle.
|
||||
float P = W / T;
|
||||
|
||||
printf("W(n) per iteration: %.2f flops\n", W_per_iter);
|
||||
printf("Q(n) per iteration: %lld bytes\n", Q_per_iter);
|
||||
printf("I(n): %.2f flops/byte\n", I);
|
||||
printf("W(n): %.2f flops\n", W);
|
||||
printf("T(n): %.2f cycles\n", T);
|
||||
printf("P(n): %.2f flops/cycle\n", P);
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
#include "./headers/board.hpp"
|
||||
#include "./headers/constants.hpp"
|
||||
#include "./headers/pedestrian.hpp"
|
||||
#include "./headers/social_force.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
#ifndef BENCHMARK
|
||||
#define BENCHMARK 0
|
||||
#endif
|
||||
|
||||
#ifndef ROOFLINE
|
||||
#define ROOFLINE 0
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
#include "./headers/tsc_x86.h"
|
||||
#include <immintrin.h>
|
||||
#define TRASH_BUFFER_SIZE (16ULL * 1024 * 1024 * 2) // 2x L3 (~16MB) on the x86 machine we use for measurements
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
#include "./headers/kperf.h"
|
||||
#include <arm_acle.h>
|
||||
#define TRASH_BUFFER_SIZE (1502838784ULL * 2) // 2x SLC (~1.5GB) on M3 Pro
|
||||
#define PAGE_SIZE 16384
|
||||
#endif
|
||||
|
||||
#define N_OBSTACLES 2
|
||||
|
||||
// Benchmark parameters (mostly copied from mvm.c in hw1)
|
||||
#define WARMUP_ITERS 3
|
||||
#define NUM_REPS 15
|
||||
#define NUM_RUNS 1
|
||||
#define CYCLES_REQUIRED 1e8
|
||||
#define MAX_NUM_RUNS (1 << 14)
|
||||
|
||||
// Flushes caches by streaming reads through a large persistent buffer.
|
||||
long long flush_cache() {
|
||||
static volatile char *trash_buffer = nullptr;
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
trash_buffer = new volatile char[TRASH_BUFFER_SIZE];
|
||||
|
||||
// Touch one byte per page once so pages are physically mapped. And we avoid paying the cost of page faults during cache trashing below.
|
||||
for (size_t i = 0; i < TRASH_BUFFER_SIZE; i += PAGE_SIZE) {
|
||||
trash_buffer[i] = 1;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
long long acc = 0;
|
||||
|
||||
for (size_t i = 0; i < TRASH_BUFFER_SIZE; i += CACHE_BLOCK_SIZE) {
|
||||
// Access one byte per cache block is enough to populate the entire block
|
||||
acc += trash_buffer[i];
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
_mm_lfence();
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
__isb(0xF);
|
||||
#endif
|
||||
|
||||
asm volatile("" ::: "memory");
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
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'\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
if (n_iter <= 0) {
|
||||
printf("Error: <number_of_iterations> must be a positive integer, got '%s'\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
|
||||
|
||||
#if ROOFLINE
|
||||
volatile long long trash_value;
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
// Initialize ARM performance counters.
|
||||
kperf_init();
|
||||
struct performance_counters startperf, endperf;
|
||||
#endif
|
||||
|
||||
// Warmup runs, to bring caches, predictors, frequency to steady state. These 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;
|
||||
}
|
||||
|
||||
// Calibration: finds num_runs such that one batch runs for at least
|
||||
// CYCLES_REQUIRED cycles. A long batch keeps makes the timer and other
|
||||
// noise overhead, negligible relative to the kernel execution cost we're
|
||||
// actually trying to measure.
|
||||
//
|
||||
// Skipped when ROOFLINE is enabled: cold-cache measurements need
|
||||
// exactly one timed simulation per batch, otherwise only the first
|
||||
// call starts cold and subsequent calls measure warm-cache behavior.
|
||||
// We can use large enough inputs so a single call already exceeds CYCLES_REQUIRED,
|
||||
// when measuring for roofline plot.
|
||||
// Note: we implemented the calibration exactly as it is done in mvm.c from hw1, we also
|
||||
// used the same values for NUM_RUNS, etc
|
||||
int num_runs = NUM_RUNS;
|
||||
#if !ROOFLINE
|
||||
while (num_runs < MAX_NUM_RUNS) {
|
||||
Pedestrian *P = init_pedestrians(n_p);
|
||||
Board board = init_board_without_obstacles();
|
||||
|
||||
long double cycles;
|
||||
|
||||
#ifdef __x86_64__
|
||||
myInt64 start = start_tsc();
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
startperf = kperf_get_counters();
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < num_runs; j++) {
|
||||
run_simulation(P, board, n_p, n_iter, nullptr);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
cycles = (long double)stop_tsc(start);
|
||||
#endif
|
||||
|
||||
#ifdef __aarch64__
|
||||
endperf = kperf_get_counters();
|
||||
cycles = (long double)(endperf.cycles - startperf.cycles);
|
||||
#endif
|
||||
|
||||
delete[] P;
|
||||
|
||||
if (cycles >= CYCLES_REQUIRED)
|
||||
break;
|
||||
num_runs *= 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
// Measurement phase
|
||||
for (int i = 0; i < NUM_REPS; i++) {
|
||||
Pedestrian *P = init_pedestrians(n_p);
|
||||
Board board = init_board_without_obstacles();
|
||||
|
||||
#if ROOFLINE
|
||||
trash_value = flush_cache();
|
||||
#endif
|
||||
|
||||
#ifdef __x86_64__
|
||||
myInt64 start = start_tsc();
|
||||
#endif
|
||||
#ifdef __aarch64__
|
||||
startperf = kperf_get_counters();
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < num_runs; j++) {
|
||||
// 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 = (long double)stop_tsc(start) / num_runs;
|
||||
#endif
|
||||
#ifdef __aarch64__
|
||||
endperf = kperf_get_counters();
|
||||
long double cycles = (long double)(endperf.cycles - startperf.cycles) / num_runs;
|
||||
#endif
|
||||
|
||||
delete[] P;
|
||||
cycles_v.push_back(cycles);
|
||||
}
|
||||
|
||||
std::sort(cycles_v.begin(), cycles_v.end());
|
||||
// Use the median cycle count to reduce the effect of noisy measurements.
|
||||
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.
|
||||
|
||||
// symmetry: each pedestrian-pedestrian pair is computed once instead of twice,
|
||||
// so the pairwise (n_p - 1) terms get halved -> n_p * (n_p - 1) / 2 pairs total.
|
||||
// the self-driving force, obstacle force and update pass are untouched.
|
||||
long long pairs = (long long)n_p * (n_p - 1) / 2;
|
||||
|
||||
long long add_count_per_iter = n_p * (2 + N_OBSTACLES * 10 + 2) + n_p * 7 + pairs * 14;
|
||||
long long mult_count_per_iter = n_p * (4 + N_OBSTACLES * 13) + n_p * 16 + pairs * 24;
|
||||
long long div_count_per_iter = n_p * (N_OBSTACLES * 1) + n_p * 2 + pairs * 1;
|
||||
long long sqrt_count_per_iter = n_p * (N_OBSTACLES * 1) + n_p * 3 + pairs * 3;
|
||||
long long exp_count_per_iter = n_p * (N_OBSTACLES * 1) + pairs * 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;
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user