From 56622161789f2ffde242e9d55c5c7d7317d319f6 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 25 Sep 2026 20:54:08 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=20=ED=85=9C=ED=94=8C=EB=A6=BF(=EB=B3=B4=EA=B0=84)=20?= =?UTF-8?q?=EC=98=A4=EB=B2=84=ED=97=A4=EB=93=9C=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 루프 내에서 변수를 인라인으로 포함하는 문자열 템플릿을 사용하여 HTML을 렌더링하면, 다수의 중간 객체가 할당되어 가비지 컬렉션(GC) 성능에 부정적인 영향을 미칩니다. 이를 `StringBuilder.append()` 체이닝 방식으로 리팩토링하고 반복 호출되던 불변의 이스케이프 문자열을 재사용함으로써 메모리 낭비를 없애고 실행 속도를 개선했습니다. --- .jules/bolt.md | 3 +++ src/main/kotlin/html4tree/main.kt | 15 +++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index ee124e69..35afd23b 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -62,3 +62,6 @@ ## 2026-08-11 - Array의 toMutableList 할당 오버헤드 최적화 **학습:** 배열을 정렬하기 위해 `.toMutableList()`를 호출하면 새로운 `ArrayList` 객체와 내부 배열 객체가 할당되어 대규모 디렉토리를 순회할 때 가비지 컬렉션(GC) 부하를 유발합니다. 배열 복제가 필요한 경우 `.clone()`을 사용하면 하나의 배열 객체만 새로 할당되므로 더 효율적입니다. **조치:** 디렉토리 파일 배열을 정렬하기 전에 복사할 때 `.toMutableList()` 대신 `.clone()`을 사용하여 불필요한 중간 컬렉션 할당을 제거하고 성능을 향상시켰습니다. +## 2026-08-11 - 핫 루프 문자열 템플릿(보간) 오버헤드 최적화 +**학습:** 디렉토리의 모든 파일 항목을 HTML로 렌더링하는 핫 루프(예: `process_dir` 안의 반복문)에서 인라인 변수를 포함하는 문자열 템플릿(`"""..."""` 또는 `"${...}"`)을 사용하면 여러 개의 중간 문자열과 빌더가 은밀하게 할당되어 GC 부하를 줍니다. +**조치:** 핫 루프 내에서는 복잡한 문자열 템플릿 대신 명시적으로 `StringBuilder.append()` 체이닝을 사용하고, 루프 안에서 변하지 않는 탈출된(escaped) 문자열(예: `fileName.escapeHtml()`)을 미리 평가하여 한 번만 변수에 담은 뒤 재사용하는 방식을 채택합니다. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 0972fa2c..9cbbb634 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -456,12 +456,19 @@ fun process_dir(curr_dir: File, excludeSet: Set? = null, dirFiles: Array } catch (e: Exception) { } if (!isSymbolicLink) { - val encodedHref = if (isLinkedDirectory) { "./${fileName.urlEncodePath()}/" } else { "./${fileName.urlEncodePath()}" } - val ariaLabel = "${fileName} ${if (isLinkedDirectory) { "디렉토리" } else { "파일" }}".escapeHtml() + // ⚡ Bolt Performance Optimization: Replace complex string interpolation with direct StringBuilder chaining + // String interpolation (`"""..."""` or `"${...}"`) inside a hot loop creates significant overhead + // through intermediate object allocations. Chaining `.append()` and reusing invariant escaped values + // is much faster and reduces garbage collection pressure. + val encodedHref = if (isLinkedDirectory) "./${fileName.urlEncodePath()}/" else "./${fileName.urlEncodePath()}" val typeLabel = if (isLinkedDirectory) { "디렉토리" } else { "파일" } + val escapedFileName = fileName.escapeHtml() val icon = if (isLinkedDirectory) { "📁" } else { "📄" } - l.append("""
  • ${fileName.escapeHtml()} ${typeLabel}
  • """) - l.append('\n') + + l.append("
  • ").append(icon) + .append(" ").append(escapedFileName).append(" ").append(typeLabel).append("
  • \n") } } } From 23104bbf2da9f044bfe3628082f8339b7ed6b8b6 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 01:47:46 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20=EB=AC=B8=EC=9E=90?= =?UTF-8?q?=EC=97=B4=20=ED=85=9C=ED=94=8C=EB=A6=BF(=EB=B3=B4=EA=B0=84)=20?= =?UTF-8?q?=EC=98=A4=EB=B2=84=ED=97=A4=EB=93=9C=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94=20[Empty]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From e90676a93875021d69979452f8a0a209475c6ed8 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 06:43:48 +0000 Subject: [PATCH 3/4] re-trigger CI From 7e3b2a8d311f08acb2a9b129d0317261596017bf Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:10:10 +0000 Subject: [PATCH 4/4] re-trigger CI