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>
|
val listFilesOrEmpty: List<File>
|
||||||
get() = if (exists) listFiles else emptyList()
|
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 child(name: String) = File(this, name)
|
||||||
fun startsWith(another: File) = javaPath.startsWith(another.javaPath)
|
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.
|
* 3. Creates resulting [KotlinLibraryResolveResult] object.
|
||||||
*/
|
*/
|
||||||
override fun List<KotlinLibrary>.resolveDependencies(): KotlinLibraryResolveResult {
|
override fun List<KotlinLibrary>.resolveDependencies(): KotlinLibraryResolveResult {
|
||||||
|
|
||||||
val rootLibraries = this.map { KotlinResolvedLibraryImpl(it) }
|
val rootLibraries = this.map { KotlinResolvedLibraryImpl(it) }
|
||||||
|
|
||||||
// As far as the list of root libraries is known from the very beginning, the result can be
|
// 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.
|
// constructed from the very beginning as well.
|
||||||
val result = KotlinLibraryResolverResultImpl(rootLibraries)
|
val result = KotlinLibraryResolverResultImpl(rootLibraries)
|
||||||
|
|
||||||
val cache = mutableMapOf<File, KotlinResolvedLibrary>()
|
val cache = mutableMapOf<Any, KotlinResolvedLibrary>()
|
||||||
cache.putAll(rootLibraries.map { it.library.libraryFile.canonicalFile to it })
|
cache.putAll(rootLibraries.map { it.library.libraryFile.fileKey to it })
|
||||||
|
|
||||||
var newDependencies = rootLibraries
|
var newDependencies = rootLibraries
|
||||||
do {
|
do {
|
||||||
@@ -119,12 +118,12 @@ class KotlinLibraryResolverImpl<L: KotlinLibrary> internal constructor(
|
|||||||
.filterNot { searchPathResolver.isProvidedByDefault(it) }
|
.filterNot { searchPathResolver.isProvidedByDefault(it) }
|
||||||
.mapNotNull { searchPathResolver.resolve(it)?.let(::KotlinResolvedLibraryImpl) }
|
.mapNotNull { searchPathResolver.resolve(it)?.let(::KotlinResolvedLibraryImpl) }
|
||||||
.map { resolved ->
|
.map { resolved ->
|
||||||
val canonicalFile = resolved.library.libraryFile.canonicalFile
|
val fileKey = resolved.library.libraryFile.fileKey
|
||||||
if (canonicalFile in cache) {
|
if (fileKey in cache) {
|
||||||
library.addDependency(cache[canonicalFile]!!)
|
library.addDependency(cache[fileKey]!!)
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
cache.put(canonicalFile, resolved)
|
cache.put(fileKey, resolved)
|
||||||
library.addDependency(resolved)
|
library.addDependency(resolved)
|
||||||
resolved
|
resolved
|
||||||
}
|
}
|
||||||
@@ -133,7 +132,6 @@ class KotlinLibraryResolverImpl<L: KotlinLibrary> internal constructor(
|
|||||||
.toList()
|
.toList()
|
||||||
}.flatten()
|
}.flatten()
|
||||||
} while (newDependencies.isNotEmpty())
|
} while (newDependencies.isNotEmpty())
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user