@@ -63,8 +63,10 @@ use std::sync::mpsc::Receiver;
63
63
use std:: thread:: available_parallelism;
64
64
use std:: time:: { Duration , Instant } ;
65
65
use std:: { fmt:: Debug , fs:: OpenOptions } ;
66
+ use tracing:: debug;
66
67
67
68
use anyhow:: { Context , Result } ;
69
+ use sys_info;
68
70
69
71
use crate :: process:: Process ;
70
72
use crate :: utils:: notifications:: Notification ;
@@ -78,6 +80,26 @@ pub(crate) enum FileBuffer {
78
80
Threaded ( PoolReference ) ,
79
81
}
80
82
83
+ impl PartialEq for FileBuffer {
84
+ fn eq ( & self , other : & Self ) -> bool {
85
+ self . deref ( ) == other. deref ( )
86
+ }
87
+ }
88
+
89
+ impl Eq for FileBuffer { }
90
+
91
+ impl PartialOrd for FileBuffer {
92
+ fn partial_cmp ( & self , other : & Self ) -> Option < std:: cmp:: Ordering > {
93
+ Some ( self . cmp ( other) )
94
+ }
95
+ }
96
+
97
+ impl Ord for FileBuffer {
98
+ fn cmp ( & self , other : & Self ) -> std:: cmp:: Ordering {
99
+ self . deref ( ) . cmp ( other. deref ( ) )
100
+ }
101
+ }
102
+
81
103
impl FileBuffer {
82
104
/// All the buffers space to be re-used when the last reference to it is dropped.
83
105
pub ( crate ) fn clear ( & mut self ) {
@@ -137,6 +159,37 @@ pub(crate) enum IncrementalFile {
137
159
ThreadedReceiver ( Receiver < FileBuffer > ) ,
138
160
}
139
161
162
+ impl PartialEq for IncrementalFile {
163
+ fn eq ( & self , other : & Self ) -> bool {
164
+ // Just compare discriminants since Receiver cannot be compared
165
+ matches ! (
166
+ ( self , other) ,
167
+ ( Self :: ImmediateReceiver , Self :: ImmediateReceiver ) |
168
+ ( Self :: ThreadedReceiver ( _) , Self :: ThreadedReceiver ( _) )
169
+ )
170
+ }
171
+ }
172
+
173
+ impl Eq for IncrementalFile { }
174
+
175
+ impl PartialOrd for IncrementalFile {
176
+ fn partial_cmp ( & self , other : & Self ) -> Option < std:: cmp:: Ordering > {
177
+ Some ( self . cmp ( other) )
178
+ }
179
+ }
180
+
181
+ impl Ord for IncrementalFile {
182
+ fn cmp ( & self , other : & Self ) -> std:: cmp:: Ordering {
183
+ // ImmediateReceiver is "less than" ThreadedReceiver
184
+ match ( self , other) {
185
+ ( Self :: ImmediateReceiver , Self :: ImmediateReceiver ) => std:: cmp:: Ordering :: Equal ,
186
+ ( Self :: ImmediateReceiver , Self :: ThreadedReceiver ( _) ) => std:: cmp:: Ordering :: Less ,
187
+ ( Self :: ThreadedReceiver ( _) , Self :: ImmediateReceiver ) => std:: cmp:: Ordering :: Greater ,
188
+ ( Self :: ThreadedReceiver ( _) , Self :: ThreadedReceiver ( _) ) => std:: cmp:: Ordering :: Equal ,
189
+ }
190
+ }
191
+ }
192
+
140
193
// The basic idea is that in single threaded mode we get this pattern:
141
194
// package budget io-layer
142
195
// +<-claim->
@@ -176,13 +229,28 @@ pub(crate) enum IncrementalFile {
176
229
// Error reporting is passed through the regular completion port, to avoid creating a new special case.
177
230
178
231
/// What kind of IO operation to perform
179
- #[ derive( Debug ) ]
232
+ #[ derive( Debug , Eq , Ord , PartialEq , PartialOrd ) ]
180
233
pub ( crate ) enum Kind {
181
234
Directory ,
182
235
File ( FileBuffer ) ,
183
236
IncrementalFile ( IncrementalFile ) ,
184
237
}
185
238
239
+ /// Priority level for I/O operations
240
+ /// Higher values indicate higher priority
241
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , PartialOrd , Ord ) ]
242
+ pub enum IOPriority {
243
+ Critical ,
244
+ Normal ,
245
+ Background ,
246
+ }
247
+
248
+ impl Default for IOPriority {
249
+ fn default ( ) -> Self {
250
+ Self :: Normal
251
+ }
252
+ }
253
+
186
254
/// The details of the IO operation
187
255
#[ derive( Debug ) ]
188
256
pub ( crate ) struct Item {
@@ -198,6 +266,32 @@ pub(crate) struct Item {
198
266
pub ( crate ) result : io:: Result < ( ) > ,
199
267
/// The mode to apply
200
268
mode : u32 ,
269
+ /// Priority of this operation
270
+ priority : IOPriority ,
271
+ }
272
+
273
+ impl PartialEq for Item {
274
+ fn eq ( & self , other : & Self ) -> bool {
275
+ self . priority == other. priority && self . full_path == other. full_path
276
+ }
277
+ }
278
+
279
+ impl Eq for Item { }
280
+
281
+ impl PartialOrd for Item {
282
+ fn partial_cmp ( & self , other : & Self ) -> Option < std:: cmp:: Ordering > {
283
+ Some ( self . cmp ( other) )
284
+ }
285
+ }
286
+
287
+ impl Ord for Item {
288
+ fn cmp ( & self , other : & Self ) -> std:: cmp:: Ordering {
289
+ // Sort by priority first (higher priority comes first)
290
+ match other. priority . cmp ( & self . priority ) {
291
+ std:: cmp:: Ordering :: Equal => self . full_path . cmp ( & other. full_path ) ,
292
+ ordering => ordering,
293
+ }
294
+ }
201
295
}
202
296
203
297
#[ derive( Debug ) ]
@@ -218,6 +312,7 @@ impl Item {
218
312
finish : None ,
219
313
result : Ok ( ( ) ) ,
220
314
mode,
315
+ priority : IOPriority :: default ( ) ,
221
316
}
222
317
}
223
318
@@ -229,6 +324,7 @@ impl Item {
229
324
finish : None ,
230
325
result : Ok ( ( ) ) ,
231
326
mode,
327
+ priority : IOPriority :: default ( ) ,
232
328
}
233
329
}
234
330
@@ -245,9 +341,23 @@ impl Item {
245
341
finish : None ,
246
342
result : Ok ( ( ) ) ,
247
343
mode,
344
+ priority : IOPriority :: default ( ) ,
248
345
} ;
249
346
Ok ( ( result, Box :: new ( chunk_submit) ) )
250
347
}
348
+
349
+ /// Set the priority of this I/O operation
350
+ /// remove for now
351
+ #[ allow( dead_code) ]
352
+ pub ( crate ) fn with_priority ( mut self , priority : IOPriority ) -> Self {
353
+ self . priority = priority;
354
+ self
355
+ }
356
+
357
+ /// Get the priority of this I/O operation
358
+ pub ( crate ) fn priority ( & self ) -> IOPriority {
359
+ self . priority
360
+ }
251
361
}
252
362
253
363
// This could be a boxed trait object perhaps... but since we're looking at
@@ -448,15 +558,56 @@ pub(crate) fn get_executor<'a>(
448
558
ram_budget : usize ,
449
559
process : & Process ,
450
560
) -> Result < Box < dyn Executor + ' a > > {
561
+ // Calculate optimal thread count based on system characteristics
562
+ // Default is CPU count for CPU-bound systems, or 2x CPU count for I/O-bound operations
563
+ let default_thread_count = available_parallelism ( )
564
+ . map ( |p| {
565
+ let cpu_count = p. get ( ) ;
566
+ // Use more threads for I/O bound operations to hide latency
567
+ // but cap it to avoid too much overhead
568
+ std:: cmp:: min ( cpu_count * 2 , 16 )
569
+ } )
570
+ . unwrap_or ( 2 ) ;
571
+
451
572
// If this gets lots of use, consider exposing via the config file.
452
573
let thread_count = match process. var ( "RUSTUP_IO_THREADS" ) {
453
- Err ( _) => available_parallelism ( ) . map ( |p| p . get ( ) ) . unwrap_or ( 1 ) ,
574
+ Err ( _) => default_thread_count ,
454
575
Ok ( n) => n
455
576
. parse :: < usize > ( )
456
577
. context ( "invalid value in RUSTUP_IO_THREADS. Must be a natural number" ) ?,
457
578
} ;
579
+
580
+ // Calculate optimal memory budget based on system memory
581
+ // Default to 10% of system memory, or fallback to 256MB
582
+ let default_ram_budget = if ram_budget == 0 {
583
+ match sys_info:: mem_info ( ) {
584
+ Ok ( mem) => {
585
+ let total_mem = mem. total as usize * 1024 ; // Convert to bytes
586
+ total_mem / 10 // Use 10% of system memory
587
+ }
588
+ Err ( _) => 256 * 1024 * 1024 , // Fallback to 256MB
589
+ }
590
+ } else {
591
+ ram_budget
592
+ } ;
593
+
594
+ // Allow overriding the memory budget via environment variable (maybe keep this but useful for testing on different systems right now)
595
+ let actual_ram_budget = match process. var ( "RUSTUP_RAM_BUDGET" ) {
596
+ Err ( _) => default_ram_budget,
597
+ Ok ( n) => n
598
+ . parse :: < usize > ( )
599
+ . context ( "invalid value in RUSTUP_RAM_BUDGET. Must be in bytes" ) ?,
600
+ } ;
601
+
602
+ // Log the chosen configuration for debugging
603
+ debug ! (
604
+ "Using IO executor with thread_count={} and ram_budget={}MB" ,
605
+ thread_count,
606
+ actual_ram_budget / ( 1024 * 1024 )
607
+ ) ;
608
+
458
609
Ok ( match thread_count {
459
610
0 | 1 => Box :: new ( immediate:: ImmediateUnpacker :: new ( ) ) ,
460
- n => Box :: new ( threaded:: Threaded :: new ( notify_handler, n, ram_budget ) ) ,
611
+ n => Box :: new ( threaded:: Threaded :: new ( notify_handler, n, actual_ram_budget ) ) ,
461
612
} )
462
613
}
0 commit comments