Skip to content

Commit dba6edf

Browse files
committed
⚡ Bolt: Replace string concatenation in loops with list join for O(N) performance
1 parent 30dc173 commit dba6edf

2 files changed

Lines changed: 13 additions & 7 deletions

File tree

.jules/bolt.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
## 2024-05-24 - [Regex Compilation Overhead]
22
**Learning:** Python`s `re.compile()` has some internal caching, but doing it in a tight loop across multiple text chunks or items still introduces measurable overhead.
33
**Action:** When a method applies dictionary/lexicon replacements via regular expressions, explicitly cache the compiled regex objects in a class attribute or closure, rather than compiling them on every call.
4+
## 2024-05-24 - [String Concatenation Bottleneck]
5+
**Learning:** Using `+=` for string concatenation inside loops (like iterating over PDF pages or large document segments) forces Python to create a new string object each time, leading to O(N²) time complexity.
6+
**Action:** Always accumulate strings in a list using `.append()` and combine them once at the end using `"".join(list)` to maintain O(N) performance.

kokoro_engine.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -433,28 +433,29 @@ def extract_text_from_file(self, fpath):
433433
if not os.path.exists(fpath):
434434
raise FileNotFoundError("File does not exist.")
435435

436-
text_data = ""
436+
text_data_list = []
437437
lower_path = fpath.lower()
438438

439+
# Performance optimization: Use list accumulation instead of += for O(N) string building
439440
if lower_path.endswith(".pdf"):
440441
reader = pypdf.PdfReader(fpath)
441442
for page in reader.pages:
442443
extracted = page.extract_text()
443444
if extracted:
444-
text_data += extracted + "\n\n"
445+
text_data_list.append(extracted + "\n\n")
445446

446447
elif lower_path.endswith(".epub"):
447448
book = epub.read_epub(fpath, options={'ignore_ncx': True})
448449
for item in book.get_items():
449450
if item.get_type() == ebooklib.ITEM_DOCUMENT:
450451
soup = BeautifulSoup(item.get_content(), 'html.parser')
451-
text_data += soup.get_text(separator='\n\n') + "\n\n"
452+
text_data_list.append(soup.get_text(separator='\n\n') + "\n\n")
452453
else:
453454
# Assume text based
454455
with open(fpath, "r", encoding="utf-8") as f:
455-
text_data = f.read()
456+
text_data_list.append(f.read())
456457

457-
return text_data
458+
return "".join(text_data_list)
458459

459460
def parse_multispeaker_text(self, text):
460461
"""
@@ -890,9 +891,11 @@ async def playback_loop():
890891
else:
891892
first_remaining_idx = 0
892893

893-
remaining_text = ""
894+
# Performance optimization: Use list accumulation instead of += for O(N) string building
895+
remaining_text_list = []
894896
for i in range(first_remaining_idx, total_segments):
895-
remaining_text += all_text_segments[i][0] + "\n\n"
897+
remaining_text_list.append(all_text_segments[i][0] + "\n\n")
898+
remaining_text = "".join(remaining_text_list)
896899

897900
if remaining_text:
898901
rem_path = os.path.join(config['out_dir'], f"{config['filename']}_{config['time_id']}_remaining.txt")

0 commit comments

Comments
 (0)