diff --git a/flamegraphs/11-multithreading-single-memory-space/flamegraph.svg b/flamegraphs/11-multithreading-single-memory-space/flamegraph.svg
new file mode 100644
index 0000000..09579b5
--- /dev/null
+++ b/flamegraphs/11-multithreading-single-memory-space/flamegraph.svg
@@ -0,0 +1,491 @@
+Flame Graph Reset Zoom Search [unknown] (28 samples, 0.03%) [unknown] (20 samples, 0.02%) [unknown] (13 samples, 0.01%) core::cell::Cell<T>::set (16 samples, 0.02%) core::cell::Cell<T>::replace (16 samples, 0.02%) core::mem::replace (16 samples, 0.02%) core::ptr::read (16 samples, 0.02%) <crossbeam_channel::select::Selected as core::convert::From<usize>>::from (42 samples, 0.04%) __sched_yield (40 samples, 0.04%) [unknown] (39 samples, 0.04%) [unknown] (37 samples, 0.04%) [unknown] (37 samples, 0.04%) [unknown] (37 samples, 0.04%) [unknown] (34 samples, 0.04%) [unknown] (29 samples, 0.03%) [unknown] (24 samples, 0.03%) [unknown] (19 samples, 0.02%) [unknown] (14 samples, 0.01%) core::hint::spin_loop (242 samples, 0.26%) core::core_arch::x86::sse2::_mm_pause (242 samples, 0.26%) crossbeam_utils::backoff::Backoff::snooze (285 samples, 0.30%) crossbeam_channel::context::Context::with::_{{closure}} (398 samples, 0.42%) crossbeam_channel::context::Context::with::_{{closure}} (382 samples, 0.40%) crossbeam_channel::flavors::array::Channel<T>::recv::_{{closure}} (378 samples, 0.40%) crossbeam_channel::context::Context::wait_until (376 samples, 0.40%) std::thread::park (42 samples, 0.04%) syscall (14 samples, 0.01%) [unknown] (14 samples, 0.01%) [unknown] (14 samples, 0.01%) [unknown] (14 samples, 0.01%) [unknown] (14 samples, 0.01%) [unknown] (14 samples, 0.01%) [unknown] (13 samples, 0.01%) [unknown] (10 samples, 0.01%) crossbeam_channel::context::Context::with (399 samples, 0.42%) std::thread::local::LocalKey<T>::try_with (399 samples, 0.42%) crossbeam_channel::flavors::array::Channel<T>::read (16 samples, 0.02%) crossbeam_channel::waker::SyncWaker::notify (15 samples, 0.02%) crossbeam_channel::flavors::array::Channel<T>::start_recv (98 samples, 0.10%) core::sync::atomic::AtomicUsize::load (48 samples, 0.05%) core::sync::atomic::atomic_load (48 samples, 0.05%) __sched_yield (63 samples, 0.07%) [unknown] (61 samples, 0.06%) [unknown] (59 samples, 0.06%) [unknown] (54 samples, 0.06%) [unknown] (53 samples, 0.06%) [unknown] (42 samples, 0.04%) [unknown] (38 samples, 0.04%) [unknown] (30 samples, 0.03%) [unknown] (24 samples, 0.03%) [unknown] (22 samples, 0.02%) [unknown] (10 samples, 0.01%) core::iter::range::<impl core::iter::traits::iterator::Iterator for core::ops::range::Range<A>>::next (350 samples, 0.37%) <core::ops::range::Range<T> as core::iter::range::RangeIteratorImpl>::spec_next (350 samples, 0.37%) core::cmp::impls::<impl core::cmp::PartialOrd for i32>::lt (331 samples, 0.35%) <crossbeam_channel::channel::IntoIter<T> as core::iter::traits::iterator::Iterator>::next (941 samples, 0.99%) crossbeam_channel::channel::Receiver<T>::recv (941 samples, 0.99%) crossbeam_channel::flavors::array::Channel<T>::recv (940 samples, 0.99%) crossbeam_utils::backoff::Backoff::snooze (419 samples, 0.44%) [libc.so.6] (16 samples, 0.02%) [libc.so.6] (96 samples, 0.10%) cfree (150 samples, 0.16%) __lll_lock_wake_private (48 samples, 0.05%) [unknown] (48 samples, 0.05%) [unknown] (46 samples, 0.05%) [unknown] (42 samples, 0.04%) [unknown] (38 samples, 0.04%) [unknown] (36 samples, 0.04%) core::ptr::drop_in_place<alloc::boxed::Box<[u8]>> (152 samples, 0.16%) <alloc::boxed::Box<T,A> as core::ops::drop::Drop>::drop (152 samples, 0.16%) <alloc::alloc::Global as core::alloc::Allocator>::deallocate (151 samples, 0.16%) alloc::alloc::dealloc (151 samples, 0.16%) __rdl_alloc (528 samples, 0.56%) __rust_alloc (522 samples, 0.55%) core::ptr::read_volatile (40 samples, 0.04%) alloc::raw_vec::RawVec<T>::with_capacity (10,631 samples, 11.21%) alloc::raw_vec::.. alloc::raw_vec::RawVec<T,A>::with_capacity_in (10,631 samples, 11.21%) alloc::raw_vec::.. alloc::raw_vec::RawVec<T,A>::allocate_in (10,631 samples, 11.21%) alloc::raw_vec::.. <alloc::alloc::Global as core::alloc::Allocator>::allocate (10,610 samples, 11.19%) <alloc::alloc::G.. alloc::alloc::Global::alloc_impl (10,610 samples, 11.19%) alloc::alloc::Gl.. alloc::alloc::alloc (10,610 samples, 11.19%) alloc::alloc::al.. malloc (7,799 samples, 8.22%) malloc <T as core::convert::Into<U>>::into (13,731 samples, 14.48%) <T as core::convert::I.. <alloc::boxed::Box<[T]> as core::convert::From<&[T]>>::from (13,731 samples, 14.48%) <alloc::boxed::Box<[T].. <alloc::boxed::Box<[T]> as alloc::boxed::BoxFromSlice<T>>::from_slice (13,731 samples, 14.48%) <alloc::boxed::Box<[T].. core::intrinsics::copy_nonoverlapping (3,100 samples, 3.27%) cor.. [libc.so.6] (3,083 samples, 3.25%) [li.. <core::ops::range::Range<usize> as core::slice::index::SliceIndex<[T]>>::index (778 samples, 0.82%) core::slice::index::<impl core::ops::index::Index<I> for [T]>::index (1,816 samples, 1.91%) c.. <core::ops::range::RangeFrom<usize> as core::slice::index::SliceIndex<[T]>>::index (1,038 samples, 1.09%) <f32 as core::ops::arith::Div>::div (101 samples, 0.11%) <f32 as fast_float::float::Float>::from_u64 (770 samples, 0.81%) <f32 as fast_float::float::Float>::pow10_fast_path (22 samples, 0.02%) fast_float::number::Number::try_fast_path (2,835 samples, 2.99%) fas.. fast_float::number::Number::is_fast_path (544 samples, 0.57%) fast_float::common::AsciiStr::check_first (775 samples, 0.82%) fast_float::common::AsciiStr::is_empty (720 samples, 0.76%) fast_float::common::AsciiStr::check_first_either (271 samples, 0.29%) fast_float::common::AsciiStr::is_empty (239 samples, 0.25%) fast_float::common::AsciiStr::first (35 samples, 0.04%) fast_float::common::AsciiStr::offset_from (12 samples, 0.01%) core::num::<impl isize>::wrapping_sub (12 samples, 0.01%) fast_float::number::try_parse_8digits_le (555 samples, 0.59%) fast_float::common::AsciiStr::try_read_u64 (555 samples, 0.59%) fast_float::common::AsciiStr::check_len (326 samples, 0.34%) core::ptr::const_ptr::<impl *const T>::add (15 samples, 0.02%) core::num::<impl u8>::is_ascii_digit (1,284 samples, 1.35%) fast_float::common::AsciiStr::first (209 samples, 0.22%) fast_float::common::AsciiStr::is_empty (743 samples, 0.78%) fast_float::common::AsciiStr::step (254 samples, 0.27%) fast_float::common::AsciiStr::step_by (254 samples, 0.27%) core::ptr::const_ptr::<impl *const T>::add (254 samples, 0.27%) fast_float::common::AsciiStr::parse_digits (4,340 samples, 4.58%) fast_.. fast_float::number::try_parse_digits::_{{closure}} (652 samples, 0.69%) core::num::<impl u64>::wrapping_add (289 samples, 0.30%) fast_float::number::parse_number (12,725 samples, 13.42%) fast_float::number::.. fast_float::number::try_parse_digits (4,379 samples, 4.62%) fast_.. rust_1brc::process_chunk (39 samples, 0.04%) fast_float::parse (16,484 samples, 17.38%) fast_float::parse fast_float::FastFloat::parse_float (16,484 samples, 17.38%) fast_float::FastFloat::pars.. fast_float::FastFloat::parse_float_partial (16,484 samples, 17.38%) fast_float::FastFloat::pars.. fast_float::parse::parse_float (16,484 samples, 17.38%) fast_float::parse::parse_fl.. rust_1brc::process_chunk (372 samples, 0.39%) <*const T as memchr::ext::Pointer>::distance (23 samples, 0.02%) core::ptr::const_ptr::<impl *const T>::offset_from (23 samples, 0.02%) <core::option::Option<T> as core::ops::try_trait::Try>::branch (186 samples, 0.20%) core::sync::atomic::AtomicPtr<T>::load (112 samples, 0.12%) core::sync::atomic::atomic_load (112 samples, 0.12%) <*const T as memchr::ext::Pointer>::distance (61 samples, 0.06%) core::ptr::const_ptr::<impl *const T>::offset_from (61 samples, 0.06%) <memchr::vector::SensibleMoveMask as memchr::vector::MoveMask>::has_non_zero (19 samples, 0.02%) memchr::vector::x86sse2::<impl memchr::vector::Vector for core::core_arch::x86::__m128i>::cmpeq (580 samples, 0.61%) core::core_arch::x86::sse2::_mm_cmpeq_epi8 (580 samples, 0.61%) memchr::arch::generic::memchr::One<V>::search_chunk (4,757 samples, 5.02%) memchr.. memchr::vector::x86sse2::<impl memchr::vector::Vector for core::core_arch::x86::__m128i>::movemask (1,221 samples, 1.29%) core::core_arch::x86::sse2::_mm_movemask_epi8 (1,221 samples, 1.29%) memchr::vector::Vector::movemask_will_have_non_zero (12 samples, 0.01%) memchr::vector::x86sse2::<impl memchr::vector::Vector for core::core_arch::x86::__m128i>::movemask (12 samples, 0.01%) core::core_arch::x86::sse2::_mm_movemask_epi8 (12 samples, 0.01%) core::core_arch::x86::sse2::_mm_cmpeq_epi8 (55 samples, 0.06%) memchr::vector::x86sse2::<impl memchr::vector::Vector for core::core_arch::x86::__m128i>::cmpeq (102 samples, 0.11%) memchr::arch::x86_64::memchr::memchr_raw::find_sse2 (47 samples, 0.05%) memchr::arch::generic::memchr::search_slice_with_raw (8,917 samples, 9.40%) memchr::arch:.. memchr::memchr::memchr::_{{closure}} (8,675 samples, 9.15%) memchr::memch.. memchr::memchr::memchr_raw (8,675 samples, 9.15%) memchr::memch.. memchr::arch::x86_64::memchr::memchr_raw (8,675 samples, 9.15%) memchr::arch:.. memchr::arch::x86_64::memchr::memchr_raw::find_sse2 (8,085 samples, 8.52%) memchr::arch.. memchr::arch::x86_64::sse2::memchr::One::find_raw (5,490 samples, 5.79%) memchr:.. memchr::arch::x86_64::sse2::memchr::One::find_raw_impl (5,036 samples, 5.31%) memchr.. memchr::arch::generic::memchr::One<V>::find_raw (5,036 samples, 5.31%) memchr.. memchr::vector::x86sse2::<impl memchr::vector::Vector for core::core_arch::x86::__m128i>::or (20 samples, 0.02%) core::core_arch::x86::sse2::_mm_or_si128 (20 samples, 0.02%) memchr::memchr::memchr (8,920 samples, 9.40%) memchr::memch.. std::collections::hash::map::Entry<K,V>::and_modify (1,184 samples, 1.25%) rust_1brc::process_chunk::_{{closure}} (1,184 samples, 1.25%) __rdl_dealloc (461 samples, 0.49%) __rust_dealloc (1,032 samples, 1.09%) [libc.so.6] (5,061 samples, 5.34%) [libc.s.. std::collections::hash::map::OccupiedEntry<K,V>::into_mut (9,493 samples, 10.01%) std::collectio.. hashbrown::rustc_entry::RustcOccupiedEntry<K,V,A>::into_mut (9,493 samples, 10.01%) hashbrown::rus.. core::ptr::drop_in_place<hashbrown::rustc_entry::RustcOccupiedEntry<alloc::boxed::Box<[u8]>,rust_1brc::StationValues>> (9,493 samples, 10.01%) core::ptr::dro.. core::ptr::drop_in_place<core::option::Option<alloc::boxed::Box<[u8]>>> (9,493 samples, 10.01%) core::ptr::dro.. core::ptr::drop_in_place<alloc::boxed::Box<[u8]>> (9,474 samples, 9.99%) core::ptr::dro.. <alloc::boxed::Box<T,A> as core::ops::drop::Drop>::drop (9,474 samples, 9.99%) <alloc::boxed:.. <alloc::alloc::Global as core::alloc::Allocator>::deallocate (9,474 samples, 9.99%) <alloc::alloc:.. alloc::alloc::dealloc (9,474 samples, 9.99%) alloc::alloc::.. cfree (7,981 samples, 8.41%) cfree std::collections::hash::map::Entry<K,V>::or_insert (9,497 samples, 10.01%) std::collectio.. hashbrown::map::HashMap<K,V,S,A>::reserve (12 samples, 0.01%) hashbrown::raw::RawTable<T,A>::reserve (12 samples, 0.01%) hashbrown::raw::RawTable<T,A>::reserve_rehash (11 samples, 0.01%) hashbrown::raw::RawTableInner::reserve_rehash_inner (10 samples, 0.01%) hashbrown::raw::RawTableInner::resize_inner (10 samples, 0.01%) core::hash::Hasher::write_length_prefix (67 samples, 0.07%) <rustc_hash::FxHasher as core::hash::Hasher>::write_usize (67 samples, 0.07%) rustc_hash::FxHasher::add_to_hash (67 samples, 0.07%) core::num::<impl usize>::wrapping_mul (67 samples, 0.07%) core::slice::index::<impl core::ops::index::Index<I> for [T]>::index (505 samples, 0.53%) <core::ops::range::RangeFrom<usize> as core::slice::index::SliceIndex<[T]>>::index (505 samples, 0.53%) <core::ops::range::RangeFrom<usize> as core::slice::index::SliceIndex<[T]>>::get_unchecked (505 samples, 0.53%) <core::ops::range::Range<usize> as core::slice::index::SliceIndex<[T]>>::get_unchecked (505 samples, 0.53%) core::ptr::const_ptr::<impl *const T>::add (11 samples, 0.01%) <usize as core::ops::bit::BitXor>::bitxor (340 samples, 0.36%) core::num::<impl usize>::rotate_left (2,332 samples, 2.46%) co.. <rustc_hash::FxHasher as core::hash::Hasher>::write (8,172 samples, 8.62%) <rustc_hash:.. rustc_hash::FxHasher::add_to_hash (5,256 samples, 5.54%) rustc_h.. core::num::<impl usize>::wrapping_mul (2,584 samples, 2.72%) co.. hashbrown::map::make_hash (8,263 samples, 8.71%) hashbrown::m.. core::hash::BuildHasher::hash_one (8,263 samples, 8.71%) core::hash::.. core::hash::impls::<impl core::hash::Hash for &T>::hash (8,263 samples, 8.71%) core::hash::.. <alloc::boxed::Box<T,A> as core::hash::Hash>::hash (8,263 samples, 8.71%) <alloc::boxe.. core::hash::impls::<impl core::hash::Hash for [T]>::hash (8,263 samples, 8.71%) core::hash::.. core::hash::impls::<impl core::hash::Hash for u8>::hash_slice (8,196 samples, 8.64%) core::hash::.. hashbrown::rustc_entry::<impl hashbrown::map::HashMap<K,V,S,A>>::rustc_entry (24 samples, 0.03%) core::num::nonzero::NonZero<u16>::new (60 samples, 0.06%) <hashbrown::raw::bitmask::BitMaskIter as core::iter::traits::iterator::Iterator>::next (69 samples, 0.07%) hashbrown::raw::bitmask::BitMask::lowest_set_bit (69 samples, 0.07%) hashbrown::raw::RawTable<T,A>::bucket (877 samples, 0.92%) hashbrown::raw::Bucket<T>::from_base_index (877 samples, 0.92%) core::ptr::mut_ptr::<impl *mut T>::sub (877 samples, 0.92%) core::ptr::mut_ptr::<impl *mut T>::offset (877 samples, 0.92%) [libc.so.6] (8,491 samples, 8.95%) [libc.so.6] <[A] as core::slice::cmp::SlicePartialEq<B>>::equal (13,510 samples, 14.24%) <[A] as core::slice::c.. hashbrown::raw::RawTable<T,A>::find::_{{closure}} (14,398 samples, 15.18%) hashbrown::raw::RawTabl.. hashbrown::rustc_entry::_<impl hashbrown::map::HashMap<K,V,S,A>>::rustc_entry::_{{closure}} (13,518 samples, 14.25%) hashbrown::rustc_entry.. <alloc::boxed::Box<T,A> as core::cmp::PartialEq>::eq (13,518 samples, 14.25%) <alloc::boxed::Box<T,A.. core::slice::cmp::<impl core::cmp::PartialEq<[B]> for [A]>::eq (13,518 samples, 14.25%) core::slice::cmp::<imp.. hashbrown::raw::h2 (2,871 samples, 3.03%) has.. core::intrinsics::copy_nonoverlapping (20 samples, 0.02%) hashbrown::raw::sse2::Group::load (482 samples, 0.51%) core::core_arch::x86::sse2::_mm_loadu_si128 (482 samples, 0.51%) hashbrown::rustc_entry::<impl hashbrown::map::HashMap<K,V,S,A>>::rustc_entry (462 samples, 0.49%) hashbrown::raw::sse2::Group::match_byte (4,412 samples, 4.65%) hashb.. core::core_arch::x86::sse2::_mm_movemask_epi8 (4,412 samples, 4.65%) core:.. hashbrown::raw::sse2::Group::match_empty (416 samples, 0.44%) hashbrown::raw::sse2::Group::match_byte (416 samples, 0.44%) core::core_arch::x86::sse2::_mm_movemask_epi8 (416 samples, 0.44%) hashbrown::raw::RawTableInner::find_inner (25,613 samples, 27.00%) hashbrown::raw::RawTableInner::find_inner hashbrown::rustc_entry::<impl hashbrown::map::HashMap<K,V,S,A>>::rustc_entry (48 samples, 0.05%) hashbrown::rustc_entry::<impl hashbrown::map::HashMap<K,V,S,A>>::rustc_entry (38,377 samples, 40.46%) hashbrown::rustc_entry::<impl hashbrown::map::HashMap<K,V,S,A>>::r.. hashbrown::raw::RawTable<T,A>::find (25,879 samples, 27.28%) hashbrown::raw::RawTable<T,A>::find rust_1brc::process_chunk (66 samples, 0.07%) std::panic::catch_unwind (91,854 samples, 96.84%) std::panic::catch_unwind std::panicking::try (91,854 samples, 96.84%) std::panicking::try std::panicking::try::do_call (91,854 samples, 96.84%) std::panicking::try::do_call <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once (91,854 samples, 96.84%) <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once std::thread::Builder::spawn_unchecked_::_{{closure}}::_{{closure}} (91,854 samples, 96.84%) std::thread::Builder::spawn_unchecked_::_{{closure}}::_{{closure}} std::sys_common::backtrace::__rust_begin_short_backtrace (91,854 samples, 96.84%) std::sys_common::backtrace::__rust_begin_short_backtrace rust_1brc::calculate_station_values::_{{closure}} (91,854 samples, 96.84%) rust_1brc::calculate_station_values::_{{closure}} rust_1brc::process_chunk (90,753 samples, 95.68%) rust_1brc::process_chunk std::collections::hash::map::HashMap<K,V,S>::entry (38,781 samples, 40.89%) std::collections::hash::map::HashMap<K,V,S>::entry std::collections::hash::map::map_entry (149 samples, 0.16%) core::ops::function::FnOnce::call_once{{vtable.shim}} (91,865 samples, 96.85%) core::ops::function::FnOnce::call_once{{vtable.shim}} std::thread::Builder::spawn_unchecked_::_{{closure}} (91,865 samples, 96.85%) std::thread::Builder::spawn_unchecked_::_{{closure}} [libc.so.6] (91,929 samples, 96.92%) [libc.so.6] std::sys::pal::unix::thread::Thread::new::thread_start (91,891 samples, 96.88%) std::sys::pal::unix::thread::Thread::new::thread_start std::sys::pal::unix::stack_overflow::imp::make_handler (26 samples, 0.03%) sigaltstack (16 samples, 0.02%) [unknown] (15 samples, 0.02%) [unknown] (13 samples, 0.01%) [unknown] (11 samples, 0.01%) [libc.so.6] (91,930 samples, 96.92%) [libc.so.6] [unknown] (23 samples, 0.02%) [libc.so.6] (18 samples, 0.02%) [libc.so.6] (76 samples, 0.08%) alloc::raw_vec::RawVec<T>::with_capacity (99 samples, 0.10%) alloc::raw_vec::RawVec<T,A>::with_capacity_in (99 samples, 0.10%) alloc::raw_vec::RawVec<T,A>::allocate_in (99 samples, 0.10%) <alloc::alloc::Global as core::alloc::Allocator>::allocate (99 samples, 0.10%) alloc::alloc::Global::alloc_impl (99 samples, 0.10%) alloc::alloc::alloc (99 samples, 0.10%) malloc (94 samples, 0.10%) [libc.so.6] (2,523 samples, 2.66%) [l.. [unknown] (279 samples, 0.29%) [unknown] (120 samples, 0.13%) [unknown] (108 samples, 0.11%) [unknown] (80 samples, 0.08%) [unknown] (33 samples, 0.03%) <alloc::boxed::Box<[T]> as core::convert::From<&[T]>>::from (2,631 samples, 2.77%) <a.. <alloc::boxed::Box<[T]> as alloc::boxed::BoxFromSlice<T>>::from_slice (2,631 samples, 2.77%) <a.. core::intrinsics::copy_nonoverlapping (2,532 samples, 2.67%) co.. <std::fs::File as std::io::Read>::read (102 samples, 0.11%) read (97 samples, 0.10%) [unknown] (94 samples, 0.10%) [unknown] (87 samples, 0.09%) [unknown] (83 samples, 0.09%) [unknown] (83 samples, 0.09%) [unknown] (77 samples, 0.08%) [unknown] (46 samples, 0.05%) [unknown] (38 samples, 0.04%) [unknown] (16 samples, 0.02%) crossbeam_channel::flavors::array::Channel<T>::start_send (20 samples, 0.02%) <core::slice::iter::Iter<T> as core::iter::traits::iterator::Iterator>::position (91 samples, 0.10%) crossbeam_channel::waker::Waker::try_select::_{{closure}} (91 samples, 0.10%) crossbeam_channel::context::Context::unpark (83 samples, 0.09%) std::thread::Thread::unpark (83 samples, 0.09%) std::sys_common::thread_parking::futex::Parker::unpark (82 samples, 0.09%) std::sys::pal::unix::futex::futex_wake (80 samples, 0.08%) syscall (80 samples, 0.08%) [unknown] (79 samples, 0.08%) [unknown] (73 samples, 0.08%) [unknown] (58 samples, 0.06%) [unknown] (54 samples, 0.06%) [unknown] (48 samples, 0.05%) crossbeam_channel::waker::Waker::try_select (93 samples, 0.10%) crossbeam_channel::flavors::array::Channel<T>::write (96 samples, 0.10%) crossbeam_channel::waker::SyncWaker::notify (96 samples, 0.10%) crossbeam_channel::channel::Sender<T>::send (130 samples, 0.14%) crossbeam_channel::flavors::array::Channel<T>::send (126 samples, 0.13%) rust_1brc::calculate_station_values (2,878 samples, 3.03%) rus.. all (94,853 samples, 100%) rust-1brc (94,853 samples, 100.00%) rust-1brc _start (2,879 samples, 3.04%) _st.. __libc_start_main (2,879 samples, 3.04%) __l.. [libc.so.6] (2,879 samples, 3.04%) [li.. main (2,879 samples, 3.04%) main std::rt::lang_start_internal (2,879 samples, 3.04%) std.. std::rt::lang_start::_{{closure}} (2,879 samples, 3.04%) std.. std::sys_common::backtrace::__rust_begin_short_backtrace (2,879 samples, 3.04%) std.. core::ops::function::FnOnce::call_once (2,879 samples, 3.04%) cor.. rust_1brc::main (2,879 samples, 3.04%) rus..
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 29303b2..ffcdc89 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -121,57 +121,35 @@ fn calculate_station_values(mut file: std::fs::File) -> FxHashMap, Sta
// Read the file in chunks and send the chunks to the processor threads
let mut buf = vec![0; READ_BUF_SIZE];
- let mut unprocessed_buffer: Vec = Vec::new();
+ let mut bytes_not_processed = 0;
loop {
- let bytes_read = file.read(&mut buf[..]).expect("Failed to read file");
- // println!("bytes_Read {:?}", bytes_read);
+ let bytes_read = file.read(&mut buf[bytes_not_processed..]).expect("Failed to read file");
if bytes_read == 0 {
break;
}
- let actual_buf = &mut buf[..bytes_read];
+ let actual_buf = &mut buf[..bytes_not_processed+bytes_read];
let last_new_line_index = match find_new_line_pos(&actual_buf) {
Some(index) => index,
None => {
- // No newline found in the buffer. Store all the bytes in unprocessed_buffer
- // and continue reading the file
- // TODO: handle this case
- unprocessed_buffer.append(&mut actual_buf.to_owned());
- continue;
+ println!("No new line found in the read buffer");
+ bytes_not_processed += bytes_read;
+ if bytes_not_processed == buf.len(){
+ panic!("No new line found in the read buffer");
+ }
+ continue; // try again, maybe we next read will have a newline
}
};
- if bytes_read == last_new_line_index + 1 {
- // If the buffer is full, then we can safely assume that the last byte is a newline
- // and we can process the buffer
-
- if unprocessed_buffer.len() != 0 {
- unprocessed_buffer.append(&mut actual_buf[..(last_new_line_index + 1)].to_owned());
- let buf_boxed = Box::<[u8]>::from(&unprocessed_buffer[..]);
- sender.send(buf_boxed).expect("Failed to send buffer");
- unprocessed_buffer.clear();
- } else {
- let buf_boxed = Box::<[u8]>::from(&actual_buf[..(last_new_line_index + 1)]);
- sender.send(buf_boxed).expect("Failed to send buffer");
- }
- } else {
- // If the buffer is not full, then we can't assume that the last byte is a newline
- // We need to store the bytes that are not processed in unprocessed_buffer
- // and continue reading the file
+ let buf_boxed = Box::<[u8]>::from(&actual_buf[..(last_new_line_index + 1)]);
+ sender.send(buf_boxed).expect("Failed to send buffer");
- // Send chunk till last new line
- if unprocessed_buffer.len() != 0 {
- unprocessed_buffer.append(&mut actual_buf[..(last_new_line_index + 1)].to_owned());
- let buf_boxed = Box::<[u8]>::from(&unprocessed_buffer[..]);
- sender.send(buf_boxed).expect("Failed to send buffer");
- unprocessed_buffer.clear();
- unprocessed_buffer.append(&mut actual_buf[(last_new_line_index + 1)..].to_vec());
- } else {
- let buf_boxed = Box::<[u8]>::from(&actual_buf[..(last_new_line_index + 1)]);
- sender.send(buf_boxed).expect("Failed to send buffer");
- unprocessed_buffer.append(&mut actual_buf[(last_new_line_index + 1)..].to_vec());
- }
- }
+ actual_buf.copy_within(last_new_line_index+1.., 0);
+ // You cannot use bytes_not_processed = bytes_read - last_new_line_index
+ // - 1; because the buffer will contain unprocessed bytes from the
+ // previous iteration and the new line index will be calculated from the
+ // start of the buffer
+ bytes_not_processed = actual_buf.len() - last_new_line_index - 1;
}
drop(sender);