Use fileKey instead of canonicalPath when deduping files
`resolveDependencies` showed up as a hotspot when profiling due to `canonicalPath` doing a lot of file system calls. `fileKey` is more efficient and should guarantee uniqueness (`canonicalPath` may fail in the case of hardlinks). The new code is 3x faster in our use case.
This commit is contained in:
committed by
Space Team
parent
3673dff959
commit
722ddc9f91
@@ -56,6 +56,18 @@ data class File constructor(internal val javaPath: Path) {
|
||||
val listFilesOrEmpty: List<File>
|
||||
get() = if (exists) listFiles else emptyList()
|
||||
|
||||
// A fileKey is an object that uniquely identifies the given file.
|
||||
val fileKey: Any
|
||||
get() {
|
||||
// It is not guaranteed that all filesystems have fileKey. If not we fall
|
||||
// back on canonicalPath which can be significantly slower to get.
|
||||
var key = Files.readAttributes(javaPath, BasicFileAttributes::class.java).fileKey()
|
||||
if (key == null) {
|
||||
key = this.canonicalPath
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
fun child(name: String) = File(this, name)
|
||||
fun startsWith(another: File) = javaPath.startsWith(another.javaPath)
|
||||
|
||||
|
||||
+6
-8
@@ -101,15 +101,14 @@ class KotlinLibraryResolverImpl<L: KotlinLibrary> internal constructor(
|
||||
* 3. Creates resulting [KotlinLibraryResolveResult] object.
|
||||
*/
|
||||
override fun List<KotlinLibrary>.resolveDependencies(): KotlinLibraryResolveResult {
|
||||
|
||||
val rootLibraries = this.map { KotlinResolvedLibraryImpl(it) }
|
||||
|
||||
// As far as the list of root libraries is known from the very beginning, the result can be
|
||||
// constructed from the very beginning as well.
|
||||
val result = KotlinLibraryResolverResultImpl(rootLibraries)
|
||||
|
||||
val cache = mutableMapOf<File, KotlinResolvedLibrary>()
|
||||
cache.putAll(rootLibraries.map { it.library.libraryFile.canonicalFile to it })
|
||||
val cache = mutableMapOf<Any, KotlinResolvedLibrary>()
|
||||
cache.putAll(rootLibraries.map { it.library.libraryFile.fileKey to it })
|
||||
|
||||
var newDependencies = rootLibraries
|
||||
do {
|
||||
@@ -119,12 +118,12 @@ class KotlinLibraryResolverImpl<L: KotlinLibrary> internal constructor(
|
||||
.filterNot { searchPathResolver.isProvidedByDefault(it) }
|
||||
.mapNotNull { searchPathResolver.resolve(it)?.let(::KotlinResolvedLibraryImpl) }
|
||||
.map { resolved ->
|
||||
val canonicalFile = resolved.library.libraryFile.canonicalFile
|
||||
if (canonicalFile in cache) {
|
||||
library.addDependency(cache[canonicalFile]!!)
|
||||
val fileKey = resolved.library.libraryFile.fileKey
|
||||
if (fileKey in cache) {
|
||||
library.addDependency(cache[fileKey]!!)
|
||||
null
|
||||
} else {
|
||||
cache.put(canonicalFile, resolved)
|
||||
cache.put(fileKey, resolved)
|
||||
library.addDependency(resolved)
|
||||
resolved
|
||||
}
|
||||
@@ -133,7 +132,6 @@ class KotlinLibraryResolverImpl<L: KotlinLibrary> internal constructor(
|
||||
.toList()
|
||||
}.flatten()
|
||||
} while (newDependencies.isNotEmpty())
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user