import std; import io; import map; import tricks; auto calculate_boundaries(std::string_view file, std::size_t thread_count) -> std::inplace_vector { auto bounds = std::inplace_vector(); 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(~word) << 59) >> 63; std::uint64_t sign_mask = ~(static_cast(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(value), dot_byte + 2); } template [[nodiscard]] std::inplace_vector, BatchSize> parse_temp( std::span batch) { auto results = std::inplace_vector, 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: {} ", 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(); auto results = std::vector(); 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("}}"); }