Merge pull request #54 from hobbyistlabs-coder/bolt/optimize-loop-utils-tolower-12691368552036696186

 Bolt: Optimize strings.ToLower allocations in hot paths
This commit is contained in:
hobbyistlabs-coder 2026-03-18 14:19:56 -04:00 committed by GitHub
commit e0b7a763c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 14 deletions

View file

@ -8,3 +8,6 @@
## 2024-05-26 - Avoid Unnecessary `strings.ToLower` ## 2024-05-26 - Avoid Unnecessary `strings.ToLower`
**Learning:** Calling `strings.ToLower` on the entire message content allocates a new string and iterates over all runes. This causes measurable GC pressure and latency on hot paths like feature extraction during routing. **Learning:** Calling `strings.ToLower` on the entire message content allocates a new string and iterates over all runes. This causes measurable GC pressure and latency on hot paths like feature extraction during routing.
**Action:** Use fast paths to bypass `strings.ToLower`. For instance, check if a requisite character (like a dot `.`) exists, or check common casings directly (`DATA:IMAGE` vs `data:image`) before falling back to full case-normalization. **Action:** Use fast paths to bypass `strings.ToLower`. For instance, check if a requisite character (like a dot `.`) exists, or check common casings directly (`DATA:IMAGE` vs `data:image`) before falling back to full case-normalization.
## 2025-03-20 - String Operations Fast Paths and Avoiding Double Searches
**Learning:** When trying to optimize `strings.ToLower`, ensure you don't introduce regressions with byte-to-rune casting on UTF-8 strings. Also, `strings.Contains(s, sub)` literally calls `strings.Index(s, sub)` under the hood. Using `strings.Contains` followed immediately by `strings.Index` to extract the position is an anti-pattern that searches the string twice, undermining the intended performance optimization.
**Action:** Always prefer a single `strings.Index` call over `Contains`+`Index`. Stick to one single optimization per PR to reduce risk and review burden.

View file

@ -102,9 +102,20 @@ func formatToolsForLog(toolDefs []providers.ToolDefinition) string {
// inferMediaType determines the media type ("image", "audio", "video", "file") // inferMediaType determines the media type ("image", "audio", "video", "file")
// from a filename and MIME content type. // from a filename and MIME content type.
func inferMediaType(filename, contentType string) string { func inferMediaType(filename, contentType string) string {
ct := strings.ToLower(contentType) // Fast path for common, correctly cased content types
fn := strings.ToLower(filename) if strings.HasPrefix(contentType, "image/") {
return "image"
}
if strings.HasPrefix(contentType, "audio/") || contentType == "application/ogg" {
return "audio"
}
if strings.HasPrefix(contentType, "video/") {
return "video"
}
// Slower path for uppercase or mixed case
if contentType != "" {
ct := strings.ToLower(contentType)
if strings.HasPrefix(ct, "image/") { if strings.HasPrefix(ct, "image/") {
return "image" return "image"
} }
@ -114,9 +125,13 @@ func inferMediaType(filename, contentType string) string {
if strings.HasPrefix(ct, "video/") { if strings.HasPrefix(ct, "video/") {
return "video" return "video"
} }
}
// Fallback: infer from extension // Fallback: infer from extension
ext := filepath.Ext(fn) ext := filepath.Ext(filename)
if ext != "" {
// Only lower case the extension, which is usually just a few characters
ext = strings.ToLower(ext)
switch ext { switch ext {
case ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg": case ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg":
return "image" return "image"
@ -125,6 +140,7 @@ func inferMediaType(filename, contentType string) string {
case ".mp4", ".avi", ".mov", ".webm", ".mkv": case ".mp4", ".avi", ".mov", ".webm", ".mkv":
return "video" return "video"
} }
}
return "file" return "file"
} }