-
Notifications
You must be signed in to change notification settings - Fork 41.5k
Optimize resource lookup in DevTools restart #46289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize resource lookup in DevTools restart #46289
Conversation
Thanks for the PR, @hoonyworld. Can you please sign your commit as described in the check failure. |
The resource resolver in DevTools can cause performance degradation during application restarts in large projects. Key methods like isDeleted() and getAdditionalResources() rely on nested loops, leading to O(n*m) complexity. This commit refactors ClassLoaderFiles to use a pre-computed, flattened map. This provides O(1) complexity for direct lookups and allows for efficient single-loop iteration. The ClassLoaderFilesResourcePatternResolver is updated to leverage this new, efficient structure: - getFile() and size() are improved from O(n) to O(1). - isDeleted() and getAdditionalResources() are improved from O(n*m) to O(m) by eliminating nested loops. - Data consistency is maintained across all operations. This optimization significantly improves restart performance with a minimal memory footprint, while preserving the existing API and exception handling behavior. Signed-off-by: DongHoon Lee <[email protected]>
ff8e9cb
to
ca25a15
Compare
@wilkinsona Thanks for the confirmation. It seems I missed the DCO-related part. I'll take care of it right away as guided. |
...ls/src/main/java/org/springframework/boot/devtools/restart/classloader/ClassLoaderFiles.java
Show resolved
Hide resolved
The resource resolver in DevTools can cause performance degradation during application restarts in large projects. Key methods like isDeleted() and getAdditionalResources() rely on nested loops, leading to O(n*m) complexity. This commit refactors ClassLoaderFiles to use a pre-computed, flattened map. This provides O(1) complexity for direct lookups and allows for efficient single-loop iteration. The ClassLoaderFilesResourcePatternResolver is updated to leverage this new, efficient structure: - getFile() and size() are improved from O(n) to O(1). - isDeleted() and getAdditionalResources() are improved from O(n*m) to O(m) by eliminating nested loops. - Data consistency is maintained across all operations. This optimization significantly improves restart performance with a minimal memory footprint, while preserving the existing API and exception handling behavior. See gh-46289 Signed-off-by: DongHoon Lee <[email protected]>
Thanks @move-hoon ! |
Hi @mhalbritter Thank you for the review and guidance. Since this is my first contribution to Spring Boot, I’d like to briefly confirm my understanding:
Could you confirm if this is correct? Thanks again for your time and support! 🙏 |
Hello @move-hoon , congrats on your first contribution to Spring Boot! Keep them coming!
|
Optimize resource lookup in DevTools restart
This pull request significantly improves the performance of resource lookups within the DevTools restart mechanism, particularly benefiting projects with a large number of files and source directories.
Motivation and Problem
The existing
ClassLoaderFilesResourcePatternResolver
and its underlyingClassLoaderFiles
implementation suffer from performance degradation in large-scale projects. Key methods such asgetFile()
,size()
,isDeleted()
, andgetAdditionalResources()
rely on nested loops that iterate through all source directories and their respective files. This results in a time complexity of O(n*m) for many critical operations, where 'n' is the number of source directories and 'm' is the number of files. As a project grows, these repeated iterations can introduce noticeable delays during application restarts.Solution
This PR addresses the performance bottleneck by refactoring
ClassLoaderFiles
to use a pre-computed, flattened map for direct lookups, and updatingClassLoaderFilesResourcePatternResolver
to leverage this new, efficient structure.1.
ClassLoaderFiles
Refactoring:A new
Map<String, ClassLoaderFile> filesByName
field has been added. This map is populated once at construction time and serves as a unified, central cache for all files across all source directories.getFile(name)
now performs a directget()
on thefilesByName
map, reducing its complexity from O(n) to O(1).size()
now directly returnsfilesByName.size()
, reducing its complexity from O(n) to O(1).The
addFile()
andremoveAll()
methods have been updated to ensure that both the originalsourceDirectories
map and the newfilesByName
map are kept in sync, guaranteeing data integrity.A new
getFileEntries()
method provides a way to iterate over all files in a single loop, which is used by the resolver.2.
ClassLoaderFilesResourcePatternResolver
Optimization:isDeleted()
:This method has been refactored to eliminate the nested loop structure (from O(n*m) to O(m)). It now iterates over the single, unified collection provided by
classLoaderFiles.getFileEntries()
, significantly reducing the complexity of the operation while preserving the original exception handling behavior.getAdditionalResources()
:Similarly, this method now iterates over the single, unified collection provided by
classLoaderFiles.getFileEntries()
, eliminating the outer loop and improving readability and performance.Benefits
Significant Performance Improvement:
Reduces the time complexity of critical file lookup operations. Direct lookups like
getFile
are improved from O(n) to O(1), while iterative searches likeisDeleted
are improved from O(n*m) to O(m), leading to faster application restarts in DevTools.Enhanced Code Quality:
Eliminates nested loops, which simplifies the code, improves readability, and makes the logic easier to maintain.
Minimal Memory Overhead:
The optimization introduces a map of references, not data duplicates. The trade-off of a negligible increase in memory usage for a substantial performance gain is highly favorable.
This change maintains full backward compatibility with the existing API while providing a much-needed performance boost for developers working on large applications.