Clang-format tests, examples, libraries, benchmarks, etc.

This commit is contained in:
Antonio Sánchez
2023-12-05 21:22:55 +00:00
committed by Rasmus Munk Larsen
parent 3252ecc7a4
commit 46e9cdb7fe
876 changed files with 33453 additions and 37795 deletions

View File

@@ -37,20 +37,17 @@ uint8_t log2_pot(size_t x) {
return l;
}
uint16_t compact_size_triple(size_t k, size_t m, size_t n)
{
uint16_t compact_size_triple(size_t k, size_t m, size_t n) {
return (log2_pot(k) << 8) | (log2_pot(m) << 4) | log2_pot(n);
}
// just a helper to store a triple of K,M,N sizes for matrix product
struct size_triple_t
{
struct size_triple_t {
uint16_t k, m, n;
size_triple_t() : k(0), m(0), n(0) {}
size_triple_t(size_t _k, size_t _m, size_t _n) : k(_k), m(_m), n(_n) {}
size_triple_t(const size_triple_t& o) : k(o.k), m(o.m), n(o.n) {}
size_triple_t(uint16_t compact)
{
size_triple_t(uint16_t compact) {
k = 1 << ((compact & 0xf00) >> 8);
m = 1 << ((compact & 0x0f0) >> 4);
n = 1 << ((compact & 0x00f) >> 0);
@@ -58,35 +55,23 @@ struct size_triple_t
bool is_cubic() const { return k == m && m == n; }
};
ostream& operator<<(ostream& s, const size_triple_t& t)
{
return s << "(" << t.k << ", " << t.m << ", " << t.n << ")";
}
ostream& operator<<(ostream& s, const size_triple_t& t) { return s << "(" << t.k << ", " << t.m << ", " << t.n << ")"; }
struct inputfile_entry_t
{
struct inputfile_entry_t {
uint16_t product_size;
uint16_t pot_block_size;
size_triple_t nonpot_block_size;
float gflops;
};
struct inputfile_t
{
enum class type_t {
unknown,
all_pot_sizes,
default_sizes
};
struct inputfile_t {
enum class type_t { unknown, all_pot_sizes, default_sizes };
string filename;
vector<inputfile_entry_t> entries;
type_t type;
inputfile_t(const string& fname)
: filename(fname)
, type(type_t::unknown)
{
inputfile_t(const string& fname) : filename(fname), type(type_t::unknown) {
ifstream stream(filename);
if (!stream.is_open()) {
cerr << "couldn't open input file: " << filename << endl;
@@ -111,27 +96,17 @@ struct inputfile_t
type = type_t::default_sizes;
continue;
}
if (type == type_t::unknown) {
continue;
}
switch(type) {
switch (type) {
case type_t::all_pot_sizes: {
unsigned int product_size, block_size;
float gflops;
int sscanf_result =
sscanf(line.c_str(), "%x %x %f",
&product_size,
&block_size,
&gflops);
if (3 != sscanf_result ||
!product_size ||
product_size > 0xfff ||
!block_size ||
block_size > 0xfff ||
!isfinite(gflops))
{
int sscanf_result = sscanf(line.c_str(), "%x %x %f", &product_size, &block_size, &gflops);
if (3 != sscanf_result || !product_size || product_size > 0xfff || !block_size || block_size > 0xfff ||
!isfinite(gflops)) {
cerr << "ill-formed input file: " << filename << endl;
cerr << "offending line:" << endl << line << endl;
exit(1);
@@ -150,16 +125,8 @@ struct inputfile_t
unsigned int product_size;
float gflops;
int bk, bm, bn;
int sscanf_result =
sscanf(line.c_str(), "%x default(%d, %d, %d) %f",
&product_size,
&bk, &bm, &bn,
&gflops);
if (5 != sscanf_result ||
!product_size ||
product_size > 0xfff ||
!isfinite(gflops))
{
int sscanf_result = sscanf(line.c_str(), "%x default(%d, %d, %d) %f", &product_size, &bk, &bm, &bn, &gflops);
if (5 != sscanf_result || !product_size || product_size > 0xfff || !isfinite(gflops)) {
cerr << "ill-formed input file: " << filename << endl;
cerr << "offending line:" << endl << line << endl;
exit(1);
@@ -175,7 +142,7 @@ struct inputfile_t
entries.push_back(entry);
break;
}
default:
break;
}
@@ -192,27 +159,22 @@ struct inputfile_t
}
};
struct preprocessed_inputfile_entry_t
{
struct preprocessed_inputfile_entry_t {
uint16_t product_size;
uint16_t block_size;
float efficiency;
};
bool lower_efficiency(const preprocessed_inputfile_entry_t& e1, const preprocessed_inputfile_entry_t& e2)
{
bool lower_efficiency(const preprocessed_inputfile_entry_t& e1, const preprocessed_inputfile_entry_t& e2) {
return e1.efficiency < e2.efficiency;
}
struct preprocessed_inputfile_t
{
struct preprocessed_inputfile_t {
string filename;
vector<preprocessed_inputfile_entry_t> entries;
preprocessed_inputfile_t(const inputfile_t& inputfile)
: filename(inputfile.filename)
{
preprocessed_inputfile_t(const inputfile_t& inputfile) : filename(inputfile.filename) {
if (inputfile.type != inputfile_t::type_t::all_pot_sizes) {
abort();
}
@@ -220,20 +182,16 @@ struct preprocessed_inputfile_t
auto it_first_with_given_product_size = it;
while (it != inputfile.entries.end()) {
++it;
if (it == inputfile.entries.end() ||
it->product_size != it_first_with_given_product_size->product_size)
{
if (it == inputfile.entries.end() || it->product_size != it_first_with_given_product_size->product_size) {
import_input_file_range_one_product_size(it_first_with_given_product_size, it);
it_first_with_given_product_size = it;
}
}
}
private:
void import_input_file_range_one_product_size(
const vector<inputfile_entry_t>::const_iterator& begin,
const vector<inputfile_entry_t>::const_iterator& end)
{
private:
void import_input_file_range_one_product_size(const vector<inputfile_entry_t>::const_iterator& begin,
const vector<inputfile_entry_t>::const_iterator& end) {
uint16_t product_size = begin->product_size;
float max_gflops = 0.0f;
for (auto it = begin; it != end; ++it) {
@@ -254,9 +212,7 @@ private:
}
};
void check_all_files_in_same_exact_order(
const vector<preprocessed_inputfile_t>& preprocessed_inputfiles)
{
void check_all_files_in_same_exact_order(const vector<preprocessed_inputfile_t>& preprocessed_inputfiles) {
if (preprocessed_inputfiles.empty()) {
return;
}
@@ -266,11 +222,8 @@ void check_all_files_in_same_exact_order(
for (size_t i = 0; i < preprocessed_inputfiles.size(); i++) {
if (preprocessed_inputfiles[i].entries.size() != num_entries) {
cerr << "these files have different number of entries: "
<< preprocessed_inputfiles[i].filename
<< " and "
<< first_file.filename
<< endl;
cerr << "these files have different number of entries: " << preprocessed_inputfiles[i].filename << " and "
<< first_file.filename << endl;
exit(1);
}
}
@@ -281,12 +234,8 @@ void check_all_files_in_same_exact_order(
for (size_t file_index = 0; file_index < preprocessed_inputfiles.size(); file_index++) {
const preprocessed_inputfile_t& cur_file = preprocessed_inputfiles[file_index];
if (cur_file.entries[entry_index].product_size != entry_product_size ||
cur_file.entries[entry_index].block_size != entry_block_size)
{
cerr << "entries not in same order between these files: "
<< first_file.filename
<< " and "
<< cur_file.filename
cur_file.entries[entry_index].block_size != entry_block_size) {
cerr << "entries not in same order between these files: " << first_file.filename << " and " << cur_file.filename
<< endl;
exit(1);
}
@@ -294,10 +243,8 @@ void check_all_files_in_same_exact_order(
}
}
float efficiency_of_subset(
const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
const vector<size_t>& subset)
{
float efficiency_of_subset(const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
const vector<size_t>& subset) {
if (subset.size() <= 1) {
return 1.0f;
}
@@ -309,9 +256,7 @@ float efficiency_of_subset(
uint16_t product_size = first_file.entries[0].product_size;
while (entry_index < num_entries) {
++entry_index;
if (entry_index == num_entries ||
first_file.entries[entry_index].product_size != product_size)
{
if (entry_index == num_entries || first_file.entries[entry_index].product_size != product_size) {
float efficiency_this_product_size = 0.0f;
for (size_t e = first_entry_index_with_this_product_size; e < entry_index; e++) {
float efficiency_this_entry = 1.0f;
@@ -331,10 +276,8 @@ float efficiency_of_subset(
return efficiency;
}
void dump_table_for_subset(
const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
const vector<size_t>& subset)
{
void dump_table_for_subset(const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
const vector<size_t>& subset) {
const preprocessed_inputfile_t& first_file = preprocessed_inputfiles[subset[0]];
const size_t num_entries = first_file.entries.size();
size_t entry_index = 0;
@@ -359,9 +302,7 @@ void dump_table_for_subset(
cout << " static const unsigned short data[" << TableSize << "] = {";
while (entry_index < num_entries) {
++entry_index;
if (entry_index == num_entries ||
first_file.entries[entry_index].product_size != product_size)
{
if (entry_index == num_entries || first_file.entries[entry_index].product_size != product_size) {
float best_efficiency_this_product_size = 0.0f;
uint16_t best_block_size_this_product_size = 0;
for (size_t e = first_entry_index_with_this_product_size; e < entry_index; e++) {
@@ -397,10 +338,8 @@ void dump_table_for_subset(
cout << "};" << endl;
}
float efficiency_of_partition(
const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
const vector<vector<size_t>>& partition)
{
float efficiency_of_partition(const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
const vector<vector<size_t>>& partition) {
float efficiency = 1.0f;
for (auto s = partition.begin(); s != partition.end(); ++s) {
efficiency = min(efficiency, efficiency_of_subset(preprocessed_inputfiles, *s));
@@ -408,8 +347,7 @@ float efficiency_of_partition(
return efficiency;
}
void make_first_subset(size_t subset_size, vector<size_t>& out_subset, size_t set_size)
{
void make_first_subset(size_t subset_size, vector<size_t>& out_subset, size_t set_size) {
assert(subset_size >= 1 && subset_size <= set_size);
out_subset.resize(subset_size);
for (size_t i = 0; i < subset_size; i++) {
@@ -417,13 +355,9 @@ void make_first_subset(size_t subset_size, vector<size_t>& out_subset, size_t se
}
}
bool is_last_subset(const vector<size_t>& subset, size_t set_size)
{
return subset[0] == set_size - subset.size();
}
bool is_last_subset(const vector<size_t>& subset, size_t set_size) { return subset[0] == set_size - subset.size(); }
void next_subset(vector<size_t>& inout_subset, size_t set_size)
{
void next_subset(vector<size_t>& inout_subset, size_t set_size) {
if (is_last_subset(inout_subset, set_size)) {
cerr << "iterating past the last subset" << endl;
abort();
@@ -444,9 +378,8 @@ void next_subset(vector<size_t>& inout_subset, size_t set_size)
const size_t number_of_subsets_limit = 100;
const size_t always_search_subsets_of_size_at_least = 2;
bool is_number_of_subsets_feasible(size_t n, size_t p)
{
assert(n>0 && p>0 && p<=n);
bool is_number_of_subsets_feasible(size_t n, size_t p) {
assert(n > 0 && p > 0 && p <= n);
uint64_t numerator = 1, denominator = 1;
for (size_t i = 0; i < p; i++) {
numerator *= n - i;
@@ -458,24 +391,20 @@ bool is_number_of_subsets_feasible(size_t n, size_t p)
return true;
}
size_t max_feasible_subset_size(size_t n)
{
size_t max_feasible_subset_size(size_t n) {
assert(n > 0);
const size_t minresult = min<size_t>(n-1, always_search_subsets_of_size_at_least);
const size_t minresult = min<size_t>(n - 1, always_search_subsets_of_size_at_least);
for (size_t p = 1; p <= n - 1; p++) {
if (!is_number_of_subsets_feasible(n, p+1)) {
if (!is_number_of_subsets_feasible(n, p + 1)) {
return max(p, minresult);
}
}
return n - 1;
}
void find_subset_with_efficiency_higher_than(
const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
float required_efficiency_to_beat,
vector<size_t>& inout_remainder,
vector<size_t>& out_subset)
{
void find_subset_with_efficiency_higher_than(const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
float required_efficiency_to_beat, vector<size_t>& inout_remainder,
vector<size_t>& out_subset) {
out_subset.resize(0);
if (required_efficiency_to_beat >= 1.0f) {
@@ -484,7 +413,6 @@ void find_subset_with_efficiency_higher_than(
}
while (!inout_remainder.empty()) {
vector<size_t> candidate_indices(inout_remainder.size());
for (size_t i = 0; i < candidate_indices.size(); i++) {
candidate_indices[i] = i;
@@ -493,20 +421,17 @@ void find_subset_with_efficiency_higher_than(
size_t candidate_indices_subset_size = max_feasible_subset_size(candidate_indices.size());
while (candidate_indices_subset_size >= 1) {
vector<size_t> candidate_indices_subset;
make_first_subset(candidate_indices_subset_size,
candidate_indices_subset,
candidate_indices.size());
make_first_subset(candidate_indices_subset_size, candidate_indices_subset, candidate_indices.size());
vector<size_t> best_candidate_indices_subset;
float best_efficiency = 0.0f;
vector<size_t> trial_subset = out_subset;
trial_subset.resize(out_subset.size() + candidate_indices_subset_size);
while (true)
{
while (true) {
for (size_t i = 0; i < candidate_indices_subset_size; i++) {
trial_subset[out_subset.size() + i] = inout_remainder[candidate_indices_subset[i]];
}
float trial_efficiency = efficiency_of_subset(preprocessed_inputfiles, trial_subset);
if (trial_efficiency > best_efficiency) {
best_efficiency = trial_efficiency;
@@ -517,7 +442,7 @@ void find_subset_with_efficiency_higher_than(
}
next_subset(candidate_indices_subset, candidate_indices.size());
}
if (best_efficiency > required_efficiency_to_beat) {
for (size_t i = 0; i < best_candidate_indices_subset.size(); i++) {
candidate_indices[i] = candidate_indices[best_candidate_indices_subset[i]];
@@ -526,7 +451,7 @@ void find_subset_with_efficiency_higher_than(
}
candidate_indices_subset_size--;
}
size_t candidate_index = candidate_indices[0];
auto candidate_iterator = inout_remainder.begin() + candidate_index;
vector<size_t> trial_subset = out_subset;
@@ -542,11 +467,9 @@ void find_subset_with_efficiency_higher_than(
}
}
void find_partition_with_efficiency_higher_than(
const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
float required_efficiency_to_beat,
vector<vector<size_t>>& out_partition)
{
void find_partition_with_efficiency_higher_than(const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
float required_efficiency_to_beat,
vector<vector<size_t>>& out_partition) {
out_partition.resize(0);
vector<size_t> remainder;
@@ -556,25 +479,19 @@ void find_partition_with_efficiency_higher_than(
while (!remainder.empty()) {
vector<size_t> new_subset;
find_subset_with_efficiency_higher_than(
preprocessed_inputfiles,
required_efficiency_to_beat,
remainder,
new_subset);
find_subset_with_efficiency_higher_than(preprocessed_inputfiles, required_efficiency_to_beat, remainder,
new_subset);
out_partition.push_back(new_subset);
}
}
void print_partition(
const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
const vector<vector<size_t>>& partition)
{
void print_partition(const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
const vector<vector<size_t>>& partition) {
float efficiency = efficiency_of_partition(preprocessed_inputfiles, partition);
cout << "Partition into " << partition.size() << " subsets for " << efficiency * 100.0f << "% efficiency" << endl;
cout << "Partition into " << partition.size() << " subsets for " << efficiency * 100.0f << "% efficiency" << endl;
for (auto subset = partition.begin(); subset != partition.end(); ++subset) {
cout << " Subset " << (subset - partition.begin())
<< ", efficiency " << efficiency_of_subset(preprocessed_inputfiles, *subset) * 100.0f << "%:"
<< endl;
cout << " Subset " << (subset - partition.begin()) << ", efficiency "
<< efficiency_of_subset(preprocessed_inputfiles, *subset) * 100.0f << "%:" << endl;
for (auto file = subset->begin(); file != subset->end(); ++file) {
cout << " " << preprocessed_inputfiles[*file].filename << endl;
}
@@ -586,18 +503,18 @@ void print_partition(
cout << endl;
}
struct action_t
{
virtual const char* invokation_name() const { abort(); return nullptr; }
struct action_t {
virtual const char* invokation_name() const {
abort();
return nullptr;
}
virtual void run(const vector<string>&) const { abort(); }
virtual ~action_t() {}
};
struct partition_action_t : action_t
{
struct partition_action_t : action_t {
virtual const char* invokation_name() const override { return "partition"; }
virtual void run(const vector<string>& input_filenames) const override
{
virtual void run(const vector<string>& input_filenames) const override {
vector<preprocessed_inputfile_t> preprocessed_inputfiles;
if (input_filenames.empty()) {
@@ -627,17 +544,12 @@ struct partition_action_t : action_t
float required_efficiency_to_beat = 0.0f;
vector<vector<vector<size_t>>> partitions;
cerr << "searching for partitions...\r" << flush;
while (true)
{
while (true) {
vector<vector<size_t>> partition;
find_partition_with_efficiency_higher_than(
preprocessed_inputfiles,
required_efficiency_to_beat,
partition);
find_partition_with_efficiency_higher_than(preprocessed_inputfiles, required_efficiency_to_beat, partition);
float actual_efficiency = efficiency_of_partition(preprocessed_inputfiles, partition);
cerr << "partition " << preprocessed_inputfiles.size() << " files into " << partition.size()
<< " subsets for " << 100.0f * actual_efficiency
<< " % efficiency"
cerr << "partition " << preprocessed_inputfiles.size() << " files into " << partition.size() << " subsets for "
<< 100.0f * actual_efficiency << " % efficiency"
<< " \r" << flush;
partitions.push_back(partition);
if (partition.size() == preprocessed_inputfiles.size() || actual_efficiency == 1.0f) {
@@ -649,7 +561,7 @@ struct partition_action_t : action_t
while (true) {
bool repeat = false;
for (size_t i = 0; i < partitions.size() - 1; i++) {
if (partitions[i].size() >= partitions[i+1].size()) {
if (partitions[i].size() >= partitions[i + 1].size()) {
partitions.erase(partitions.begin() + i);
repeat = true;
break;
@@ -665,8 +577,7 @@ struct partition_action_t : action_t
}
};
struct evaluate_defaults_action_t : action_t
{
struct evaluate_defaults_action_t : action_t {
struct results_entry_t {
uint16_t product_size;
size_triple_t default_block_size;
@@ -675,30 +586,24 @@ struct evaluate_defaults_action_t : action_t
float best_pot_gflops;
float default_efficiency;
};
friend ostream& operator<<(ostream& s, const results_entry_t& entry)
{
return s
<< "Product size " << size_triple_t(entry.product_size)
<< ": default block size " << entry.default_block_size
<< " -> " << entry.default_gflops
<< " GFlop/s = " << entry.default_efficiency * 100.0f << " %"
<< " of best POT block size " << size_triple_t(entry.best_pot_block_size)
<< " -> " << entry.best_pot_gflops
<< " GFlop/s" << dec;
friend ostream& operator<<(ostream& s, const results_entry_t& entry) {
return s << "Product size " << size_triple_t(entry.product_size) << ": default block size "
<< entry.default_block_size << " -> " << entry.default_gflops
<< " GFlop/s = " << entry.default_efficiency * 100.0f << " %"
<< " of best POT block size " << size_triple_t(entry.best_pot_block_size) << " -> "
<< entry.best_pot_gflops << " GFlop/s" << dec;
}
static bool lower_efficiency(const results_entry_t& e1, const results_entry_t& e2) {
return e1.default_efficiency < e2.default_efficiency;
}
virtual const char* invokation_name() const override { return "evaluate-defaults"; }
void show_usage_and_exit() const
{
void show_usage_and_exit() const {
cerr << "usage: " << invokation_name() << " default-sizes-data all-pot-sizes-data" << endl;
cerr << "checks how well the performance with default sizes compares to the best "
<< "performance measured over all POT sizes." << endl;
exit(1);
}
virtual void run(const vector<string>& input_filenames) const override
{
virtual void run(const vector<string>& input_filenames) const override {
if (input_filenames.size() != 2) {
show_usage_and_exit();
}
@@ -714,20 +619,17 @@ struct evaluate_defaults_action_t : action_t
}
vector<results_entry_t> results;
vector<results_entry_t> cubic_results;
uint16_t product_size = 0;
auto it_all_pot_sizes = inputfile_all_pot_sizes.entries.begin();
for (auto it_default_sizes = inputfile_default_sizes.entries.begin();
it_default_sizes != inputfile_default_sizes.entries.end();
++it_default_sizes)
{
it_default_sizes != inputfile_default_sizes.entries.end(); ++it_default_sizes) {
if (it_default_sizes->product_size == product_size) {
continue;
}
product_size = it_default_sizes->product_size;
while (it_all_pot_sizes != inputfile_all_pot_sizes.entries.end() &&
it_all_pot_sizes->product_size != product_size)
{
it_all_pot_sizes->product_size != product_size) {
++it_all_pot_sizes;
}
if (it_all_pot_sizes == inputfile_all_pot_sizes.entries.end()) {
@@ -735,10 +637,8 @@ struct evaluate_defaults_action_t : action_t
}
uint16_t best_pot_block_size = 0;
float best_pot_gflops = 0;
for (auto it = it_all_pot_sizes;
it != inputfile_all_pot_sizes.entries.end() && it->product_size == product_size;
++it)
{
for (auto it = it_all_pot_sizes; it != inputfile_all_pot_sizes.entries.end() && it->product_size == product_size;
++it) {
if (it->gflops > best_pot_gflops) {
best_pot_gflops = it->gflops;
best_pot_block_size = it->pot_block_size;
@@ -766,7 +666,7 @@ struct evaluate_defaults_action_t : action_t
cout << endl;
sort(results.begin(), results.end(), lower_efficiency);
const size_t n = min<size_t>(20, results.size());
cout << n << " worst results:" << endl;
for (size_t i = 0; i < n; i++) {
@@ -781,34 +681,30 @@ struct evaluate_defaults_action_t : action_t
cout << endl;
sort(cubic_results.begin(), cubic_results.end(), lower_efficiency);
cout.precision(2);
vector<float> a = {0.5f, 0.20f, 0.10f, 0.05f, 0.02f, 0.01f};
for (auto it = a.begin(); it != a.end(); ++it) {
size_t n = min(results.size() - 1, size_t(*it * results.size()));
cout << (100.0f * n / (results.size() - 1))
<< " % of product sizes have default efficiency <= "
<< 100.0f * results[n].default_efficiency << " %" << endl;
<< " % of product sizes have default efficiency <= " << 100.0f * results[n].default_efficiency << " %"
<< endl;
}
cout.precision(default_precision);
}
};
void show_usage_and_exit(int argc, char* argv[],
const vector<unique_ptr<action_t>>& available_actions)
{
void show_usage_and_exit(int argc, char* argv[], const vector<unique_ptr<action_t>>& available_actions) {
cerr << "usage: " << argv[0] << " <action> [options...] <input files...>" << endl;
cerr << "available actions:" << endl;
for (auto it = available_actions.begin(); it != available_actions.end(); ++it) {
cerr << " " << (*it)->invokation_name() << endl;
}
}
cerr << "the input files should each contain an output of benchmark-blocking-sizes" << endl;
exit(1);
}
int main(int argc, char* argv[])
{
int main(int argc, char* argv[]) {
cout.precision(default_precision);
cerr.precision(default_precision);