@@ -30,8 +30,8 @@ use subspace_farmer::utils::piece_validator::SegmentCommitmentPieceValidator;
3030use subspace_farmer:: utils:: readers_and_pieces:: ReadersAndPieces ;
3131use subspace_farmer:: utils:: ss58:: parse_ss58_reward_address;
3232use subspace_farmer:: utils:: {
33- all_cpu_cores, create_plotting_thread_pool_manager, run_future_in_dedicated_thread ,
34- thread_pool_core_indices, AsyncJoinOnDrop ,
33+ all_cpu_cores, create_plotting_thread_pool_manager, parse_cpu_cores_sets ,
34+ run_future_in_dedicated_thread , thread_pool_core_indices, AsyncJoinOnDrop ,
3535} ;
3636use subspace_farmer:: { Identity , NodeClient , NodeRpcClient } ;
3737use subspace_farmer_components:: plotting:: PlottedSector ;
@@ -138,6 +138,17 @@ pub(crate) struct FarmingArgs {
138138 /// Threads will be pinned to corresponding CPU cores at creation.
139139 #[ arg( long) ]
140140 plotting_thread_pool_size : Option < NonZeroUsize > ,
141+ /// Specify exact CPU cores to be used for plotting bypassing any custom logic farmer might use
142+ /// otherwise. It replaces both `--sector-encoding-concurrency` and
143+ /// `--plotting-thread-pool-size` options if specified. Requires `--replotting-cpu-cores` to be
144+ /// specified with the same number of CPU cores groups (or not specified at all, in which case
145+ /// it'll use the same thread pool as plotting).
146+ ///
147+ /// Cores are coma-separated, with whitespace separating different thread pools/encoding
148+ /// instances. For example "0,1 2,3" will result in two sectors being encoded at the same time,
149+ /// each with a pair of CPU cores.
150+ #[ arg( long, conflicts_with_all = & [ "sector_encoding_concurrency" , "plotting_thread_pool_size" ] ) ]
151+ plotting_cpu_cores : Option < String > ,
141152 /// Size of one thread pool used for replotting, typically smaller pool than for plotting
142153 /// to not affect farming as much, defaults to half of the number of logical CPUs available on
143154 /// UMA system and number of logical CPUs available in NUMA node on NUMA system.
@@ -148,6 +159,15 @@ pub(crate) struct FarmingArgs {
148159 /// Threads will be pinned to corresponding CPU cores at creation.
149160 #[ arg( long) ]
150161 replotting_thread_pool_size : Option < NonZeroUsize > ,
162+ /// Specify exact CPU cores to be used for replotting bypassing any custom logic farmer might
163+ /// use otherwise. It replaces `--replotting-thread_pool_size` options if specified. Requires
164+ /// `--plotting-cpu-cores` to be specified with the same number of CPU cores groups.
165+ ///
166+ /// Cores are coma-separated, with whitespace separating different thread pools/encoding
167+ /// instances. For example "0,1 2,3" will result in two sectors being encoded at the same time,
168+ /// each with a pair of CPU cores.
169+ #[ arg( long, conflicts_with_all = & [ "sector_encoding_concurrency" , "replotting_thread_pool_size" ] ) ]
170+ replotting_cpu_cores : Option < String > ,
151171}
152172
153173fn cache_percentage_parser ( s : & str ) -> anyhow:: Result < NonZeroU8 > {
@@ -286,7 +306,9 @@ where
286306 farm_during_initial_plotting,
287307 farming_thread_pool_size,
288308 plotting_thread_pool_size,
309+ plotting_cpu_cores,
289310 replotting_thread_pool_size,
311+ replotting_cpu_cores,
290312 } = farming_args;
291313
292314 // Override flags with `--dev`
@@ -431,19 +453,41 @@ where
431453 None => farmer_app_info. protocol_info . max_pieces_in_sector ,
432454 } ;
433455
434- let plotting_thread_pool_core_indices =
435- thread_pool_core_indices ( plotting_thread_pool_size, sector_encoding_concurrency) ;
436- let replotting_thread_pool_core_indices = {
437- let mut replotting_thread_pool_core_indices =
438- thread_pool_core_indices ( replotting_thread_pool_size, sector_encoding_concurrency) ;
439- if replotting_thread_pool_size. is_none ( ) {
440- // The default behavior is to use all CPU cores, but for replotting we just want half
441- replotting_thread_pool_core_indices
442- . iter_mut ( )
443- . for_each ( |set| set. truncate ( set. cpu_cores ( ) . len ( ) / 2 ) ) ;
456+ let plotting_thread_pool_core_indices;
457+ let replotting_thread_pool_core_indices;
458+ if let Some ( plotting_cpu_cores) = plotting_cpu_cores {
459+ plotting_thread_pool_core_indices = parse_cpu_cores_sets ( & plotting_cpu_cores)
460+ . map_err ( |error| anyhow:: anyhow!( "Failed to parse `--plotting-cpu-cores`: {error}" ) ) ?;
461+ replotting_thread_pool_core_indices = match replotting_cpu_cores {
462+ Some ( replotting_cpu_cores) => {
463+ parse_cpu_cores_sets ( & replotting_cpu_cores) . map_err ( |error| {
464+ anyhow:: anyhow!( "Failed to parse `--replotting-cpu-cores`: {error}" )
465+ } ) ?
466+ }
467+ None => plotting_thread_pool_core_indices. clone ( ) ,
468+ } ;
469+ if plotting_thread_pool_core_indices. len ( ) != replotting_thread_pool_core_indices. len ( ) {
470+ return Err ( anyhow:: anyhow!(
471+ "Number of plotting thread pools ({}) is not the same as for replotting ({})" ,
472+ plotting_thread_pool_core_indices. len( ) ,
473+ replotting_thread_pool_core_indices. len( )
474+ ) ) ;
444475 }
445- replotting_thread_pool_core_indices
446- } ;
476+ } else {
477+ plotting_thread_pool_core_indices =
478+ thread_pool_core_indices ( plotting_thread_pool_size, sector_encoding_concurrency) ;
479+ replotting_thread_pool_core_indices = {
480+ let mut replotting_thread_pool_core_indices =
481+ thread_pool_core_indices ( replotting_thread_pool_size, sector_encoding_concurrency) ;
482+ if replotting_thread_pool_size. is_none ( ) {
483+ // The default behavior is to use all CPU cores, but for replotting we just want half
484+ replotting_thread_pool_core_indices
485+ . iter_mut ( )
486+ . for_each ( |set| set. truncate ( set. cpu_cores ( ) . len ( ) / 2 ) ) ;
487+ }
488+ replotting_thread_pool_core_indices
489+ } ;
490+ }
447491
448492 let downloading_semaphore = Arc :: new ( Semaphore :: new (
449493 sector_downloading_concurrency
0 commit comments