Skip to content

Commit 979d2a2

Browse files
[runtime/iobuf/pool] improve buffer pool defaults (#3860)
Co-authored-by: Patrick O'Grady <me@patrickogrady.xyz>
1 parent a7e8f48 commit 979d2a2

5 files changed

Lines changed: 66 additions & 89 deletions

File tree

runtime/src/iobuf/benches/pool.rs

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
4040
use super::utils::{measure, Threading};
4141
use commonware_runtime::{
42-
tokio, BufferPool, BufferPoolConfig, BufferPooler, IoBufMut, Runner as _,
42+
page_size, tokio, BufferPool, BufferPoolConfig, BufferPooler, IoBufMut, Runner as _,
4343
};
4444
use commonware_utils::{NZUsize, NZU32};
4545
use criterion::Criterion;
@@ -228,22 +228,3 @@ fn build_pool(size: usize, threads: usize) -> BufferPool {
228228

229229
tokio::Runner::new(runner_cfg).start(|ctx| async move { ctx.network_buffer_pool().clone() })
230230
}
231-
232-
#[allow(clippy::missing_const_for_fn)]
233-
fn page_size() -> usize {
234-
#[cfg(unix)]
235-
{
236-
// SAFETY: sysconf is safe to call.
237-
let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
238-
if size <= 0 {
239-
4096
240-
} else {
241-
size as usize
242-
}
243-
}
244-
245-
#[cfg(not(unix))]
246-
{
247-
4096
248-
}
249-
}

runtime/src/iobuf/buffer.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -830,17 +830,16 @@ impl BufMut<PooledBacking> {
830830
mod tests {
831831
use super::*;
832832
use crate::{
833-
iobuf::pool::{BufferPool, BufferPoolConfig, BufferPoolThreadCacheConfig},
833+
iobuf::{
834+
cache_line_size, page_size, pool::BufferPoolThreadCacheConfig, BufferPool,
835+
BufferPoolConfig,
836+
},
834837
telemetry::metrics::Registry,
835838
};
836839
use bytes::{Buf, BufMut, Bytes, BytesMut};
837840
use commonware_utils::{NZUsize, NZU32};
838841
use std::ops::Bound;
839842

840-
fn page_size() -> usize {
841-
BufferPoolConfig::for_storage().min_size.get()
842-
}
843-
844843
fn test_pool(config: BufferPoolConfig) -> BufferPool {
845844
let mut registry = Registry::default();
846845
BufferPool::new(config, &mut registry)
@@ -868,7 +867,7 @@ mod tests {
868867
assert!((buf.as_ptr() as usize).is_multiple_of(page));
869868

870869
// Cache-line-aligned allocation should also satisfy its alignment.
871-
let cache_line = BufferPoolConfig::for_network().alignment.get();
870+
let cache_line = cache_line_size();
872871
let buf2 = AlignedBuffer::new(4096, cache_line);
873872
assert_eq!(buf2.capacity(), 4096);
874873
assert!((buf2.as_ptr() as usize).is_multiple_of(cache_line));
@@ -1319,7 +1318,7 @@ mod tests {
13191318
fn test_alignment_after_advance() {
13201319
// Advancing breaks base-pointer alignment, which is expected.
13211320
let page = page_size();
1322-
let pool = test_pool(BufferPoolConfig::for_storage());
1321+
let pool = test_pool(BufferPoolConfig::for_storage().with_alignment(NZUsize!(page)));
13231322

13241323
let mut buf = pool.try_alloc(100).unwrap();
13251324
buf.put_slice(&[0; 100]);

runtime/src/iobuf/mod.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,37 @@ pub(crate) use buffer::AlignedBuffer;
2222
use buffer::{AlignedBuf, AlignedBufMut, PooledBuf, PooledBufMut};
2323
use bytes::{Buf, BufMut, Bytes, BytesMut};
2424
use commonware_codec::{util::at_least, BufsMut, EncodeSize, Error, RangeCfg, Read, Write};
25+
use crossbeam_utils::CachePadded;
2526
pub use pool::{BufferPool, BufferPoolConfig, BufferPoolThreadCache, PoolError};
26-
use std::{collections::VecDeque, io::IoSlice, num::NonZeroUsize, ops::RangeBounds};
27+
use std::{collections::VecDeque, io::IoSlice, mem::align_of, num::NonZeroUsize, ops::RangeBounds};
28+
29+
/// Returns the system page size.
30+
///
31+
/// On Unix systems, queries the actual page size via `sysconf`.
32+
/// On other systems (Windows), defaults to 4KB.
33+
#[allow(clippy::missing_const_for_fn)]
34+
pub fn page_size() -> usize {
35+
#[cfg(unix)]
36+
{
37+
// SAFETY: sysconf is safe to call.
38+
let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
39+
if size <= 0 {
40+
4096 // Safe fallback if sysconf fails
41+
} else {
42+
size as usize
43+
}
44+
}
45+
46+
#[cfg(not(unix))]
47+
{
48+
4096
49+
}
50+
}
51+
52+
/// Returns the cache line size for the current architecture.
53+
pub const fn cache_line_size() -> usize {
54+
align_of::<CachePadded<u8>>()
55+
}
2756

2857
#[cfg(feature = "bench")]
2958
pub mod bench {

runtime/src/iobuf/pool.rs

Lines changed: 27 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,16 @@
4949
//! first try to re-enter the dropping thread's local cache, spilling a bounded
5050
//! batch back to the global freelist if needed.
5151
52-
use super::{freelist::Freelist, IoBufMut};
52+
use super::{freelist::Freelist, page_size, IoBufMut};
5353
use crate::{
5454
iobuf::buffer::{PooledBufMut, PooledBuffer},
5555
telemetry::metrics::{raw, Counter, CounterFamily, EncodeLabelSet, GaugeFamily, Register},
5656
};
5757
use commonware_utils::{NZUsize, NZU32};
58-
use crossbeam_utils::CachePadded;
5958
use std::{
6059
alloc::Layout,
6160
cell::{Cell, UnsafeCell},
62-
mem::{align_of, MaybeUninit},
61+
mem::MaybeUninit,
6362
num::{NonZeroU32, NonZeroUsize},
6463
ptr,
6564
sync::{
@@ -95,35 +94,6 @@ impl std::fmt::Display for PoolError {
9594

9695
impl std::error::Error for PoolError {}
9796

98-
/// Returns the system page size.
99-
///
100-
/// On Unix systems, queries the actual page size via `sysconf`.
101-
/// On other systems (Windows), defaults to 4KB.
102-
#[cfg(unix)]
103-
fn page_size() -> usize {
104-
// SAFETY: sysconf is safe to call.
105-
let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
106-
if size <= 0 {
107-
4096 // Safe fallback if sysconf fails
108-
} else {
109-
size as usize
110-
}
111-
}
112-
113-
#[cfg(not(unix))]
114-
#[allow(clippy::missing_const_for_fn)]
115-
fn page_size() -> usize {
116-
4096
117-
}
118-
119-
/// Returns the cache line size for the current architecture.
120-
///
121-
/// Matches the architecture-specific alignment used by
122-
/// [`crossbeam_utils::CachePadded`].
123-
const fn cache_line_size() -> usize {
124-
align_of::<CachePadded<u8>>()
125-
}
126-
12797
/// Policy for sizing each thread's cache within a buffer pool size class.
12898
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12999
pub(crate) enum BufferPoolThreadCacheConfig {
@@ -184,40 +154,36 @@ pub struct BufferPoolConfig {
184154
}
185155

186156
impl BufferPoolConfig {
187-
/// Network I/O preset: cache-line aligned, 1KB to 64KB buffers,
188-
/// 4096 per class, not prefilled.
157+
/// Network I/O preset: 1KB to 128KB buffers, 4096 per class, not prefilled.
189158
///
190-
/// Network operations typically need multiple concurrent buffers per connection
191-
/// (message, encoding, encryption) so we allow 4096 buffers per size class.
192-
/// Cache-line alignment is used because network buffers don't require page
193-
/// alignment for DMA, and smaller alignment reduces internal fragmentation.
159+
/// Network operations typically need multiple concurrent buffers per
160+
/// connection (message, encoding, encryption) so we allow 4096 buffers per
161+
/// size class.
194162
pub const fn for_network() -> Self {
195-
let cache_line = NZUsize!(cache_line_size());
196163
Self {
197-
pool_min_size: 1024,
164+
pool_min_size: 0,
198165
min_size: NZUsize!(1024),
199-
max_size: NZUsize!(64 * 1024),
166+
max_size: NZUsize!(128 * 1024),
200167
max_per_class: NZU32!(4096),
201168
prefill: false,
202-
alignment: cache_line,
169+
alignment: NZUsize!(1),
203170
parallelism: NZUsize!(1),
204171
thread_cache_config: BufferPoolThreadCacheConfig::Enabled(None),
205172
}
206173
}
207174

208-
/// Storage I/O preset: page-aligned, page_size to 8MB buffers, 64 per class,
175+
/// Storage I/O preset: `page_size` (usually 4KB) to 8MB buffers, 64 per class,
209176
/// not prefilled.
210-
///
211-
/// Page alignment is required for direct I/O and efficient DMA transfers.
212177
pub fn for_storage() -> Self {
213178
let page = NZUsize!(page_size());
214179
Self {
215-
pool_min_size: 1024,
180+
pool_min_size: 0,
216181
min_size: page,
217182
max_size: NZUsize!(8 * 1024 * 1024),
218183
max_per_class: NZU32!(64),
219184
prefill: false,
220-
alignment: page,
185+
// TODO (#2960): this needs to be page/block aligned for O_DIRECT
186+
alignment: NZUsize!(1),
221187
parallelism: NZUsize!(1),
222188
thread_cache_config: BufferPoolThreadCacheConfig::Enabled(None),
223189
}
@@ -1745,7 +1711,7 @@ impl BufferPool {
17451711
mod tests {
17461712
use super::*;
17471713
use crate::{
1748-
iobuf::{freelist, IoBuf},
1714+
iobuf::{cache_line_size, freelist, IoBuf},
17491715
telemetry::metrics::Registry,
17501716
};
17511717
use bytes::{Buf, BufMut};
@@ -2053,24 +2019,24 @@ mod tests {
20532019
fn test_config_for_network() {
20542020
let config = BufferPoolConfig::for_network();
20552021
config.validate();
2056-
assert_eq!(config.pool_min_size, 1024);
2022+
assert_eq!(config.pool_min_size, 0);
20572023
assert_eq!(config.min_size.get(), 1024);
2058-
assert_eq!(config.max_size.get(), 64 * 1024);
2024+
assert_eq!(config.max_size.get(), 128 * 1024);
20592025
assert_eq!(config.max_per_class.get(), 4096);
20602026
assert_eq!(config.parallelism, NZUsize!(1));
20612027
assert_eq!(
20622028
config.thread_cache_config,
20632029
BufferPoolThreadCacheConfig::Enabled(None)
20642030
);
20652031
assert!(!config.prefill);
2066-
assert_eq!(config.alignment.get(), cache_line_size());
2032+
assert_eq!(config.alignment.get(), 1);
20672033
}
20682034

20692035
#[test]
20702036
fn test_config_for_storage() {
20712037
let config = BufferPoolConfig::for_storage();
20722038
config.validate();
2073-
assert_eq!(config.pool_min_size, 1024);
2039+
assert_eq!(config.pool_min_size, 0);
20742040
assert_eq!(config.min_size.get(), page_size());
20752041
assert_eq!(config.max_size.get(), 8 * 1024 * 1024);
20762042
assert_eq!(config.max_per_class.get(), 64);
@@ -2080,7 +2046,7 @@ mod tests {
20802046
BufferPoolThreadCacheConfig::Enabled(None)
20812047
);
20822048
assert!(!config.prefill);
2083-
assert_eq!(config.alignment.get(), page_size());
2049+
assert_eq!(config.alignment.get(), 1);
20842050
}
20852051

20862052
#[test]
@@ -2115,8 +2081,7 @@ mod tests {
21152081
BufferPoolThreadCacheConfig::Enabled(Some(NZUsize!(8)))
21162082
);
21172083
assert!(config.prefill);
2118-
// Storage profile alignment stays page-sized unless explicitly changed.
2119-
assert_eq!(config.alignment.get(), page_size());
2084+
assert_eq!(config.alignment.get(), 1);
21202085

21212086
// Alignment can be tuned explicitly as long as min_size is also adjusted.
21222087
let aligned = BufferPoolConfig::for_network()
@@ -3142,20 +3107,23 @@ mod tests {
31423107
fn test_buffer_alignment() {
31433108
let page = page_size();
31443109
let cache_line = cache_line_size();
3110+
31453111
// Reduce max_per_class under miri (atomics are slow)
31463112
cfg_if::cfg_if! {
31473113
if #[cfg(miri)] {
31483114
let storage_config = BufferPoolConfig {
31493115
max_per_class: NZU32!(32),
3150-
..BufferPoolConfig::for_storage()
3116+
..BufferPoolConfig::for_storage().with_alignment(NZUsize!(page))
31513117
};
31523118
let network_config = BufferPoolConfig {
31533119
max_per_class: NZU32!(32),
3154-
..BufferPoolConfig::for_network()
3120+
..BufferPoolConfig::for_network().with_alignment(NZUsize!(cache_line))
31553121
};
31563122
} else {
3157-
let storage_config = BufferPoolConfig::for_storage();
3158-
let network_config = BufferPoolConfig::for_network();
3123+
let storage_config =
3124+
BufferPoolConfig::for_storage().with_alignment(NZUsize!(page));
3125+
let network_config =
3126+
BufferPoolConfig::for_network().with_alignment(NZUsize!(cache_line));
31593127
}
31603128
}
31613129

runtime/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ stability_scope!(BETA {
6464

6565
pub mod iobuf;
6666
pub use iobuf::{
67-
BufferPool, BufferPoolConfig, BufferPoolThreadCache, Builder as IoBufsBuilder, IoBuf,
68-
IoBufMut, IoBufs, IoBufsMut,
67+
cache_line_size, page_size, BufferPool, BufferPoolConfig, BufferPoolThreadCache,
68+
Builder as IoBufsBuilder, IoBuf, IoBufMut, IoBufs, IoBufsMut,
6969
};
7070

7171
pub mod utils;

0 commit comments

Comments
 (0)