Inlining + Reorganization + Roofline

This commit is contained in:
[Quy Anh] «Elliot» Nguyen
2026-05-19 23:17:44 +02:00
committed by Linus Baumberger
parent 43f11a661b
commit b86c856e08
34 changed files with 2303 additions and 221 deletions
+97
View File
@@ -0,0 +1,97 @@
#pragma once
#include "vector2d.hpp"
struct Obstacle {
Vector2d start; // First endpoint of the obstacle segment.
Vector2d end; // Second endpoint of the obstacle segment.
Vector2d obstacle_v; // Direction vector of the segment: end - start.
double length_sq_inv; // Inverse squared length of the segment, used for projection.
// Default constructor: creates a degenerate obstacle at the origin.
// This is mainly used for array allocation before assigning real obstacles.
Obstacle() : start(0, 0), end(0, 0), obstacle_v(0, 0), length_sq_inv(0.0) {}
// Constructs an obstacle as a finite line segment from s to e.
//
// The segment direction and inverse squared length are precomputed and cached because
// they are reused when projecting pedestrian positions onto the obstacle.
//
// @param s The first endpoint of the obstacle segment.
// @param e The second endpoint of the obstacle segment.
//
Obstacle(Vector2d s, Vector2d e) : start(s), end(e) {
obstacle_v = end - start;
double length_sq = obstacle_v.dot(obstacle_v);
length_sq_inv = length_sq != 0.0 ? 1.0 / length_sq : 0.0;
}
// Flop count: 5 ADDs, 5 MULTs
//
// Finds the point on this finite obstacle segment that is closest to p.
//
// The method projects the vector (p - start) onto the obstacle direction
// vector, then clamps the projection factor to [0, 1] so that the returned
// point lies on the finite segment.
//
// Note : If the obstacle is a single point, meaning start == end, length_sq_inv is zero
// and the method returns start.
//
// @param p The point for which the closest obstacle point is computed.
// @return The closest point on the obstacle segment to p.
//
Vector2d find_nearest_obstacle_point(const Vector2d &p) const {
// # 3 ADDs, 3 MULTs
double projection = (p - start).dot(obstacle_v) * length_sq_inv;
if (projection < 0.0)
projection = 0.0;
else if (projection > 1.0)
projection = 1.0;
// # 2 ADDs, 2 MULTs
return start + obstacle_v * projection;
}
};
struct Board {
double length; // Board length.
double width; // Board width.
Obstacle *obstacles; // Array of obstacles contained in the board.
int n_obstacles; // Total number of obstacles
// Constructs a rectangular board of size length x width.
//
// The input array o contains n user-defined obstacles. The constructor copies
// those obstacles and then appends the two horizontal borders of the board (bottom and top) as additional
// obstacle segments:
//
// bottom: (0, 0) -> (length, 0)
// top: (0, width) -> (length, width)
//
// Note: only the horizontal borders are modeled as obstacles. The vertical
// borders are left open so pedestrians can enter and leave the walkway without
// being affected by repulsive forces from those borders. This is consistent
// with Fig. 2 in the paper, where the horizontal walkway boundaries are
// emphasized but the vertical ends are not shown as walls.
//
// @param l The board length.
// @param w The board width.
// @param n The number of user-defined obstacles in o.
// @param o Array of user-defined obstacles to copy into the board obstacles array.
//
Board(double l, double w, int n, Obstacle *o) : length(l), width(w), n_obstacles(n + 2) {
obstacles = new Obstacle[n + 2];
for (int i = 0; i < n; i++) {
obstacles[i] = o[i];
}
// Board borders: bottom, top.
obstacles[n] = Obstacle(Vector2d(0, 0), Vector2d(l, 0));
obstacles[n + 1] = Obstacle(Vector2d(0, w), Vector2d(l, w));
}
// Releases the dynamically allocated obstacle array.
~Board() { delete[] obstacles; }
};
+96
View File
@@ -0,0 +1,96 @@
#pragma once
#include <cmath>
//
// $\frac{1}{\tau_{\alpha}}$; $\tau_{\alpha} = 0.5s$
//
const double tau_alpha_inv = 2.0;
// $V^0_{\alpha \beta}$
const double V0_AB = 2.1;
// $\sigma$
const double SIGMA = 0.3;
//
// $\frac{-1}{2\sigma}$
//
const double NEG_TWO_SIGMA_INV = -1.0 / (2 * SIGMA);
//
// $\frac{-V^0_{\alpha\beta}}{2\sigma}$
//
const double NEG_V0_AB_div_TWO_SIGMA = V0_AB * NEG_TWO_SIGMA_INV;
// $U^0_{\alpha B}$
const double U0_AB = 10.0;
// $R$
const double R = 0.2;
//
// $\frac{-1}{R}$
//
const double NEG_R_INV = -1.0 / R;
//
// $\frac{-U^0_{\alpha B}}{R}$
//
const double NEG_U0_AB_div_R = -U0_AB / R;
// $-c$
const double c_neg = -0.5;
// $\varphi$
const double phi = 100.0 / 180.0 * M_PI;
// $cos(\varphi)$
const double cos_phi = cos(phi);
//
// $\frac{1}{cos(\varphi)}$
//
const double cos_phi_inv = 1 / cos(phi);
// $<v^0>$
const double v0_mean = 1.34;
// $\sqrt{\theta}$
const double v0_std_dev = 0.26;
// $\varDelta_{t}$
const double delta_t = 2;
// $\varDelta_{t}^2$
const double delta_t_sq = delta_t * delta_t;
// $dt$
const double dt = 0.02;
// $-dt$
const double dt_neg = -dt;
// $-c \cdot dt$
const double c_neg_times_dt = c_neg * dt;
//
// $\frac{1}{\tau_{\alpha}} \cdot dt$
//
const double dt_times_tau_alpha_inv = dt * tau_alpha_inv;
const double cost_add = 1;
const double cost_mult = 1;
const double cost_div = 1;
// https://acl.inf.ethz.ch/teaching/fastcode/knowledgebase/#:~:text=How%20do%20exp(x)%2C%20sin(x)%2C%20etc.%20count%20in%20the%20flopcount%20of%20an%20algorithm%3F
const double cost_exp = 30;
// -O3 maps each sqrt() call to an instruction
const double cost_sqrt = 1;
// Numerical tolerance used to avoid division by zero and unstable operations
// when distances or norms are extremely small.
const double EPS = 1e-9;
// Squared numerical tolerance
const double EPS_SQ = EPS * EPS;
+110
View File
@@ -0,0 +1,110 @@
#pragma once
#include "board.hpp"
#include "pedestrian.hpp"
#include "vector2d.hpp"
// Note: force contributions are multiplied by dt inside each function because
// this allows us to bake dt into precomputed constants and thus save two
// multiplications in the main loop. Conceptually, each returned term is the
// explicit-Euler approximation of the time integral of one force contribution
// over a single simulation step.
//
// This is valid because integration is linear: the integral of a sum is the sum
// of the integrals. Thus, summing F_i * dt for each contribution is equivalent
// to computing dt * sum(F_i) after all force contributions have been accumulated.
// Computes the direction-dependent perception weight from Eq. (7) in the paper:
//
// $w(\vec{e}_{\alpha}, \vec{f}) = \begin{cases} 1.0, & \text{if } \vec{e}_{\alpha} \cdot \vec{f} \geq \|\vec{f}\| \cos(\varphi), \\ c, & \text{otherwise}. \end{cases}$
//
// where:
// - $\vec{e}_{\alpha}$ is the desired direction of motion of the pedestrian,
// - $\vec{f}$ is the force under consideration (e.g., the repulsive force from another pedestrian or an obstacle),
// - $\varphi$ is half of the effective angle of sight,
// - $c$ is the reduced weight factor for situations outside the field of view.
//
// The function returns:
//
// $w(\vec{e}_{\alpha}, \vec{f}).(-dt)$
//
// Note: the reason we multiply the perception weight by -dt is:
// (1) the factor dt saves a multiplication by dt, in pedestrian_repulsion_dt and obstacle_repulsion_dt;
// (2) the minus sign is applied because the repulsion routines (pedestrian_repulsion_dt and obstacle_repulsion_dt) compute and
// pass the negated force direction to w(), matching the paper's w(e_alpha, -f) term. Returning -w() then converts that internally
// negated force back to the correct signed result vector without needing to perform any vector negation, in order to meet the paper's formula.
//
// @param e : The desired direction of motion of the pedestrian.
// @param f : The force under consideration (e.g., the repulsive force from another pedestrian or an obstacle).
// @return The direction-dependent perception weight multiplied by -dt.
//
double w(Vector2d e, Vector2d f);
// Computes the acceleration term of the social force model
// for pedestrian alpha (Eq. (2) in the paper):
//
// $\vec{F}^0_\alpha = \frac{1}{\tau_\alpha}(v^0_\alpha \vec{e}_\alpha - \vec{v}_\alpha)$
//
// where:
// - $v^0_\alpha$ is the desired speed,
// - $\vec{e}_\alpha$ is the desired direction,
// - $\vec{v}_\alpha$ is the current velocity,
// - $\tau_\alpha$ is the relaxation time.
//
// This function returns $\vec{F}^0_\alpha.dt$, i.e. the velocity increment caused by
// the acceleration term during one simulation time step:
//
// $\vec{F}^0_\alpha.dt = \frac{dt}{\tau_\alpha}(v^0_\alpha \vec{e}_\alpha - \vec{v}_\alpha)$
//
// @param alpha : The pedestrian for which the acceleration contribution is computed.
// @return The velocity increment caused by the acceleration term during one simulation time step.
//
Vector2d acceleration_term_dt(const Pedestrian &alpha);
// Computes the repulsive pedestrian-pedestrian effect
// exerted by pedestrian beta on pedestrian alpha (Eq. (3) in the paper),
// weighted by the direction-dependent perception factor from Eq. (7)-(8):
//
// $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha \beta}) = w(\vec{e}_{\alpha}, -\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}))\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta})$
//
// where:
// - $\vec{r}_{\alpha \beta} = \vec{r}_\alpha - \vec{r}_\beta$ is the vector from beta's position to alpha's position,
// - $\vec{e}_\alpha$ is the desired direction of pedestrian alpha,
//
// - $\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}) = -\nabla_{\vec{r}_{\alpha\beta}} V_{\alpha\beta}\left[b(\vec{r}_{\alpha\beta})\right]$ is the repulsive effect from beta on alpha (The expanded formula details are given in the code comments given inside the function's implementation in variants_baseline.cpp),
//
// - $w(\vec{e}_{\alpha}, -\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}))$ the direction-dependent perception weighting function, for which the formula is detailed in the w() function above.
//
// This function returns $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha \beta}).dt$, i.e. the velocity increment caused by the repulsive pedestrian-pedestrian effect during one simulation time step:
//
// $\vec{F}_{\alpha\beta}(\vec{e}_{\alpha}, \vec{r}_{\alpha \beta}).dt = w(\vec{e}_{\alpha}, -\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}))\vec{f}_{\alpha\beta}(\vec{r}_{\alpha \beta}).dt$
//
// @param alpha : The pedestrian receiving the repulsive effect.
// @param beta : The pedestrian producing the repulsive effect.
// @return The velocity increment caused by the repulsive effect from pedestrian beta on alpha during one
// simulation time step.
//
Vector2d pedestrian_repulsion_dt(const Pedestrian &alpha, const Pedestrian &beta);
// Computes the repulsive effect exerted by obstacle B
// on pedestrian alpha (Eq. (5) in the paper), weighted by the direction-dependent perception factor from Eq. (7)-(8]:
//
// $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha B}) = w(\vec{e}_{\alpha}, -\vec{F}_{\alpha B}(\vec{r}_{\alpha B}))\vec{F}_{\alpha B}(\vec{r}_{\alpha B})$
//
// where:
// - $\vec{r}_{\alpha B} = \vec{r}_\alpha - \vec{r}^{\,\alpha}_B$ is the vector from the closest point on obstacle B (i.e., $\vec{r}^{\,\alpha}_B$) to pedestrian alpha's position,
// - $\vec{e}_\alpha$ is the desired direction of pedestrian alpha,
//
// - $\vec{F}_{\alpha B}(\vec{r}_{\alpha B}) = -\nabla_{\vec{r}_{\alpha B}} U_{\alpha B}(||\vec{r}_{\alpha B}||)$ is the repulsive effect from obstacle B on alpha (The expanded formula details are given in the code comments given inside the function's implementation in variant_baseline.cpp),
//
// - $w(\vec{e}_{\alpha}, -\vec{F}_{\alpha B}(\vec{r}_{\alpha B}))$ the direction-dependent perception weighting function, for which the formula is detailed in the w() function above.
//
// This function returns $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha B}).dt$, i.e. the velocity increment caused by the repulsive obstacle effect during one simulation time step:
//
// $\vec{F}_{\alpha B}(\vec{e}_{\alpha}, \vec{r}_{\alpha B}).dt = w(\vec{e}_{\alpha}, -\vec{F}_{\alpha B}(\vec{r}_{\alpha B}))\vec{F}_{\alpha B}(\vec{r}_{\alpha B}).dt$
//
// @param alpha : The pedestrian receiving the obstacle repulsive effect.
// @param B : The obstacle producing the repulsive effect.
// @return The velocity increment caused by the repulsive effect from obstacle B on alpha during one
// simulation time step.
//
Vector2d obstacle_repulsion_dt(const Pedestrian &alpha, const Obstacle &B);
+1093
View File
File diff suppressed because it is too large Load Diff
+65
View File
@@ -0,0 +1,65 @@
#pragma once
#include "constants.hpp"
#include "vector2d.hpp"
// Needed for visual simulation, in order to distinguish two groups of pedestrians:
// - White pedestrians move from left to right
// - Black pedestrians move from right to left
enum Color { WHITE, BLACK};
struct Pedestrian {
Color color;
// $\vec{v}_{\alpha}(t)$
Vector2d curr_vel; // v_alpha (actual, ≤ v_max)
// $\vec{w}_{\alpha}$
Vector2d w; // w_alpha (preferred, integrates force)
// $\vec{r}_\alpha(t)$
Vector2d curr_pos;
// $\vec{e}_{\alpha}(t)$
Vector2d e; // desired direction vector
// $v_{\alpha}^{max} = 1.3 v^{0}_{\alpha}$
double v_max;
// $v_\alpha^0$
double desired_v0;
// $\vec{r}^0_\alpha$
Vector2d destination;
// $s_\beta^2 = (v_\beta \Delta t)^2$
double s_b_sq;
// $s_\beta \vec{e}_\beta$
Vector2d s_b_e;
// Default constructor: initializes a pedestrian with color WHITE and all vector/scalar fields set to zero.
Pedestrian() : color(WHITE), curr_vel(0.0, 0.0), w(0.0, 0.0), curr_pos(0.0, 0.0), e(0.0, 0.0), v_max(0.0), desired_v0(0.0), destination(0.0, 0.0), s_b_sq(0.0), s_b_e(0.0, 0.0) {}
// Constructs a pedestrian with the given initial state and precomputes the
// quantities used in the pedestrian-pedestrian repulsion term.
//
// The preferred velocity w is initialized to the current velocity c_v. The
// cached quantities s_b_sq and s_b_e correspond to the predicted step of this
// pedestrian over $\Delta t$:
//
// s_b = ||c_v|| * dt
// s_b_sq = s_b^2
// s_b_e = e * s_b
//
// These values are used when another pedestrian computes the elliptical
// interaction term involving this pedestrian.
//
// @param clr The display/color category of the pedestrian.
// @param c_v The initial current velocity.
// @param c_p The initial current position.
// @param e The initial desired direction of motion.
// @param v_max The maximum speed that the pedestrian can reach.
// @param desired_v0 The desired walking speed.
// @param d The pedestrian's target destination point.
//
Pedestrian(Color clr, Vector2d c_v, Vector2d c_p, Vector2d e, double v_max, double desired_v0, Vector2d d) : color(clr), curr_vel(c_v), w(c_v), curr_pos(c_p), e(e), v_max(v_max), desired_v0(desired_v0), destination(d), s_b_sq(c_v.dot(c_v) * delta_t_sq), s_b_e(e * c_v.norm() * delta_t) {}
};
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include "constants.hpp"
struct Pedestrian {
int color;
double curr_vel_x;
double curr_vel_y;
double w_x;
double w_y;
double curr_pos_x;
double curr_pos_y;
double e_x;
double e_y;
double v_max;
double desired_v0;
double destination_x;
double destination_y;
double s_b_sq;
double s_b_e_x;
double s_b_e_y;
Pedestrian() : color(0), curr_vel_x(0.0), curr_vel_y(0), w_x(0.0), w_y(0.0), curr_pos_x(0.0), curr_pos_y(0.0), e_x(0.0), e_y(0.0), v_max(0.0), desired_v0(0.0), destination_x(0.0), destination_y(0.0), s_b_sq(0.0), s_b_e_x(0.0), s_b_e_y(0.0) {}
Pedestrian(int clr, double c_v_x, double c_v_y, double c_p_x, double c_p_y, double e_x, double e_y, double v_max, double desired_v0, double d_x, double d_y)
: color(clr), curr_vel_x(c_v_x), curr_vel_y(c_v_y), w_x(c_v_x), w_y(c_v_y), curr_pos_x(c_p_x), curr_pos_y(c_p_y), e_x(e_x), e_y(e_y), v_max(v_max), desired_v0(desired_v0), destination_x(d_x), destination_y(d_y), s_b_sq((c_v_x * c_v_x + c_v_y * c_v_y) * delta_t_sq), s_b_e_x(e_x * (sqrt(c_v_x * c_v_x + c_v_y * c_v_y)) * delta_t), s_b_e_y(e_y * (sqrt(c_v_x * c_v_x + c_v_y * c_v_y)) * delta_t) {}
};
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include "board.hpp"
#include "pedestrian.hpp"
// Initializes the pedestrians used in the simulation.
//
// The returned array contains n_p pedestrians split into two groups:
// one group walking from left to right and one group walking from right to left.
// Pedestrian positions, desired speeds, destinations, and initial directions are
// initialized according to the simulation setup.
//
// @param n_p : Number of pedestrians to initialize.
// @return Pointer to a dynamically allocated array of pedestrians.
Pedestrian *init_pedestrians(int n_p);
// Initializes a simulation board with no obstacles inside.
//
// @return Initialized Board instance.
Board init_board_without_obstacles();
// Runs the social force model with n_p pedestrians for n_iter iterations,
// i.e., n_iter * dt simulated seconds.
// If dump_path is not nullptr, the simulation states are written to that CSV
// file, which can be used to generate a visualization video. If dump_path is
// nullptr, no output file is generated.
//
// @param pedestrians : Array of pedestrians to simulate. The array is updated in place.
// @param board : Board containing the simulation dimensions and obstacles.
// @param n_p : Number of pedestrians.
// @param n_iter : Number of simulation iterations.
// @param dump_path : Optional path to the CSV output file.
// @return 0 on success.
int run_simulation(Pedestrian *pedestrians, Board &board, int n_p, int n_iter, const char *dump_path);
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include "vector2d.hpp"
#include <gtest/gtest.h>
// default tolerance value
constexpr double tolerance = 1e-9;
// Compare two Vector2d component-wise, with tolerance, and assert if difference between expected and obtained value is below tolerance
inline ::testing::AssertionResult Vec2Near(const Vector2d &a, const Vector2d &b, double tol = tolerance) {
if (std::fabs(a.x - b.x) <= tol && std::fabs(a.y - b.y) <= tol) {
return ::testing::AssertionSuccess();
}
return ::testing::AssertionFailure() << "expected (" << b.x << ", " << b.y << "), got (" << a.x << ", " << a.y << "), diff (" << (a.x - b.x) << ", " << (a.y - b.y) << ")" << ", exceeds tolerance : " << tol;
}
#define EXPECT_VEC2_NEAR(actual, expected) EXPECT_TRUE(Vec2Near((actual), (expected)))
#define EXPECT_VEC2_NEAR_TOL(actual, expected, tol) EXPECT_TRUE(Vec2Near((actual), (expected), (tol)))
+83
View File
@@ -0,0 +1,83 @@
/* ==================== GNU C and possibly other UNIX compilers ===================== */
#ifndef _WIN32
#if defined(__GNUC__) || defined(__linux__)
#define VOLATILE __volatile__
#define ASM __asm__
#else
/* if we're neither compiling with gcc or under linux, we can hope
* the following lines work, they probably won't */
#define ASM asm
#define VOLATILE
#endif
#define myInt64 unsigned long long
#define INT32 unsigned int
/* ======================== WIN32 ======================= */
#else
#define myInt64 signed __int64
#define INT32 unsigned __int32
#endif
/* This is the RDTSC timer.
* RDTSC is an instruction on several Intel and compatible CPUs that Reads the
* Time Stamp Counter. The Intel manuals contain more information.
*/
#define COUNTER_LO(a) ((a).int32.lo)
#define COUNTER_HI(a) ((a).int32.hi)
#define COUNTER_VAL(a) ((a).int64)
#define COUNTER(a) ((unsigned long long)COUNTER_VAL(a))
#define COUNTER_DIFF(a, b) (COUNTER(a) - COUNTER(b))
/* ==================== GNU C and possibly other UNIX compilers ===================== */
#ifndef _WIN32
typedef union {
myInt64 int64;
struct {
INT32 lo, hi;
} int32;
} tsc_counter;
#define RDTSC(cpu_c) ASM VOLATILE("rdtsc" : "=a"((cpu_c).int32.lo), "=d"((cpu_c).int32.hi))
#define CPUID() ASM VOLATILE("cpuid" : : "a"(0) : "bx", "cx", "dx")
/* ======================== WIN32 ======================= */
#else
typedef union {
myInt64 int64;
struct {
INT32 lo, hi;
} int32;
} tsc_counter;
#define RDTSC(cpu_c) {__asm rdtsc __asm mov(cpu_c).int32.lo, eax __asm mov(cpu_c).int32.hi, edx}
#define CPUID() {__asm mov eax, 0 __asm cpuid}
#endif
static void init_tsc() {
; // no need to initialize anything for x86
}
static myInt64 start_tsc(void) {
tsc_counter start;
CPUID();
RDTSC(start);
return COUNTER_VAL(start);
}
static myInt64 stop_tsc(myInt64 start) {
tsc_counter end;
RDTSC(end);
CPUID();
return COUNTER_VAL(end) - start;
}
+116
View File
@@ -0,0 +1,116 @@
#pragma once
// This function exactly tracks the flop count for each function
// Usage
// compile, e.g.:
// make run-bench FLOPCOUNT=1 N_P=60 N_ITER=4000
// if not compiled with FLOPCOUNT all associated function are written as ((void)0) and removed when using -O3 flag
// In code:
// FLOP_RECORD(flop_counter::<FUNC_NAME>, int add, int mult, int div, int sqrt, int exp)
// <FUNC_NAME> see FunctionId in util.hpp
// Print out metrics:
// flop_counter::report();
// flop_counter::report_csv();
// Additonal remarks:
// make sure to call flop_counter::reset(); after the warmup phase, because else counting flops double
// make sure to import util.hpp #include "util.hpp"
// get the cycles count per flop
#include "constants.hpp" // cost_add, cost_mult, cost_div, cost_sqrt, cost_exp
#include <array>
#include <cstdint>
#include <cstdio>
#ifndef FLOPCOUNT
#define FLOPCOUNT 0
#endif
namespace flop_counter {
// defined functions, add new one if needed
// create enumeration, which must be in sync with kFunctionNames
enum FunctionId : int {
FN_W,
FN_ACCELERATION,
FN_PEDESTRIAN_REPULSION,
FN_OBSTACLE_REPULSION,
FN_NEAREST_OBSTACLE_POINT,
FN_UPDATE,
FN_OTHER,
FN_COUNT
};
// track the flop count for each function separately
// add more functions, do not mix up the order
inline constexpr std::array<const char *, FN_COUNT> kFunctionNames = {
"w",
"acceleration_term_dt",
"pedestrian_repulsion_dt",
"obstacle_repulsion_dt",
"find_nearest_obstacle_point",
"update_pass",
"other",
};
#if FLOPCOUNT
// for each function store add, mult, div, sqrt, exp
inline std::uint64_t counters[FN_COUNT][5]{};
// update
inline void record(FunctionId fn, std::uint64_t add, std::uint64_t mult,
std::uint64_t div, std::uint64_t sqrt_n,
std::uint64_t exp_n) {
counters[fn][0] += add;
counters[fn][1] += mult;
counters[fn][2] += div;
counters[fn][3] += sqrt_n;
counters[fn][4] += exp_n;
}
// set all metrics to 0
inline void reset() {
for (int f = 0; f < FN_COUNT; ++f)
for (int o = 0; o < 5; ++o)
counters[f][o] = 0;
}
inline void report() {
std::printf("\n=== flop count ===\n");
std::printf("%-30s %12s %12s %12s %12s %12s %16s\n",
"function", "add", "mult", "div", "sqrt", "exp", "per function");
long double total = 0;
for (int f = 0; f < FN_COUNT; ++f) {
auto a = counters[f][0], m = counters[f][1], d = counters[f][2],
s = counters[f][3], e = counters[f][4];
long double w = a * (long double)cost_add + m * (long double)cost_mult
+ d * (long double)cost_div + s * (long double)cost_sqrt
+ e * (long double)cost_exp;
total += w;
if ((a | m | d | s | e) == 0) continue;
std::printf("%-30s %12llu %12llu %12llu %12llu %12llu %16.2Lf\n",
kFunctionNames[f],
(unsigned long long)a, (unsigned long long)m,
(unsigned long long)d, (unsigned long long)s,
(unsigned long long)e, w);
}
std::printf("%-30s %12s %12s %12s %12s %12s %16.2Lf\n",
"TOTAL", "", "", "", "", "", total);
}
#define FLOP_RECORD(fn, a, m, d, s, e) \
::flop_counter::record((fn), (a), (m), (d), (s), (e))
#else // FLOPCOUNT == 0 -> no-op, removed by -O3
inline void record(FunctionId, std::uint64_t, std::uint64_t, std::uint64_t,
std::uint64_t, std::uint64_t) {}
inline void reset() {}
inline void report() {}
#define FLOP_RECORD(fn, a, m, d, s, e) ((void)0)
#endif
} // namespace flop_counter
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <math.h>
struct Vector2d {
double x, y;
Vector2d(double a, double b) : x(a), y(b) {}
Vector2d() : x(.0), y(.0) {}
Vector2d operator+(Vector2d v) const { return Vector2d(v.x + x, v.y + y); }
Vector2d operator-(Vector2d v) const { return Vector2d(x - v.x, y - v.y); }
Vector2d operator-(double a) const { return Vector2d(x - a, y - a); }
Vector2d operator+(double a) const { return Vector2d(x + a, y + a); }
Vector2d operator/(double a) const { return Vector2d(x / a, y / a); }
Vector2d operator*(double a) const { return Vector2d(x * a, y * a); }
Vector2d operator+=(const Vector2d &v) {
*this = *this + v;
return *this;
}
Vector2d operator-=(const Vector2d &v) {
*this = *this - v;
return *this;
}
Vector2d reverse_sign() { return Vector2d(-x, -y); }
// # 1 ADD, 2 MULTs, 1 SQRT
double norm() { return sqrt(x * x + y * y); }
// # 1 ADD, 2 MULTs
double dot(Vector2d v) { return x * v.x + y * v.y; }
void reset() {
x = 0;
y = 0;
}
};