mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
5510089d45
* Rename simdjson_really_inline -> simdjson_inline I want to change the simdjson_really_inline macro to sometimes not force inlining. After that upcoming change, the name simdjson_really_inline will no longer makes sense. Rename simdjson_really_inline to simdjson_inline. This patch should not change semantics; simdjson_inline still forces inlining as before. Some functions still need to be really inlined for ABI reasons. (GCC's -Wpsabi complains otherwise.) Leave those functions marked as simdjson_really_inline. * Improve build times for debug builds simdjson_inline is used for most simdjson functions. It forces inlining. In unoptimized/debug builds, this can lead to a lot of machine code being generated (especially with Address Sanitizer), causing slow compilation. Change simdjson_inline to force inlining only for optimized builds. Sometimes, the programmer might want a slightly-optimized build and want fast compilation (e.g. GCC's -Og mode). Allow simdjson users to define the simdjson_inline macro themselves (e.g. on the command line: -Dsimdjson_inline=inline) in cases where the default behavior is undesired. This patch reduced build times by over 75% for ondemand_object_tests.cpp with GCC 9.4.0 and CMAKE_BUILD_TYPE=Debug on my AMD 5950X: Before: 6.885 6.683 6.971 6.957 6.949 seconds (5 samples) After: 1.492 1.551 1.494 1.490 1.531 seconds (5 samples)
176 lines
4.8 KiB
C++
176 lines
4.8 KiB
C++
#ifndef __EVENT_COUNTER_H
|
|
#define __EVENT_COUNTER_H
|
|
|
|
#ifndef SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
|
#ifdef __aarch64__
|
|
// on ARM, we use just cycles and instructions
|
|
#define SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS 1
|
|
#else
|
|
// elsewhere, we try to use four counters.
|
|
#define SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS 0
|
|
#endif
|
|
#endif
|
|
#include <cassert>
|
|
#include <cctype>
|
|
#ifndef _MSC_VER
|
|
#include <dirent.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
#include <cinttypes>
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "linux-perf-events.h"
|
|
#ifdef __linux__
|
|
#include <libgen.h>
|
|
#endif
|
|
|
|
#include "simdjson.h"
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
using std::chrono::steady_clock;
|
|
using std::chrono::time_point;
|
|
using std::chrono::duration;
|
|
|
|
struct event_count {
|
|
duration<double> elapsed;
|
|
vector<unsigned long long> event_counts;
|
|
event_count() : elapsed(0), event_counts{0,0,0,0,0} {}
|
|
event_count(const duration<double> _elapsed, const vector<unsigned long long> _event_counts) : elapsed(_elapsed), event_counts(_event_counts) {}
|
|
event_count(const event_count& other): elapsed(other.elapsed), event_counts(other.event_counts) { }
|
|
|
|
// The types of counters (so we can read the getter more easily)
|
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
|
enum event_counter_types {
|
|
CPU_CYCLES,
|
|
INSTRUCTIONS
|
|
};
|
|
#else
|
|
enum event_counter_types {
|
|
CPU_CYCLES,
|
|
INSTRUCTIONS,
|
|
BRANCH_MISSES,
|
|
CACHE_REFERENCES,
|
|
CACHE_MISSES
|
|
};
|
|
#endif
|
|
double elapsed_sec() const { return duration<double>(elapsed).count(); }
|
|
double elapsed_ns() const { return duration<double, std::nano>(elapsed).count(); }
|
|
double cycles() const { return static_cast<double>(event_counts[CPU_CYCLES]); }
|
|
double instructions() const { return static_cast<double>(event_counts[INSTRUCTIONS]); }
|
|
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
|
double branch_misses() const { return static_cast<double>(event_counts[BRANCH_MISSES]); }
|
|
double cache_references() const { return static_cast<double>(event_counts[CACHE_REFERENCES]); }
|
|
double cache_misses() const { return static_cast<double>(event_counts[CACHE_MISSES]); }
|
|
#endif
|
|
event_count& operator=(const event_count& other) {
|
|
this->elapsed = other.elapsed;
|
|
this->event_counts = other.event_counts;
|
|
return *this;
|
|
}
|
|
event_count operator+(const event_count& other) const {
|
|
return event_count(elapsed+other.elapsed, {
|
|
event_counts[0]+other.event_counts[0],
|
|
event_counts[1]+other.event_counts[1],
|
|
event_counts[2]+other.event_counts[2],
|
|
event_counts[3]+other.event_counts[3],
|
|
event_counts[4]+other.event_counts[4],
|
|
});
|
|
}
|
|
|
|
void operator+=(const event_count& other) {
|
|
*this = *this + other;
|
|
}
|
|
};
|
|
|
|
struct event_aggregate {
|
|
int iterations = 0;
|
|
event_count total{};
|
|
event_count best{};
|
|
event_count worst{};
|
|
|
|
event_aggregate() {}
|
|
|
|
void operator<<(const event_count& other) {
|
|
if (iterations == 0 || other.elapsed < best.elapsed) {
|
|
best = other;
|
|
}
|
|
if (iterations == 0 || other.elapsed > worst.elapsed) {
|
|
worst = other;
|
|
}
|
|
iterations++;
|
|
total += other;
|
|
}
|
|
|
|
double elapsed_sec() const { return total.elapsed_sec() / iterations; }
|
|
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
|
|
double cycles() const { return total.cycles() / iterations; }
|
|
double instructions() const { return total.instructions() / iterations; }
|
|
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
|
double branch_misses() const { return total.branch_misses() / iterations; }
|
|
double cache_references() const { return total.cache_references() / iterations; }
|
|
double cache_misses() const { return total.cache_misses() / iterations; }
|
|
#endif
|
|
};
|
|
|
|
struct event_collector {
|
|
event_count count{};
|
|
time_point<steady_clock> start_clock{};
|
|
|
|
#if defined(__linux__)
|
|
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
|
|
event_collector(bool quiet = false) : linux_events(vector<int>{
|
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
|
PERF_COUNT_HW_CPU_CYCLES,
|
|
PERF_COUNT_HW_INSTRUCTIONS,
|
|
#else
|
|
PERF_COUNT_HW_CPU_CYCLES,
|
|
PERF_COUNT_HW_INSTRUCTIONS,
|
|
PERF_COUNT_HW_BRANCH_MISSES,
|
|
PERF_COUNT_HW_CACHE_REFERENCES,
|
|
PERF_COUNT_HW_CACHE_MISSES
|
|
#endif
|
|
}, quiet) {}
|
|
bool has_events() {
|
|
return linux_events.is_working();
|
|
}
|
|
#else
|
|
event_collector(simdjson_unused bool _quiet = false) {}
|
|
bool has_events() {
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
simdjson_inline void start() {
|
|
#if defined(__linux)
|
|
linux_events.start();
|
|
#endif
|
|
start_clock = steady_clock::now();
|
|
}
|
|
simdjson_inline event_count& end() {
|
|
time_point<steady_clock> end_clock = steady_clock::now();
|
|
#if defined(__linux)
|
|
linux_events.end(count.event_counts);
|
|
#endif
|
|
count.elapsed = end_clock - start_clock;
|
|
return count;
|
|
}
|
|
};
|
|
|
|
#endif
|