test(lyrics): use real public-domain fixtures for parser benchmarks

Replace synthetic benchmark payloads with 'Auld Lang Syne' (Robert Burns,
1788, public domain) rendered into every supported format (LRC, plain,
enhanced LRC, SRT, TTML, Lyricsfile YAML) so the numbers reflect realistic
content. Same song across formats makes per-format cost comparable.

Baseline (Apple M-series, -benchmem, real fixtures):
  LRC          ~28 us/op   42 KB   147 allocs
  Plain        ~23 us/op   18 KB    22 allocs
  EnhancedLRC  ~37 us/op   51 KB   374 allocs
  SRT          ~52 us/op  139 KB   581 allocs
  TTML        ~119 us/op  276 KB  1227 allocs
  YAML        ~142 us/op  193 KB  1732 allocs
  Sniff(LRC)   ~47 us/op   57 KB   237 allocs
  Sniff(TTML) ~122 us/op  282 KB  1250 allocs
  Sniff(YAML) ~186 us/op  218 KB  1847 allocs

Fixtures in tests/fixtures/lyrics/.
This commit is contained in:
Deluan 2026-06-19 15:20:20 -04:00
commit 39dfc54e28
7 changed files with 309 additions and 58 deletions

View file

@ -1,59 +1,26 @@
package model
import (
"fmt"
"strings"
"os"
"path/filepath"
"testing"
)
// repeatLines builds a payload of n lines using the given per-line template,
// where %d is the line index, so benchmark inputs are realistically sized.
func repeatLines(n int, format string) string {
var sb strings.Builder
for i := 0; i < n; i++ {
fmt.Fprintf(&sb, format, i)
// Benchmark payloads are real public-domain lyrics ("Auld Lang Syne", Robert
// Burns, 1788) rendered into every supported format, so the numbers reflect
// realistic content and sizing. The same song across formats makes per-format
// cost directly comparable. Fixtures live in tests/fixtures/lyrics/.
func loadLyricFixture(b *testing.B, name string) []byte {
b.Helper()
contents, err := os.ReadFile(filepath.Join("..", "tests", "fixtures", "lyrics", name))
if err != nil {
b.Fatal(err)
}
return sb.String()
return contents
}
const benchLineCount = 200
func benchLRC() []byte {
return []byte(repeatLines(benchLineCount, "[00:%02d.00]lyric line number with some words\n"))
}
func benchPlain() []byte {
return []byte(repeatLines(benchLineCount, "lyric line number %d with some words\n"))
}
func benchEnhancedLRC() []byte {
return []byte(repeatLines(benchLineCount, "[00:%02d.00]<00:00.10>word <00:00.50>by <00:00.90>word\n"))
}
func benchSRT() []byte {
return []byte(repeatLines(benchLineCount, "%d\n00:00:01,000 --> 00:00:02,000\nsubtitle line\n\n"))
}
func benchTTML() []byte {
var sb strings.Builder
sb.WriteString(`<tt xmlns="http://www.w3.org/ns/ttml"><body><div>`)
for i := 0; i < benchLineCount; i++ {
fmt.Fprintf(&sb, `<p begin="00:00:%02d.000" end="00:00:%02d.000">ttml line</p>`, i%60, (i+1)%60)
}
sb.WriteString(`</div></body></tt>`)
return []byte(sb.String())
}
func benchYAML() []byte {
var sb strings.Builder
sb.WriteString("version: \"1.0\"\nmetadata:\n language: eng\nlines:\n")
for i := 0; i < benchLineCount; i++ {
fmt.Fprintf(&sb, " - text: yaml line %d\n start_ms: %d\n", i, i*1000)
}
return []byte(sb.String())
}
func benchmarkParse(b *testing.B, suffix string, contents []byte) {
func benchmarkParse(b *testing.B, suffix, fixture string) {
contents := loadLyricFixture(b, fixture)
b.ReportAllocs()
b.SetBytes(int64(len(contents)))
for b.Loop() {
@ -63,16 +30,16 @@ func benchmarkParse(b *testing.B, suffix string, contents []byte) {
}
}
func BenchmarkParseLyrics_LRC(b *testing.B) { benchmarkParse(b, ".lrc", benchLRC()) }
func BenchmarkParseLyrics_Plain(b *testing.B) { benchmarkParse(b, ".txt", benchPlain()) }
func BenchmarkParseLyrics_EnhancedLRC(b *testing.B) { benchmarkParse(b, ".lrc", benchEnhancedLRC()) }
func BenchmarkParseLyrics_SRT(b *testing.B) { benchmarkParse(b, ".srt", benchSRT()) }
func BenchmarkParseLyrics_TTML(b *testing.B) { benchmarkParse(b, ".ttml", benchTTML()) }
func BenchmarkParseLyrics_YAML(b *testing.B) { benchmarkParse(b, ".yaml", benchYAML()) }
func BenchmarkParseLyrics_LRC(b *testing.B) { benchmarkParse(b, ".lrc", "auld-lang-syne.lrc") }
func BenchmarkParseLyrics_Plain(b *testing.B) { benchmarkParse(b, ".txt", "auld-lang-syne.txt") }
func BenchmarkParseLyrics_EnhancedLRC(b *testing.B) { benchmarkParse(b, ".lrc", "auld-lang-syne.elrc") }
func BenchmarkParseLyrics_SRT(b *testing.B) { benchmarkParse(b, ".srt", "auld-lang-syne.srt") }
func BenchmarkParseLyrics_TTML(b *testing.B) { benchmarkParse(b, ".ttml", "auld-lang-syne.ttml") }
func BenchmarkParseLyrics_YAML(b *testing.B) { benchmarkParse(b, ".yaml", "auld-lang-syne.yaml") }
// Content-sniff path (empty suffix) — what embedded tags and plugins hit.
func BenchmarkParseLyrics_SniffTTML(b *testing.B) { benchmarkParse(b, "", benchTTML()) }
func BenchmarkParseLyrics_SniffSRT(b *testing.B) { benchmarkParse(b, "", benchSRT()) }
func BenchmarkParseLyrics_SniffYAML(b *testing.B) { benchmarkParse(b, "", benchYAML()) }
func BenchmarkParseLyrics_SniffLRC(b *testing.B) { benchmarkParse(b, "", benchLRC()) }
func BenchmarkParseLyrics_SniffPlain(b *testing.B) { benchmarkParse(b, "", benchPlain()) }
func BenchmarkParseLyrics_SniffTTML(b *testing.B) { benchmarkParse(b, "", "auld-lang-syne.ttml") }
func BenchmarkParseLyrics_SniffSRT(b *testing.B) { benchmarkParse(b, "", "auld-lang-syne.srt") }
func BenchmarkParseLyrics_SniffYAML(b *testing.B) { benchmarkParse(b, "", "auld-lang-syne.yaml") }
func BenchmarkParseLyrics_SniffLRC(b *testing.B) { benchmarkParse(b, "", "auld-lang-syne.lrc") }
func BenchmarkParseLyrics_SniffPlain(b *testing.B) { benchmarkParse(b, "", "auld-lang-syne.txt") }

View file

@ -0,0 +1,11 @@
[ar:Robert Burns]
[ti:Auld Lang Syne]
[lang:eng]
[00:00.00]<00:00.00>Should <00:00.90>auld <00:01.80>acquaintance <00:02.70>be <00:03.60>forgot,
[00:04.50]<00:04.50>And <00:05.40>never <00:06.30>brought <00:07.20>to <00:08.10>mind?
[00:09.00]<00:09.00>Should <00:09.90>auld <00:10.80>acquaintance <00:11.70>be <00:12.60>forgot,
[00:13.50]<00:13.50>And <00:14.40>auld <00:15.30>lang <00:16.20>syne?
[00:18.00]<00:18.00>For <00:18.90>auld <00:19.80>lang <00:20.70>syne, <00:21.60>my <00:22.05>dear,
[00:22.50]<00:22.50>For <00:23.40>auld <00:24.30>lang <00:25.20>syne,
[00:27.00]<00:27.00>We'll <00:27.90>tak <00:28.80>a <00:29.25>cup <00:30.15>o' <00:30.60>kindness <00:31.05>yet,
[00:31.50]<00:31.50>For <00:32.40>auld <00:33.30>lang <00:34.20>syne.

View file

@ -0,0 +1,28 @@
[ar:Robert Burns]
[ti:Auld Lang Syne]
[al:Traditional]
[lang:eng]
[00:00.00]Should auld acquaintance be forgot,
[00:04.50]And never brought to mind?
[00:09.00]Should auld acquaintance be forgot,
[00:13.50]And auld lang syne?
[00:18.00]For auld lang syne, my dear,
[00:22.50]For auld lang syne,
[00:27.00]We'll tak a cup o' kindness yet,
[00:31.50]For auld lang syne.
[00:36.00]And surely ye'll be your pint-stowp,
[00:40.50]And surely I'll be mine,
[00:45.00]And we'll tak a cup o' kindness yet,
[00:49.50]For auld lang syne.
[00:54.00]We twa hae run about the braes,
[00:58.50]And pou'd the gowans fine,
[01:03.00]But we've wander'd mony a weary fit,
[01:07.50]Sin auld lang syne.
[01:12.00]We twa hae paidl'd in the burn,
[01:16.50]Frae morning sun till dine,
[01:21.00]But seas between us braid hae roar'd
[01:25.50]Sin auld lang syne.
[01:30.00]And there's a hand, my trusty fiere,
[01:34.50]And gie's a hand o' thine,
[01:39.00]And we'll tak a right gude-willie waught,
[01:43.50]For auld lang syne.

View file

@ -0,0 +1,95 @@
1
00:00:00,000 --> 00:00:04,500
Should auld acquaintance be forgot,
2
00:00:04,500 --> 00:00:09,000
And never brought to mind?
3
00:00:09,000 --> 00:00:13,500
Should auld acquaintance be forgot,
4
00:00:13,500 --> 00:00:18,000
And auld lang syne?
5
00:00:18,000 --> 00:00:22,500
For auld lang syne, my dear,
6
00:00:22,500 --> 00:00:27,000
For auld lang syne,
7
00:00:27,000 --> 00:00:31,500
We'll tak a cup o' kindness yet,
8
00:00:31,500 --> 00:00:36,000
For auld lang syne.
9
00:00:36,000 --> 00:00:40,500
And surely ye'll be your pint-stowp,
10
00:00:40,500 --> 00:00:45,000
And surely I'll be mine,
11
00:00:45,000 --> 00:00:49,500
And we'll tak a cup o' kindness yet,
12
00:00:49,500 --> 00:00:54,000
For auld lang syne.
13
00:00:54,000 --> 00:00:58,500
We twa hae run about the braes,
14
00:00:58,500 --> 00:01:03,000
And pou'd the gowans fine,
15
00:01:03,000 --> 00:01:07,500
But we've wander'd mony a weary fit,
16
00:01:07,500 --> 00:01:12,000
Sin auld lang syne.
17
00:01:12,000 --> 00:01:16,500
We twa hae paidl'd in the burn,
18
00:01:16,500 --> 00:01:21,000
Frae morning sun till dine,
19
00:01:21,000 --> 00:01:25,500
But seas between us braid hae roar'd
20
00:01:25,500 --> 00:01:30,000
Sin auld lang syne.
21
00:01:30,000 --> 00:01:34,500
And there's a hand, my trusty fiere,
22
00:01:34,500 --> 00:01:39,000
And gie's a hand o' thine,
23
00:01:39,000 --> 00:01:43,500
And we'll tak a right gude-willie waught,
24
00:01:43,500 --> 00:01:48,000
For auld lang syne.

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xml:lang="eng">
<body>
<div>
<p begin="00:00:00.000" end="00:00:04.500">Should auld acquaintance be forgot,</p>
<p begin="00:00:04.500" end="00:00:09.000">And never brought to mind?</p>
<p begin="00:00:09.000" end="00:00:13.500">Should auld acquaintance be forgot,</p>
<p begin="00:00:13.500" end="00:00:18.000">And auld lang syne?</p>
<p begin="00:00:18.000" end="00:00:22.500">For auld lang syne, my dear,</p>
<p begin="00:00:22.500" end="00:00:27.000">For auld lang syne,</p>
<p begin="00:00:27.000" end="00:00:31.500">We'll tak a cup o' kindness yet,</p>
<p begin="00:00:31.500" end="00:00:36.000">For auld lang syne.</p>
<p begin="00:00:36.000" end="00:00:40.500">And surely ye'll be your pint-stowp,</p>
<p begin="00:00:40.500" end="00:00:45.000">And surely I'll be mine,</p>
<p begin="00:00:45.000" end="00:00:49.500">And we'll tak a cup o' kindness yet,</p>
<p begin="00:00:49.500" end="00:00:54.000">For auld lang syne.</p>
<p begin="00:00:54.000" end="00:00:58.500">We twa hae run about the braes,</p>
<p begin="00:00:58.500" end="00:01:03.000">And pou'd the gowans fine,</p>
<p begin="00:01:03.000" end="00:01:07.500">But we've wander'd mony a weary fit,</p>
<p begin="00:01:07.500" end="00:01:12.000">Sin auld lang syne.</p>
<p begin="00:01:12.000" end="00:01:16.500">We twa hae paidl'd in the burn,</p>
<p begin="00:01:16.500" end="00:01:21.000">Frae morning sun till dine,</p>
<p begin="00:01:21.000" end="00:01:25.500">But seas between us braid hae roar'd</p>
<p begin="00:01:25.500" end="00:01:30.000">Sin auld lang syne.</p>
<p begin="00:01:30.000" end="00:01:34.500">And there's a hand, my trusty fiere,</p>
<p begin="00:01:34.500" end="00:01:39.000">And gie's a hand o' thine,</p>
<p begin="00:01:39.000" end="00:01:43.500">And we'll tak a right gude-willie waught,</p>
<p begin="00:01:43.500" end="00:01:48.000">For auld lang syne.</p>
</div>
</body>
</tt>

View file

@ -0,0 +1,24 @@
Should auld acquaintance be forgot,
And never brought to mind?
Should auld acquaintance be forgot,
And auld lang syne?
For auld lang syne, my dear,
For auld lang syne,
We'll tak a cup o' kindness yet,
For auld lang syne.
And surely ye'll be your pint-stowp,
And surely I'll be mine,
And we'll tak a cup o' kindness yet,
For auld lang syne.
We twa hae run about the braes,
And pou'd the gowans fine,
But we've wander'd mony a weary fit,
Sin auld lang syne.
We twa hae paidl'd in the burn,
Frae morning sun till dine,
But seas between us braid hae roar'd
Sin auld lang syne.
And there's a hand, my trusty fiere,
And gie's a hand o' thine,
And we'll tak a right gude-willie waught,
For auld lang syne.

View file

@ -0,0 +1,95 @@
version: '1.0'
metadata:
title: 'Auld Lang Syne'
artist: 'Robert Burns'
album: 'Traditional'
language: 'eng'
lines:
- text: "Should auld acquaintance be forgot,"
start_ms: 0
end_ms: 4500
words:
- text: "Should "
start_ms: 0
end_ms: 900
- text: "auld "
start_ms: 900
end_ms: 1800
- text: "acquaintance "
start_ms: 1800
end_ms: 2700
- text: "be "
start_ms: 2700
end_ms: 3600
- text: "forgot,"
start_ms: 3600
end_ms: 4500
- text: "And never brought to mind?"
start_ms: 4500
end_ms: 9000
- text: "Should auld acquaintance be forgot,"
start_ms: 9000
end_ms: 13500
- text: "And auld lang syne?"
start_ms: 13500
end_ms: 18000
- text: "For auld lang syne, my dear,"
start_ms: 18000
end_ms: 22500
- text: "For auld lang syne,"
start_ms: 22500
end_ms: 27000
- text: "We'll tak a cup o' kindness yet,"
start_ms: 27000
end_ms: 31500
- text: "For auld lang syne."
start_ms: 31500
end_ms: 36000
- text: "And surely ye'll be your pint-stowp,"
start_ms: 36000
end_ms: 40500
- text: "And surely I'll be mine,"
start_ms: 40500
end_ms: 45000
- text: "And we'll tak a cup o' kindness yet,"
start_ms: 45000
end_ms: 49500
- text: "For auld lang syne."
start_ms: 49500
end_ms: 54000
- text: "We twa hae run about the braes,"
start_ms: 54000
end_ms: 58500
- text: "And pou'd the gowans fine,"
start_ms: 58500
end_ms: 63000
- text: "But we've wander'd mony a weary fit,"
start_ms: 63000
end_ms: 67500
- text: "Sin auld lang syne."
start_ms: 67500
end_ms: 72000
- text: "We twa hae paidl'd in the burn,"
start_ms: 72000
end_ms: 76500
- text: "Frae morning sun till dine,"
start_ms: 76500
end_ms: 81000
- text: "But seas between us braid hae roar'd"
start_ms: 81000
end_ms: 85500
- text: "Sin auld lang syne."
start_ms: 85500
end_ms: 90000
- text: "And there's a hand, my trusty fiere,"
start_ms: 90000
end_ms: 94500
- text: "And gie's a hand o' thine,"
start_ms: 94500
end_ms: 99000
- text: "And we'll tak a right gude-willie waught,"
start_ms: 99000
end_ms: 103500
- text: "For auld lang syne."
start_ms: 103500
end_ms: 108000