1use std::cmp;
18use std::path::Path;
19use std::time::Duration;
20
21use common_base::memory_limit::MemoryLimit;
22use common_base::readable_size::ReadableSize;
23use common_memory_manager::OnExhaustedPolicy;
24use common_stat::{get_total_cpu_cores, get_total_memory_readable};
25use common_telemetry::warn;
26use serde::{Deserialize, Serialize};
27use serde_with::serde_as;
28
29use crate::cache::file_cache::DEFAULT_INDEX_CACHE_PERCENT;
30use crate::error::Result;
31use crate::gc::GcConfig;
32use crate::sst::DEFAULT_WRITE_BUFFER_SIZE;
33
34const MULTIPART_UPLOAD_MINIMUM_SIZE: ReadableSize = ReadableSize::mb(5);
35const DEFAULT_SERIES_INDEX_MAINTENANCE_INTERVAL: Duration = Duration::from_secs(5 * 60);
36pub(crate) const DEFAULT_MAX_CONCURRENT_SCAN_FILES: usize = 384;
38
39const GLOBAL_WRITE_BUFFER_SIZE_FACTOR: u64 = 8;
41const SST_META_CACHE_SIZE_FACTOR: u64 = 8;
43const PREFILTER_RESULT_CACHE_SIZE_FACTOR: u64 = 32;
45const INDEX_METADATA_CACHE_SIZE_FACTOR: u64 = 32;
47const MEM_CACHE_SIZE_FACTOR: u64 = 16;
49const PAGE_CACHE_SIZE_FACTOR: u64 = 8;
51const INDEX_CREATE_MEM_THRESHOLD_FACTOR: u64 = 16;
53
54pub(crate) const FETCH_OPTION_TIMEOUT: Duration = Duration::from_secs(3);
56
57#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
60#[serde(default)]
61pub struct MitoConfig {
62 pub num_workers: usize,
66 pub worker_channel_size: usize,
68 pub worker_request_batch_size: usize,
70
71 pub manifest_checkpoint_distance: u64,
75 pub experimental_manifest_keep_removed_file_count: usize,
80 #[serde(with = "humantime_serde")]
85 pub experimental_manifest_keep_removed_file_ttl: Duration,
86 pub compress_manifest: bool,
88
89 pub max_background_index_builds: usize,
92 pub experimental_enable_series_index: bool,
95 pub experimental_series_index_max_size: ReadableSize,
99 pub experimental_enable_range_index: bool,
102 #[serde(with = "humantime_serde")]
104 pub experimental_series_index_maintenance_interval: Duration,
105 #[serde(with = "humantime_serde")]
108 pub experimental_series_index_bucket_width: Duration,
109 pub max_background_flushes: usize,
111 pub max_background_compactions: usize,
113 pub max_background_purges: usize,
115 pub experimental_compaction_memory_limit: MemoryLimit,
119 pub experimental_compaction_on_exhausted: OnExhaustedPolicy,
121
122 #[serde(with = "humantime_serde")]
125 pub auto_flush_interval: Duration,
126 pub global_write_buffer_size: ReadableSize,
128 pub global_write_buffer_reject_size: ReadableSize,
130 pub default_region_write_buffer_size: ReadableSize,
134
135 pub sst_meta_cache_size: ReadableSize,
138 pub vector_cache_size: ReadableSize,
140 pub page_cache_size: ReadableSize,
142 pub selector_result_cache_size: ReadableSize,
144 pub range_result_cache_size: ReadableSize,
146 pub prefilter_result_cache_size: ReadableSize,
148 pub enable_write_cache: bool,
150 pub write_cache_path: String,
152 pub write_cache_size: ReadableSize,
154 #[serde(with = "humantime_serde")]
156 pub write_cache_ttl: Option<Duration>,
157 pub preload_index_cache: bool,
159 pub index_cache_percent: u8,
163 pub enable_refill_cache_on_read: bool,
166 pub manifest_cache_size: ReadableSize,
168
169 pub sst_write_buffer_size: ReadableSize,
173 pub max_concurrent_scan_files: usize,
175 pub allow_stale_entries: bool,
177 pub scan_memory_limit: MemoryLimit,
181 pub scan_memory_on_exhausted: OnExhaustedPolicy,
186
187 pub index: IndexConfig,
189 pub inverted_index: InvertedIndexConfig,
191 pub fulltext_index: FulltextIndexConfig,
193 pub bloom_filter_index: BloomFilterConfig,
195
196 #[serde(with = "humantime_serde")]
199 pub min_compaction_interval: Duration,
200 pub schedule_compaction_after_edit: bool,
202
203 pub default_flat_format: bool,
206
207 pub experimental_series_scan_v2: bool,
209
210 pub gc: GcConfig,
211}
212
213impl Default for MitoConfig {
214 fn default() -> Self {
215 let mut mito_config = MitoConfig {
216 num_workers: divide_num_cpus(2),
217 worker_channel_size: 128,
218 worker_request_batch_size: 64,
219 manifest_checkpoint_distance: 10,
220 experimental_manifest_keep_removed_file_count: 256,
221 experimental_manifest_keep_removed_file_ttl: Duration::from_secs(60 * 60),
222 compress_manifest: false,
223 max_background_index_builds: divide_num_cpus(8),
224 experimental_enable_series_index: false,
225 experimental_series_index_max_size: ReadableSize::gb(5),
226 experimental_enable_range_index: false,
227 experimental_series_index_maintenance_interval:
228 DEFAULT_SERIES_INDEX_MAINTENANCE_INTERVAL,
229 experimental_series_index_bucket_width: Duration::from_secs(5 * 24 * 60 * 60),
230 max_background_flushes: divide_num_cpus(2),
231 max_background_compactions: divide_num_cpus(4),
232 max_background_purges: get_total_cpu_cores(),
233 experimental_compaction_memory_limit: MemoryLimit::Unlimited,
234 experimental_compaction_on_exhausted: OnExhaustedPolicy::default(),
235 auto_flush_interval: Duration::from_secs(10 * 60),
236 global_write_buffer_size: ReadableSize::gb(1),
237 global_write_buffer_reject_size: ReadableSize::gb(2),
238 default_region_write_buffer_size: ReadableSize::mb(0),
239 sst_meta_cache_size: ReadableSize::mb(128),
240 vector_cache_size: ReadableSize::mb(512),
241 page_cache_size: ReadableSize::mb(512),
242 selector_result_cache_size: ReadableSize::mb(512),
243 range_result_cache_size: ReadableSize::mb(512),
244 prefilter_result_cache_size: ReadableSize::mb(128),
245 enable_write_cache: false,
246 write_cache_path: String::new(),
247 write_cache_size: ReadableSize::gb(5),
248 write_cache_ttl: None,
249 preload_index_cache: true,
250 index_cache_percent: DEFAULT_INDEX_CACHE_PERCENT,
251 enable_refill_cache_on_read: true,
252 manifest_cache_size: ReadableSize::mb(256),
253 sst_write_buffer_size: DEFAULT_WRITE_BUFFER_SIZE,
254 max_concurrent_scan_files: DEFAULT_MAX_CONCURRENT_SCAN_FILES,
255 allow_stale_entries: false,
256 scan_memory_limit: MemoryLimit::default(),
257 scan_memory_on_exhausted: OnExhaustedPolicy::Fail,
258 index: IndexConfig::default(),
259 inverted_index: InvertedIndexConfig::default(),
260 fulltext_index: FulltextIndexConfig::default(),
261 bloom_filter_index: BloomFilterConfig::default(),
262 min_compaction_interval: Duration::from_secs(0),
263 schedule_compaction_after_edit: true,
264 default_flat_format: true,
265 experimental_series_scan_v2: true,
266 gc: GcConfig::default(),
267 };
268
269 if let Some(sys_memory) = get_total_memory_readable() {
271 mito_config.adjust_buffer_and_cache_size(sys_memory);
272 }
273
274 mito_config
275 }
276}
277
278impl MitoConfig {
279 pub fn sanitize(&mut self, data_home: &str) -> Result<()> {
283 if self.experimental_enable_series_index {
284 snafu::ensure!(
285 self.experimental_series_index_max_size.as_bytes() >= 1024,
286 crate::error::InvalidConfigSnafu {
287 reason: "experimental_series_index_max_size must be at least 1KiB"
288 }
289 );
290 }
291 if self.num_workers == 0 {
293 self.num_workers = divide_num_cpus(2);
294 }
295
296 if self.worker_channel_size == 0 {
298 warn!("Sanitize channel size 0 to 1");
299 self.worker_channel_size = 1;
300 }
301
302 if self.max_background_flushes == 0 {
303 warn!(
304 "Sanitize max background flushes 0 to {}",
305 divide_num_cpus(2)
306 );
307 self.max_background_flushes = divide_num_cpus(2);
308 }
309 if self.max_background_compactions == 0 {
310 warn!(
311 "Sanitize max background compactions 0 to {}",
312 divide_num_cpus(4)
313 );
314 self.max_background_compactions = divide_num_cpus(4);
315 }
316 if self.max_background_purges == 0 {
317 let cpu_cores = get_total_cpu_cores();
318 warn!("Sanitize max background purges 0 to {}", cpu_cores);
319 self.max_background_purges = cpu_cores;
320 }
321
322 if self
323 .experimental_series_index_maintenance_interval
324 .is_zero()
325 {
326 warn!("Sanitize series-index maintenance interval 0 to 5 minutes");
327 self.experimental_series_index_maintenance_interval =
328 DEFAULT_SERIES_INDEX_MAINTENANCE_INTERVAL;
329 }
330
331 if self.global_write_buffer_reject_size <= self.global_write_buffer_size {
332 self.global_write_buffer_reject_size = self.global_write_buffer_size * 2;
333 warn!(
334 "Sanitize global write buffer reject size to {}",
335 self.global_write_buffer_reject_size
336 );
337 }
338
339 if self.sst_write_buffer_size < MULTIPART_UPLOAD_MINIMUM_SIZE {
340 self.sst_write_buffer_size = MULTIPART_UPLOAD_MINIMUM_SIZE;
341 warn!(
342 "Sanitize sst write buffer size to {}",
343 self.sst_write_buffer_size
344 );
345 }
346
347 if self.write_cache_path.trim().is_empty() {
349 self.write_cache_path = data_home.to_string();
350 }
351
352 if self.index_cache_percent == 0 || self.index_cache_percent >= 100 {
354 warn!(
355 "Invalid index_cache_percent {}, resetting to default {}",
356 self.index_cache_percent, DEFAULT_INDEX_CACHE_PERCENT
357 );
358 self.index_cache_percent = DEFAULT_INDEX_CACHE_PERCENT;
359 }
360
361 self.index.sanitize(data_home, &self.inverted_index)?;
362
363 Ok(())
364 }
365
366 fn adjust_buffer_and_cache_size(&mut self, sys_memory: ReadableSize) {
367 let global_write_buffer_size = cmp::min(
369 sys_memory / GLOBAL_WRITE_BUFFER_SIZE_FACTOR,
370 ReadableSize::gb(1),
371 );
372 let global_write_buffer_reject_size = global_write_buffer_size * 2;
374 let sst_meta_cache_size = cmp::min(
377 sys_memory / SST_META_CACHE_SIZE_FACTOR,
378 ReadableSize::mb(512),
379 );
380 let prefilter_result_cache_size = cmp::min(
381 sys_memory / PREFILTER_RESULT_CACHE_SIZE_FACTOR,
382 ReadableSize::mb(128),
383 );
384 let mem_cache_size = cmp::min(sys_memory / MEM_CACHE_SIZE_FACTOR, ReadableSize::mb(512));
386 let page_cache_size = sys_memory / PAGE_CACHE_SIZE_FACTOR;
387
388 self.global_write_buffer_size = global_write_buffer_size;
389 self.global_write_buffer_reject_size = global_write_buffer_reject_size;
390 self.sst_meta_cache_size = sst_meta_cache_size;
391 self.vector_cache_size = mem_cache_size;
392 self.page_cache_size = page_cache_size;
393 self.selector_result_cache_size = mem_cache_size;
394 self.range_result_cache_size = mem_cache_size;
395 self.prefilter_result_cache_size = prefilter_result_cache_size;
397
398 self.index.adjust_buffer_and_cache_size(sys_memory);
399 }
400
401 #[cfg(test)]
403 pub fn enable_write_cache(
404 mut self,
405 path: String,
406 size: ReadableSize,
407 ttl: Option<Duration>,
408 ) -> Self {
409 self.enable_write_cache = true;
410 self.write_cache_path = path;
411 self.write_cache_size = size;
412 self.write_cache_ttl = ttl;
413 self
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420
421 #[test]
422 fn test_default_auto_flush_interval() {
423 assert_eq!(
424 Duration::from_secs(10 * 60),
425 MitoConfig::default().auto_flush_interval
426 );
427 }
428
429 #[test]
430 fn test_adjust_sst_metadata_and_prefilter_cache_caps_independently() {
431 let mut config = MitoConfig::default();
432
433 config.adjust_buffer_and_cache_size(ReadableSize::gb(1));
434 assert_eq!(ReadableSize::mb(128), config.sst_meta_cache_size);
435 assert_eq!(ReadableSize::mb(32), config.prefilter_result_cache_size);
436
437 config.adjust_buffer_and_cache_size(ReadableSize::gb(64));
438 assert_eq!(ReadableSize::mb(512), config.sst_meta_cache_size);
439 assert_eq!(ReadableSize::mb(128), config.prefilter_result_cache_size);
440 }
441
442 #[test]
443 fn test_series_index_config() {
444 assert!(!MitoConfig::default().experimental_enable_series_index);
445 assert!(!MitoConfig::default().experimental_enable_range_index);
446 assert_eq!(
447 MitoConfig::default().experimental_series_index_bucket_width,
448 Duration::from_secs(5 * 24 * 60 * 60)
449 );
450 let mut config: MitoConfig = toml::from_str(
451 "experimental_enable_series_index = true
452 experimental_enable_range_index = false
453 experimental_series_index_max_size = '64MiB'
454 experimental_series_index_maintenance_interval = '30s'
455 experimental_series_index_bucket_width = '2days'",
456 )
457 .unwrap();
458 config.sanitize("/data").unwrap();
459 assert!(config.experimental_enable_series_index);
460 assert_eq!(
461 ReadableSize::mb(64),
462 config.experimental_series_index_max_size
463 );
464 assert!(!config.experimental_enable_range_index);
465 assert_eq!(
466 config.experimental_series_index_maintenance_interval,
467 Duration::from_secs(30)
468 );
469 assert_eq!(
470 config.experimental_series_index_bucket_width,
471 Duration::from_secs(2 * 24 * 60 * 60)
472 );
473 let restored: MitoConfig = toml::from_str(&toml::to_string(&config).unwrap()).unwrap();
474 assert_eq!(config, restored);
475 config.experimental_series_index_max_size = ReadableSize(1023);
476 assert!(config.sanitize("/data").is_err());
477 config.experimental_series_index_max_size = ReadableSize(1024);
478 config.sanitize("/data").unwrap();
479
480 let mut config: MitoConfig =
481 toml::from_str("experimental_series_index_maintenance_interval = '0s'").unwrap();
482 config.sanitize("/data").unwrap();
483 assert_eq!(
484 config.experimental_series_index_maintenance_interval,
485 MitoConfig::default().experimental_series_index_maintenance_interval
486 );
487 }
488
489 #[test]
490 fn test_range_index_config() {
491 let config: MitoConfig = toml::from_str("experimental_enable_series_index = true").unwrap();
492 assert!(!config.experimental_enable_range_index);
493 for enabled in [false, true] {
494 let config: MitoConfig =
495 toml::from_str(&format!("experimental_enable_range_index = {enabled}")).unwrap();
496 assert_eq!(enabled, config.experimental_enable_range_index);
497 let restored: MitoConfig = toml::from_str(&toml::to_string(&config).unwrap()).unwrap();
498 assert_eq!(config, restored);
499 }
500 }
501
502 #[test]
503 fn test_experimental_series_scan_v2_config() {
504 assert!(MitoConfig::default().experimental_series_scan_v2);
505
506 let config: MitoConfig = toml::from_str("experimental_series_scan_v2 = false").unwrap();
507 assert!(!config.experimental_series_scan_v2);
508 }
509}
510
511#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
513#[serde(rename_all = "snake_case")]
514pub enum IndexBuildMode {
515 #[default]
517 Sync,
518 Async,
520}
521
522#[serde_as]
523#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
524#[serde(default)]
525pub struct IndexConfig {
526 pub aux_path: String,
536
537 pub staging_size: ReadableSize,
539 #[serde(with = "humantime_serde")]
543 pub staging_ttl: Option<Duration>,
544
545 pub build_mode: IndexBuildMode,
547
548 pub write_buffer_size: ReadableSize,
550
551 pub metadata_cache_size: ReadableSize,
553 pub content_cache_size: ReadableSize,
555 pub content_cache_page_size: ReadableSize,
557 pub result_cache_size: ReadableSize,
559}
560
561impl Default for IndexConfig {
562 fn default() -> Self {
563 Self {
564 aux_path: String::new(),
565 staging_size: ReadableSize::gb(2),
566 staging_ttl: Some(Duration::from_secs(7 * 24 * 60 * 60)),
567 build_mode: IndexBuildMode::default(),
568 write_buffer_size: ReadableSize::mb(8),
569 metadata_cache_size: ReadableSize::mb(64),
570 content_cache_size: ReadableSize::mb(128),
571 content_cache_page_size: ReadableSize::kb(64),
572 result_cache_size: ReadableSize::mb(128),
573 }
574 }
575}
576
577impl IndexConfig {
578 pub fn sanitize(
579 &mut self,
580 data_home: &str,
581 inverted_index: &InvertedIndexConfig,
582 ) -> Result<()> {
583 #[allow(deprecated)]
584 if self.aux_path.is_empty() && !inverted_index.intermediate_path.is_empty() {
585 self.aux_path.clone_from(&inverted_index.intermediate_path);
586 warn!(
587 "`inverted_index.intermediate_path` is deprecated, use
588 `index.aux_path` instead. Set `index.aux_path` to {}",
589 &inverted_index.intermediate_path
590 )
591 }
592 if self.aux_path.is_empty() {
593 let path = Path::new(data_home).join("index_intermediate");
594 self.aux_path = path.as_os_str().to_string_lossy().to_string();
595 }
596
597 if self.write_buffer_size < MULTIPART_UPLOAD_MINIMUM_SIZE {
598 self.write_buffer_size = MULTIPART_UPLOAD_MINIMUM_SIZE;
599 warn!(
600 "Sanitize index write buffer size to {}",
601 self.write_buffer_size
602 );
603 }
604
605 if self.staging_ttl.map(|ttl| ttl.is_zero()).unwrap_or(false) {
606 self.staging_ttl = None;
607 }
608
609 Ok(())
610 }
611
612 pub fn adjust_buffer_and_cache_size(&mut self, sys_memory: ReadableSize) {
613 let cache_size = cmp::min(sys_memory / MEM_CACHE_SIZE_FACTOR, ReadableSize::mb(128));
614 self.result_cache_size = cmp::min(self.result_cache_size, cache_size);
615 self.content_cache_size = cmp::min(self.content_cache_size, cache_size);
616
617 let metadata_cache_size = cmp::min(
618 sys_memory / INDEX_METADATA_CACHE_SIZE_FACTOR,
619 ReadableSize::mb(64),
620 );
621 self.metadata_cache_size = cmp::min(self.metadata_cache_size, metadata_cache_size);
622 }
623}
624
625#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
627#[serde(rename_all = "snake_case")]
628pub enum Mode {
629 #[default]
631 Auto,
632 Disable,
634}
635
636impl Mode {
637 pub fn disabled(&self) -> bool {
639 matches!(self, Mode::Disable)
640 }
641
642 pub fn auto(&self) -> bool {
644 matches!(self, Mode::Auto)
645 }
646}
647
648#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
650#[serde(rename_all = "snake_case")]
651pub enum MemoryThreshold {
652 #[default]
654 Auto,
655 Unlimited,
657 #[serde(untagged)]
659 Size(ReadableSize),
660}
661
662#[serde_as]
664#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
665#[serde(default)]
666pub struct InvertedIndexConfig {
667 pub create_on_flush: Mode,
669 pub create_on_compaction: Mode,
671 pub apply_on_query: Mode,
673
674 pub mem_threshold_on_create: MemoryThreshold,
676
677 #[deprecated = "use [IndexConfig::aux_path] instead"]
678 #[serde(skip_serializing)]
679 pub intermediate_path: String,
680
681 #[deprecated = "use [IndexConfig::write_buffer_size] instead"]
682 #[serde(skip_serializing)]
683 pub write_buffer_size: ReadableSize,
684}
685
686impl Default for InvertedIndexConfig {
687 #[allow(deprecated)]
688 fn default() -> Self {
689 Self {
690 create_on_flush: Mode::Auto,
691 create_on_compaction: Mode::Auto,
692 apply_on_query: Mode::Auto,
693 mem_threshold_on_create: MemoryThreshold::Auto,
694 write_buffer_size: ReadableSize::mb(8),
695 intermediate_path: String::new(),
696 }
697 }
698}
699
700impl InvertedIndexConfig {
701 pub fn mem_threshold_on_create(&self) -> Option<usize> {
702 match self.mem_threshold_on_create {
703 MemoryThreshold::Auto => {
704 if let Some(sys_memory) = get_total_memory_readable() {
705 Some((sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as usize)
706 } else {
707 Some(ReadableSize::mb(64).as_bytes() as usize)
708 }
709 }
710 MemoryThreshold::Unlimited => None,
711 MemoryThreshold::Size(size) => Some(size.as_bytes() as usize),
712 }
713 }
714}
715
716#[serde_as]
718#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
719#[serde(default)]
720pub struct FulltextIndexConfig {
721 pub create_on_flush: Mode,
723 pub create_on_compaction: Mode,
725 pub apply_on_query: Mode,
727 pub mem_threshold_on_create: MemoryThreshold,
729 pub compress: bool,
731}
732
733impl Default for FulltextIndexConfig {
734 fn default() -> Self {
735 Self {
736 create_on_flush: Mode::Auto,
737 create_on_compaction: Mode::Auto,
738 apply_on_query: Mode::Auto,
739 mem_threshold_on_create: MemoryThreshold::Auto,
740 compress: true,
741 }
742 }
743}
744
745impl FulltextIndexConfig {
746 pub fn mem_threshold_on_create(&self) -> usize {
747 match self.mem_threshold_on_create {
748 MemoryThreshold::Auto => {
749 if let Some(sys_memory) = get_total_memory_readable() {
750 (sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as _
751 } else {
752 ReadableSize::mb(64).as_bytes() as _
753 }
754 }
755 MemoryThreshold::Unlimited => usize::MAX,
756 MemoryThreshold::Size(size) => size.as_bytes() as _,
757 }
758 }
759}
760
761#[serde_as]
763#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
764#[serde(default)]
765pub struct BloomFilterConfig {
766 pub create_on_flush: Mode,
768 pub create_on_compaction: Mode,
770 pub apply_on_query: Mode,
772 pub mem_threshold_on_create: MemoryThreshold,
774}
775
776impl Default for BloomFilterConfig {
777 fn default() -> Self {
778 Self {
779 create_on_flush: Mode::Auto,
780 create_on_compaction: Mode::Auto,
781 apply_on_query: Mode::Auto,
782 mem_threshold_on_create: MemoryThreshold::Auto,
783 }
784 }
785}
786
787impl BloomFilterConfig {
788 pub fn mem_threshold_on_create(&self) -> Option<usize> {
789 match self.mem_threshold_on_create {
790 MemoryThreshold::Auto => {
791 if let Some(sys_memory) = get_total_memory_readable() {
792 Some((sys_memory / INDEX_CREATE_MEM_THRESHOLD_FACTOR).as_bytes() as usize)
793 } else {
794 Some(ReadableSize::mb(64).as_bytes() as usize)
795 }
796 }
797 MemoryThreshold::Unlimited => None,
798 MemoryThreshold::Size(size) => Some(size.as_bytes() as usize),
799 }
800 }
801}
802
803fn divide_num_cpus(divisor: usize) -> usize {
805 debug_assert!(divisor > 0);
806 let cores = get_total_cpu_cores();
807 debug_assert!(cores > 0);
808
809 cores.div_ceil(divisor)
810}