diff --git a/.gitignore b/.gitignore index 54a5f5de..943bdf17 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ # Ignore Gradle build output directory build -.idea \ No newline at end of file +.idea +perf_test/ diff --git a/.jules/bolt.md b/.jules/bolt.md index 39f32f6e..9e53d6a3 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,7 @@ -## 2024-06-21 - Regex Compilation in Loops -**Learning:** In Kotlin, compiling regular expressions (`.toRegex()`) inside a loop over files is a significant O(N * M) performance bottleneck when processing ignore files (N files * M rules). -**Action:** Always map string rules to compiled `Regex` objects outside of the file iteration loop (O(M) compilation) to avoid unnecessary regex re-compilations. +## 2024-05-18 - StringBuilder over String Concatenation in Kotlin +**Learning:** In Kotlin (and Java), using `+=` for string concatenation within a loop has an O(N²) time complexity due to the immutability of `String`, which requires creating a new `String` and copying the contents on every iteration. This can become a significant bottleneck when processing large collections (like thousands of directory files). +**Action:** Always prefer using a `StringBuilder` (and ideally initializing it with an estimated capacity) for string accumulation inside loops to maintain O(N) performance and avoid unnecessary memory allocations. + +## 2024-05-18 - StringBuilder over String Concatenation in Kotlin +**Learning:** In Kotlin (and Java), using `+=` for string concatenation within a loop has an O(N²) time complexity due to the immutability of `String`, which requires creating a new `String` and copying the contents on every iteration. This can become a significant bottleneck when processing large collections (like thousands of directory files). +**Action:** Always prefer using a `StringBuilder` (and ideally initializing it with an estimated capacity) for string accumulation inside loops to maintain O(N) performance and avoid unnecessary memory allocations. diff --git a/src/main/kotlin/html4tree/main.kt b/src/main/kotlin/html4tree/main.kt index 50b2680d..02cf4daa 100644 --- a/src/main/kotlin/html4tree/main.kt +++ b/src/main/kotlin/html4tree/main.kt @@ -124,18 +124,22 @@ fun process_dir(curr_dir: File){ """ val index_middle = fun():String{ - var l="" - val dir_files: MutableList = curr_dir.listFiles()?.toMutableList() ?: mutableListOf() dir_files.sortWith(compareBy ({it.name}) ) + + // ⚡ Bolt: 문자열 결합 성능 최적화 (O(N^2) -> O(N)) + // StringBuilder를 사용하여 리스트 항목들을 하나로 합칩니다. + // dir_files의 크기를 기반으로 초기 용량을 설정하여 재할당 오버헤드를 방지합니다. + val l = StringBuilder(dir_files.size * 250) + dir_files.forEach { val isLinkedDirectory = it.isDirectory() && !java.nio.file.Files.isSymbolicLink(it.toPath()) if((it.getName() !in exclude) && (isLinkedDirectory || !it.isDirectory())) { - l += """
  • ${if (isLinkedDirectory) { "📁" } else { "▸" }} ${it.getName().escapeHtml()}
  • """+"\n" + l.append("""
  • ${if (isLinkedDirectory) { "📁" } else { "▸" }} ${it.getName().escapeHtml()}
  • """+"\n") } } - return l; + return l.toString() } val index_bottom=""" diff --git a/src/test/kotlin/html4tree/HelpTest.kt b/src/test/kotlin/html4tree/HelpTest.kt new file mode 100644 index 00000000..20976e8b --- /dev/null +++ b/src/test/kotlin/html4tree/HelpTest.kt @@ -0,0 +1,23 @@ +package html4tree + +import org.junit.Test +import kotlin.test.assertEquals +import java.io.ByteArrayOutputStream +import java.io.PrintStream + +class HelpTest { + @Test + fun testHelp() { + val originalOut = System.out + val baos = ByteArrayOutputStream() + val ps = PrintStream(baos) + System.setOut(ps) + + try { + help() + assertEquals("ERROR: help has not been written yet!\n", baos.toString().replace("\r\n", "\n")) + } finally { + System.setOut(originalOut) + } + } +}