Files
1brc/source/main.cxx
T
2026-09-09 00:52:00 -03:00

117 lines
3.6 KiB
C++

import std;
import io;
import map;
import tricks;
auto calculate_boundaries(std::string_view file, std::size_t thread_count) -> std::inplace_vector<std::size_t, 64> {
auto bounds = std::inplace_vector<std::size_t, 64>();
const auto step_estimate = file.size() / thread_count;
while (bounds.size() < thread_count) {
auto at_guess = file.data() + (step_estimate * (1 + bounds.size()));
while (*at_guess++ != '\n') [[likely]] {
}
bounds.push_back(at_guess - file.data());
}
return bounds;
}
[[nodiscard]] auto parse_temp(std::string_view sv) {
constexpr auto dot_bit_mask = std::uint64_t(0x10101000ULL);
auto word = std::uint64_t();
std::memcpy(&word, sv.data(), sizeof(word));
const auto decimal_sep_pos = std::countr_zero(~word & dot_bit_mask);
const auto dot_byte = decimal_sep_pos / 8;
const auto shift = 28 - decimal_sep_pos;
// I don't trust auto here
std::int64_t is_neg = (static_cast<std::int64_t>(~word) << 59) >> 63;
std::uint64_t sign_mask = ~(static_cast<std::uint64_t>(is_neg) & 0xFF);
std::uint64_t digits = ((word & sign_mask) << shift) & 0xFFFFFFFFFFULL;
std::int64_t abs_value = ((digits * 0x640a0001ULL) >> 32) & 0x3FF;
std::int64_t value = (abs_value ^ is_neg) - is_neg;
return std::make_pair(static_cast<std::int32_t>(value), dot_byte + 2);
}
template <std::size_t BatchSize>
[[nodiscard]] std::inplace_vector<std::pair<std::int32_t, std::size_t>, BatchSize> parse_temp(
std::span<const std::string_view> batch) {
auto results = std::inplace_vector<std::pair<std::int32_t, std::size_t>, BatchSize>();
for (std::size_t i = 0; i < batch.size(); ++i) {
results.push_back(parse_temp(batch[i]));
}
return results;
}
void process_chunk(std::string_view chunk, brc::map& results) {
auto it = chunk.begin();
while (it != chunk.end()) {
const auto semicolon = brc::find_delim({it, chunk.end()}, ';');
const auto city = std::string_view(it, it + semicolon);
const auto [value, len] = parse_temp({it + semicolon + 1, chunk.end()});
results.insert(city, value);
it = it + semicolon + 1 + len + 1;
}
}
auto main(int argc, char** argv) -> int {
if (argc != 2) {
std::println("usage: {} <file.txt>", argv[0]);
return 1;
}
const auto filename = std::filesystem::path(argv[1]);
const auto file = brc::mapped_file(filename);
const auto view = std::string_view(file);
const auto thread_count = std::thread::hardware_concurrency() / 1;
const auto boundaries = calculate_boundaries(file, thread_count);
auto threads = std::vector<std::thread>();
auto results = std::vector<brc::map>();
for (std::size_t i = 0; i < thread_count; i++) {
results.emplace_back(1ull << 10ull);
}
for (std::size_t i = 0; i < thread_count; i++) {
const auto begin = i == 0 ? 0 : boundaries[i - 1];
const auto end = boundaries[i];
const auto sv = std::string_view(view.begin() + begin, view.begin() + end);
threads.emplace_back([i, sv, &results]() { process_chunk(sv, results[i]); });
}
for (std::size_t i = 0; i < thread_count; i++) {
threads[i].join();
}
for (std::size_t i = 1; i < results.size(); i++) {
results[0].merge_against(results[i]);
}
const auto& map = results[0];
const auto entries = map.collapse();
std::print("{{");
for (auto it = entries.begin(); it != entries.end(); it++) {
const auto entry = *it;
const auto name = std::string_view(entry.key_ptr, entry.key_len);
const auto min = entry.min / 10.0f;
const auto mean = (entry.total / entry.count) / 10.0f;
const auto max = entry.max / 10.0f;
std::print("{}={:.1f}/{:.1f}/{:.1f}", name, min, mean, max);
if (it + 1 != entries.end()) {
std::print(",");
}
}
std::print("}}");
}