Performance Guide
Tips and best practices for writing efficient Scriptling code.
String Concatenation
The Problem
String concatenation with += in loops is slow because each operation creates a new string object:
# SLOW - Creates 1000 string objects
result = ""
for i in range(1000):
result += str(i) # Each += creates a new stringPerformance: 3.45 ms, 25.9 MB for 10,000 iterations
Solution: Use join()
# FAST - Efficient and Python-compatible
parts = []
for i in range(1000):
parts.append(str(i))
result = "".join(parts)Performance: ~36 μs for 1000 iterations (70x faster!)
Why it’s fast: join() pre-allocates the exact amount of memory needed and copies all strings once.
When += is OK
# OK - Fine for small numbers
result = "hello" + " " + "world"
# or
result = "hello"
result += " "
result += "world"When to use:
- Concatenating < 10 strings
- Outside of loops
- Readability matters more
Examples
Building CSV
# SLOW
csv = ""
for row in data:
csv += ",".join(row) + "\n"
# FAST - Using join()
lines = []
for row in data:
lines.append(",".join(row))
csv = "\n".join(lines)Building HTML
# SLOW
html = "<ul>"
for item in items:
html += "<li>" + item + "</li>"
html += "</ul>"
# FAST - Using join()
parts = ["<ul>"]
for item in items:
parts.append("<li>" + item + "</li>")
parts.append("</ul>")
html = "".join(parts)Building JSON-like Strings
# SLOW
json_str = "["
for i, item in enumerate(items):
if i > 0:
json_str += ", "
json_str += "\"" + item + "\""
json_str += "]"
# FAST - Using join()
parts = ["\"" + item + "\"" for item in items]
json_str = "[" + ", ".join(parts) + "]"
# EVEN BETTER - Use json library
import json
json_str = json.dumps(items)Recursion vs Iteration
The Problem
Deep recursion creates many function call frames and environment copies:
# SLOW - Deep recursion
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
result = fib(10) # 114 μs, 376 KB, 3,983 allocationsSolution: Use Iteration
# FAST - Iterative approach
def fib(n):
if n <= 1:
return n
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
result = fib(10) # Much faster, constant memoryWhen Recursion is OK
Recursion is fine for:
- Tree/graph traversal (limited depth)
- Divide-and-conquer algorithms
- Naturally recursive problems with small depth
Avoid recursion for:
- Problems with deep recursion (> 100 levels)
- Problems that can be solved iteratively
- Performance-critical code
List Operations
Pre-allocate When Size is Known
# SLOW - Multiple reallocations
items = []
for i in range(1000):
items.append(i)
# OK - List comprehension (often faster)
items = [i for i in range(1000)]Avoid Repeated lookups
# SLOW - Looks up data["items"] each time
for i in range(len(data["items"])):
process(data["items"][i])
# FAST - Cache the reference
items = data["items"]
for i in range(len(items)):
process(items[i])
# EVEN BETTER - Iterate directly
for item in data["items"]:
process(item)Dictionary Operations
Use get() for Optional Keys
# SLOW - Exception handling overhead
try:
value = data["key"]
except KeyError:
value = default
# FAST - No exception overhead
value = data.get("key", default)Check Membership Efficiently
# FAST - O(1) lookup
if key in data:
value = data[key]
# Also FAST - Single lookup with default
value = data.get(key)
if value is not None:
# Use valueGeneral Tips
- Profile before optimizing: Use benchmarks to find real bottlenecks
- Readability first: Optimize only when needed
- Use built-in functions: They’re already optimized
- Avoid premature optimization: Write clear code first
- Prefer iteration over recursion: For deep operations
- Use join() for string building: In loops
Benchmarking Your Code
import time
# Measure execution time
start = time.time()
# Your code here
result = []
for i in range(10000):
result.append(i)
end = time.time()
print("Took " + str((end - start) * 1000) + " ms")Benchmark Function
def benchmark(name, func, iterations=1000):
import time
start = time.time()
for _ in range(iterations):
func()
end = time.time()
ms = (end - start) * 1000
print(name + ": " + str(ms) + " ms (" + str(ms/iterations) + " ms/iter)")
# Usage
benchmark("string += ", func=lambda: slow_concat(), iterations=100)
benchmark("join() ", func=lambda: fast_concat(), iterations=100)Summary
| Pattern | Avoid | Prefer |
|---|---|---|
| String building in loops | result += str(i) |
"".join(parts) |
| Deep recursion | fib(n-1) + fib(n-2) |
Iterative loop |
| Optional dict access | try/except KeyError |
dict.get(key, default) |
| List building | Multiple append() in simple cases |
List comprehension |
| Repeated lookups | data["key"] in loop |
Cache reference |
See Also
- Built-in Functions - String and list functions
- HTTP & JSON - Using the json library