From 0d117eabc863cbabc286b8ce47162259c7bf0f33 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 14 Apr 2026 23:49:45 +0300 Subject: [PATCH] cgen: optimize string interpolation performance (fixes #7516) --- vlib/v/gen/c/str_intp.v | 78 +++++++++++++++++++ ...erpolation_simple_optimization.c.must_have | 3 + ...tring_interpolation_simple_optimization.vv | 17 ++++ 3 files changed, 98 insertions(+) create mode 100644 vlib/v/gen/c/testdata/string_interpolation_simple_optimization.c.must_have create mode 100644 vlib/v/gen/c/testdata/string_interpolation_simple_optimization.vv diff --git a/vlib/v/gen/c/str_intp.v b/vlib/v/gen/c/str_intp.v index cd5d73434..f7828072f 100644 --- a/vlib/v/gen/c/str_intp.v +++ b/vlib/v/gen/c/str_intp.v @@ -629,6 +629,9 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { else {} } } + if g.gen_simple_string_inter_literal(node_, fmts) { + return + } g.write2('builtin__str_intp(', node.vals.len.str()) g.write(', _MOV((StrIntpData[]){') for i, val in node.vals { @@ -709,3 +712,78 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { } g.write('}))') } + +const simple_string_interpolation_default_precision = 987698 + +fn (mut g Gen) gen_simple_string_inter_literal(node ast.StringInterLiteral, fmts []u8) bool { + if node.exprs.len == 0 || node.expr_types.len < node.exprs.len { + return false + } + for i in 0 .. node.exprs.len { + if i >= node.need_fmts.len || node.need_fmts[i] || i >= fmts.len || fmts[i] == `_` { + return false + } + if node.expr_types[i].is_any_kind_of_pointer() || node.expr_types[i].is_int_valptr() + || node.expr_types[i].is_float_valptr() { + return false + } + if i < node.fwidths.len && node.fwidths[i] != 0 { + return false + } + if i < node.fwidth_exprs.len && node.fwidth_exprs[i] !is ast.EmptyExpr { + return false + } + if i < node.precisions.len + && node.precisions[i] != simple_string_interpolation_default_precision { + return false + } + if i < node.precision_exprs.len && node.precision_exprs[i] !is ast.EmptyExpr { + return false + } + if i < node.pluss.len && node.pluss[i] { + return false + } + if i < node.fills.len && node.fills[i] { + return false + } + } + if node.exprs.len == 1 && node.vals.len == 2 && node.vals[0].len == 0 && node.vals[1].len == 0 { + g.gen_expr_to_string(node.exprs[0], node.expr_types[0]) + return true + } + mut part_count := 0 + for i, val in node.vals { + if val.len > 0 { + part_count++ + } + if i < node.exprs.len { + part_count++ + } + } + if part_count <= 0 { + return false + } + g.write('builtin__string_plus_many(${part_count}, _MOV((string[${part_count}]){') + mut written_parts := 0 + for i, val in node.vals { + if val.len > 0 { + if written_parts > 0 { + g.write(', ') + } + mut escaped_val := cescape_nonascii(util.smart_quote(val, false)) + escaped_val = escaped_val.replace('\0', '\\0') + g.write2('_S("', escaped_val) + g.write('")') + written_parts++ + } + if i < node.exprs.len { + if written_parts > 0 { + g.write(', ') + } + g.gen_expr_to_string(node.exprs[i], node.expr_types[i]) + written_parts++ + } + } + g.write('}))') + return true +} diff --git a/vlib/v/gen/c/testdata/string_interpolation_simple_optimization.c.must_have b/vlib/v/gen/c/testdata/string_interpolation_simple_optimization.c.must_have new file mode 100644 index 000000000..ba07c248f --- /dev/null +++ b/vlib/v/gen/c/testdata/string_interpolation_simple_optimization.c.must_have @@ -0,0 +1,3 @@ +return builtin__int_str(n); +builtin__string_plus_many(3, _MOV((string[3]){prefix, builtin__int_str(n), suffix})) +builtin__string_plus_many(4, _MOV((string[4]){_S("n="), builtin__int_str(n), _S(":"), suffix})) diff --git a/vlib/v/gen/c/testdata/string_interpolation_simple_optimization.vv b/vlib/v/gen/c/testdata/string_interpolation_simple_optimization.vv new file mode 100644 index 000000000..2d310cca0 --- /dev/null +++ b/vlib/v/gen/c/testdata/string_interpolation_simple_optimization.vv @@ -0,0 +1,17 @@ +fn interp_number(n int) string { + return '${n}' +} + +fn interp_chain(prefix string, n int, suffix string) string { + return '${prefix}${n}${suffix}' +} + +fn interp_static(n int, suffix string) string { + return 'n=${n}:${suffix}' +} + +fn main() { + assert interp_number(42) == '42' + assert interp_chain('a', 7, 'b') == 'a7b' + assert interp_static(9, 'z') == 'n=9:z' +} -- 2.39.5