Context
string.format with width-flagged specifiers is 2.03× slower than luerl
(width-flagged, n=1000: 5.51ms vs 2.71ms in benchmarks/string_format.exs).
The literal-heavy fast path from PR #299 is now winning by 2.6×, so the
remaining work is in per-specifier width padding.
apply_width_flags/3 at lib/lua/vm/stdlib/string.ex:828 builds
String.duplicate(pad_char, deficit) and concatenates with <>, producing a
fresh binary per specifier. Luerl returns [Padding, Bin] as iolist
(deps/luerl/src/luerl_lib_string_format.erl:303-319) and lets the final
iolist_to_binary materialise everything at once.
Change
lib/lua/vm/stdlib/string.ex:828-859 plus its caller in format_string/3.
Verification
mix test test/lua/vm/stdlib/string_test.exs
- Full
mix test
mix run benchmarks/string_format.exs
Expected
Closes most of the 1.5–2× width-flag gap.
Context
string.formatwith width-flagged specifiers is 2.03× slower than luerl(width-flagged, n=1000: 5.51ms vs 2.71ms in
benchmarks/string_format.exs).The literal-heavy fast path from PR #299 is now winning by 2.6×, so the
remaining work is in per-specifier width padding.
apply_width_flags/3atlib/lua/vm/stdlib/string.ex:828buildsString.duplicate(pad_char, deficit)and concatenates with<>, producing afresh binary per specifier. Luerl returns
[Padding, Bin]as iolist(
deps/luerl/src/luerl_lib_string_format.erl:303-319) and lets the finaliolist_to_binarymaterialise everything at once.Change
lib/lua/vm/stdlib/string.ex:828-859plus its caller informat_string/3.apply_width_flagsreturns iolist ([pad, str]/[str, pad]) insteadof a binary.
format_stringso the existing accumulator fromPR perf(stdlib): iolist string.format and plain-table sort/concat fast paths #299 absorbs the padding.
IO.iodata_to_binary/1.Verification
mix test test/lua/vm/stdlib/string_test.exsmix testmix run benchmarks/string_format.exsExpected
Closes most of the 1.5–2× width-flag gap.