1+ use std:: cmp:: max;
12use std:: collections:: { HashMap , HashSet } ;
23use std:: fs;
34use std:: path:: PathBuf ;
@@ -75,18 +76,13 @@ impl GraphBuilder {
7576 // Thread 1: Discover modules
7677 let package = self . package . clone ( ) ;
7778 let handle = thread:: spawn ( move || {
78- let modules = discover_python_modules ( & package) ;
79- // Send modules to parser threads
80- for module in modules {
81- module_discovery_sender. send ( module) . unwrap ( ) ;
82- }
83- drop ( module_discovery_sender) ; // Close channel to signal completion
79+ discover_python_modules ( & package, module_discovery_sender) ;
8480 } ) ;
8581 thread_handles. push ( handle) ;
8682
8783 // Thread pool: Parse imports
8884 let num_workers = thread:: available_parallelism ( )
89- . map ( |n| n. get ( ) )
85+ . map ( |n| max ( n. get ( ) / 2 , 1 ) )
9086 . unwrap_or ( 4 ) ;
9187 for _ in 0 ..num_workers {
9288 let receiver = module_discovery_receiver. clone ( ) ;
@@ -151,11 +147,16 @@ struct ParsedModule {
151147 imported_objects : Vec < ImportedObject > ,
152148}
153149
154- fn discover_python_modules ( package : & PackageSpec ) -> Vec < FoundModule > {
155- let mut modules = Vec :: new ( ) ;
150+ fn discover_python_modules ( package : & PackageSpec , sender : channel:: Sender < FoundModule > ) {
151+ let num_threads = thread:: available_parallelism ( )
152+ . map ( |n| max ( n. get ( ) / 2 , 1 ) )
153+ . unwrap_or ( 4 ) ;
154+
155+ let package_clone = package. clone ( ) ;
156156
157- let walker = WalkBuilder :: new ( & package. directory )
157+ WalkBuilder :: new ( & package. directory )
158158 . standard_filters ( false ) // Don't use gitignore or other filters
159+ . threads ( num_threads)
159160 . filter_entry ( |entry| {
160161 // Allow Python files
161162 if entry. file_type ( ) . is_some_and ( |ft| ft. is_file ( ) ) {
@@ -170,31 +171,45 @@ fn discover_python_modules(package: &PackageSpec) -> Vec<FoundModule> {
170171
171172 false
172173 } )
173- . build ( ) ;
174-
175- for entry in walker. flatten ( ) {
176- let path = entry. path ( ) ;
177- if let Some ( module_name) = path_to_module_name ( path, package) {
178- let is_package = is_package ( path) ;
179-
180- // Get mtime
181- let mtime_secs = fs:: metadata ( path)
182- . and_then ( |m| m. modified ( ) )
183- . ok ( )
184- . and_then ( |t| t. duration_since ( std:: time:: UNIX_EPOCH ) . ok ( ) )
185- . map ( |d| d. as_secs ( ) as i64 )
186- . unwrap_or ( 0 ) ;
187-
188- modules. push ( FoundModule {
189- name : module_name,
190- path : path. to_owned ( ) ,
191- is_package,
192- mtime_secs,
193- } ) ;
194- }
195- }
174+ . build_parallel ( )
175+ . run ( || {
176+ let sender = sender. clone ( ) ;
177+ let package = package_clone. clone ( ) ;
178+
179+ Box :: new ( move |entry| {
180+ use ignore:: WalkState ;
181+
182+ let entry = match entry {
183+ Ok ( e) => e,
184+ Err ( _) => return WalkState :: Continue ,
185+ } ;
186+
187+ let path = entry. path ( ) ;
188+ if let Some ( module_name) = path_to_module_name ( path, & package) {
189+ let is_package = is_package ( path) ;
190+
191+ // Get mtime
192+ let mtime_secs = fs:: metadata ( path)
193+ . and_then ( |m| m. modified ( ) )
194+ . ok ( )
195+ . and_then ( |t| t. duration_since ( std:: time:: UNIX_EPOCH ) . ok ( ) )
196+ . map ( |d| d. as_secs ( ) as i64 )
197+ . unwrap_or ( 0 ) ;
198+
199+ let found_module = FoundModule {
200+ name : module_name,
201+ path : path. to_owned ( ) ,
202+ is_package,
203+ mtime_secs,
204+ } ;
205+
206+ // Send module as soon as we discover it
207+ let _ = sender. send ( found_module) ;
208+ }
196209
197- modules
210+ WalkState :: Continue
211+ } )
212+ } ) ;
198213}
199214
200215fn parse_module_imports ( module : & FoundModule , cache : & ImportCache ) -> Option < ParsedModule > {
0 commit comments