From 893bd9062285a743c86aed216e99658df70da5af Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 8 Jul 2026 20:46:21 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=EC=84=B1=EB=8A=A5=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94:=20urlEncodePath=EC=97=90=EC=84=9C=20=EA=B0=80?= =?UTF-8?q?=EB=B9=84=EC=A7=80=20=EC=BB=AC=EB=A0=89=EC=85=98(GC)=20?= =?UTF-8?q?=EC=98=A4=EB=B2=84=ED=97=A4=EB=93=9C=20=EA=B0=90=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 루프 내에서 바이트를 16진수 문자열로 변환하고 패딩 처리하는 과정(`toString(16).padStart(2, '0').toUpperCase()`)을 비트 연산(`ushr`, `and`)과 미리 할당된 16진수 문자 배열 조회를 사용하여 개선했습니다. - 지연 초기화(lazy initialization) 방식의 StringBuilder를 도입하여, 인코딩이 필요 없는 문자열의 경우 불필요한 메모리 할당 없이 즉시 원본 문자열을 반환하도록 했습니다. - 이 변경으로 인코딩이 잦은 경로에서 불필요한 중간 문자열 할당을 없애 실행 속도와 메모리 효율을 크게 높였습니다. - 관련 내용을 .jules/bolt.md 에 기록했습니다. --- .jules/bolt.md | 4 ++++ src/main/kotlin/html4tree/main.kt | 23 +++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 6904de2..49ce467 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -9,3 +9,7 @@ ## 2024-07-26 - Intermediate String Allocations **Learning:** Chained `.replace()` calls on strings in Kotlin (e.g. for HTML escaping) allocate an intermediate String at each step, significantly impacting performance and garbage collection on hot paths with many elements. **Action:** Replace chained `.replace()` calls with a single-pass loop that iterates over characters once, lazily initializing a `StringBuilder` to append the transformed output. + +## 2024-07-31 - GC Overhead from String Padding in Loops +**Learning:** Using `byte.toString(16).padStart(2, '0').toUpperCase()` for hex encoding inside a loop generates significant GC overhead due to multiple intermediate string allocations per byte. +**Action:** Use bitwise shifts (`ushr` and `and`) with a pre-allocated array of hex characters (e.g. `val hexChars = "0123456789ABCDEF"`) to map bytes directly to hex characters, and combine it with a lazy `StringBuilder` to minimize allocations. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 1710d99..46deee4 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -80,10 +80,14 @@ fun String.escapeHtml(): String { return sb?.toString() ?: this } +// ⚡ Bolt Performance Optimization: Reduce GC overhead in urlEncodePath fun String.urlEncodePath(): String { - val encoded = StringBuilder() - this.toByteArray(Charsets.UTF_8).forEach { - val byte = it.toInt() and 0xff + var encoded: StringBuilder? = null + val bytes = this.toByteArray(Charsets.UTF_8) + val hexChars = "0123456789ABCDEF" + + for (i in bytes.indices) { + val byte = bytes[i].toInt() and 0xff val isUnreserved = (byte in 'A'.toInt()..'Z'.toInt()) || (byte in 'a'.toInt()..'z'.toInt()) || (byte in '0'.toInt()..'9'.toInt()) || @@ -92,13 +96,20 @@ fun String.urlEncodePath(): String { byte == '_'.toInt() || byte == '~'.toInt() if (isUnreserved) { - encoded.append(byte.toChar()) + encoded?.append(byte.toChar()) } else { + if (encoded == null) { + encoded = StringBuilder(bytes.size + 16) + for (j in 0 until i) { + encoded.append(bytes[j].toChar()) + } + } encoded.append('%') - encoded.append(byte.toString(16).padStart(2, '0').toUpperCase()) + encoded.append(hexChars[byte ushr 4]) + encoded.append(hexChars[byte and 0x0F]) } } - return encoded.toString() + return encoded?.toString() ?: this } fun process_ignore_file(curr_dir: File): Set { From 494bc522b60c4d32ce2db05bd88a5a5afd31d2d2 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 9 Jul 2026 16:19:49 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=EC=84=B1=EB=8A=A5=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94:=20urlEncodePath=EC=97=90=EC=84=9C=20=EA=B0=80?= =?UTF-8?q?=EB=B9=84=EC=A7=80=20=EC=BB=AC=EB=A0=89=EC=85=98(GC)=20?= =?UTF-8?q?=EC=98=A4=EB=B2=84=ED=97=A4=EB=93=9C=20=EA=B0=90=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 루프 내에서 바이트를 16진수 문자열로 변환하고 패딩 처리하는 과정(`toString(16).padStart(2, '0').toUpperCase()`)을 비트 연산(`ushr`, `and`)과 미리 할당된 16진수 문자 배열 조회를 사용하여 개선했습니다. - 지연 초기화(lazy initialization) 방식의 StringBuilder를 도입하여, 인코딩이 필요 없는 문자열의 경우 불필요한 메모리 할당 없이 즉시 원본 문자열을 반환하도록 했습니다. - 이 변경으로 인코딩이 잦은 경로에서 불필요한 중간 문자열 할당을 없애 실행 속도와 메모리 효율을 크게 높였습니다. - 관련 내용을 .jules/bolt.md 에 기록했습니다.