From ffa293918ba16486e4a5e3846a01b66183d7b7e5 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 10 Jul 2026 03:12:07 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[performance=20improvement]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 파일 시스템 접근을 최소화하기 위해 파일 제외 목록 확인을 Files.isDirectory 호출 이전으로 이동했습니다. --- .jules/bolt.md | 1 + src/main/kotlin/html4tree/main.kt | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index a9253e7a..82fe47bb 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -12,3 +12,4 @@ ## 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. +## 2026-07-10 - Expensive OS stat calls before cheap in-memory checks\n**Learning:** In Kotlin/Java, checking file properties (like `isDirectory` or `isSymbolicLink`) via `java.nio.file.Files` requires allocating `Path` objects and performs expensive native OS stat calls. When processing file listings, always short-circuit filesystem checks by testing against exclusion lists (using cheap in-memory string operations) before calling methods that touch the filesystem.\n**Action:** Re-ordered conditionals to check `exclude` sets before invoking `Files.isDirectory` and `Files.isSymbolicLink`. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 1f1a5a2e..d8ad85dc 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -216,14 +216,17 @@ 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 isLinkedDirectory = Files.isDirectory(it.toPath(), LinkOption.NOFOLLOW_LINKS) - if((it.getName() !in exclude) && (isLinkedDirectory || !it.isDirectory()) && !Files.isSymbolicLink(it.toPath())) { - val fileName = it.getName() - val encodedHref = if (isLinkedDirectory) { "./${fileName.urlEncodePath()}/" } else { "./${fileName.urlEncodePath()}" } - val ariaLabel = "${fileName} ${if (isLinkedDirectory) { "디렉토리" } else { "파일" }}".escapeHtml() - val icon = if (isLinkedDirectory) { "📁" } else { "▸" } - l.append("""
  • ${fileName.escapeHtml()}
  • """) - l.append('\n') + val fileName = it.getName() + // ⚡ Bolt Performance Optimization: Short-circuit string match before expensive OS filesystem calls + if (fileName !in exclude) { + val isLinkedDirectory = Files.isDirectory(it.toPath(), LinkOption.NOFOLLOW_LINKS) + 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 { "▸" } + l.append("""
  • ${fileName.escapeHtml()}
  • """) + l.append('\n') + } } }