diff --git a/.jules/bolt.md b/.jules/bolt.md index a9253e7..3439b66 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -12,3 +12,7 @@ ## 2024-07-08 - URL Encoding String Allocation Bottleneck **Learning:** `byte.toString(16).padStart(2, '0').toUpperCase()` inside a loop allocating up to 3 strings per reserved byte in a hot path causes significant GC pressure. This is a common but dangerous anti-pattern in Kotlin when processing large strings or numerous files in directory crawlers. **Action:** Replace chained string operations with direct character mapping and bitwise operations (`ushr`, `and`) when building formatted hex output, which avoids intermediate string creation entirely. Ensure 100% branch coverage with test inputs spanning both < 10 and > 9 hex values. + +## 2024-07-28 - 디렉토리 탐색 성능 최적화 +**Learning:** 디렉토리 엔트리를 순회할 때 제외(exclude) 대상인 파일에 대해 불필요한 `Path` 객체 생성과 `Files.isDirectory` 파일 시스템 I/O를 호출하는 것은 심각한 성능 저하를 초래합니다. 또한, 순서가 무의미한 제외 목록(`Set`)을 구성하기 위해 `.sorted()`를 호출하는 것은 O(N log N)의 불필요한 오버헤드를 더합니다. +**Action:** 항상 제외 조건(`fileName !in exclude`)을 루프 진입 즉시 확인하여 조기 반환하고, 결과를 정렬할 필요가 없는 경우에는 `.sorted()` 호출을 제거하여 디렉토리 처리 성능을 향상시킵니다. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 1f1a5a2..aec6496 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -130,7 +130,7 @@ fun process_ignore_file(curr_dir: File): Set { } } - curr_dir.list()?.sorted()?.forEach { + curr_dir.list()?.forEach { val current = it ignored_regexes.forEach { regex -> if(regex.matches(current)){ @@ -216,9 +216,11 @@ fun process_dir(curr_dir: File){ val dir_files: MutableList = curr_dir.listFiles()?.toMutableList() ?: mutableListOf() dir_files.sortWith(compareBy ({it.name}) ) dir_files.forEach { + val fileName = it.getName() + if (fileName in exclude) return@forEach + val isLinkedDirectory = Files.isDirectory(it.toPath(), LinkOption.NOFOLLOW_LINKS) - if((it.getName() !in exclude) && (isLinkedDirectory || !it.isDirectory()) && !Files.isSymbolicLink(it.toPath())) { - val fileName = it.getName() + if((isLinkedDirectory || !it.isDirectory()) && !Files.isSymbolicLink(it.toPath())) { val encodedHref = if (isLinkedDirectory) { "./${fileName.urlEncodePath()}/" } else { "./${fileName.urlEncodePath()}" } val ariaLabel = "${fileName} ${if (isLinkedDirectory) { "디렉토리" } else { "파일" }}".escapeHtml() val icon = if (isLinkedDirectory) { "📁" } else { "▸" }