v: optimize literal string comparison (match, in and ==) (#22643)

This commit is contained in:
Felipe Pena 2024-10-25 04:40:09 -03:00 committed by GitHub
parent f715d49156
commit 4e9f7c21aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 12 deletions

View file

@ -220,11 +220,11 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string {
if field.typ.has_flag(.option) { if field.typ.has_flag(.option) {
left_arg_opt := g.read_opt_field(left_type, field_name, 'a', field.typ) left_arg_opt := g.read_opt_field(left_type, field_name, 'a', field.typ)
right_arg_opt := g.read_opt_field(left_type, field_name, 'b', field.typ) right_arg_opt := g.read_opt_field(left_type, field_name, 'b', field.typ)
fn_builder.write_string('(((${left_arg_opt}).len == (${right_arg_opt}).len && (${left_arg_opt}).len == 0) || string__eq(${left_arg_opt}, ${right_arg_opt}))') fn_builder.write_string('(((${left_arg_opt}).len == (${right_arg_opt}).len && (${left_arg_opt}).len == 0) || fast_string_eq(${left_arg_opt}, ${right_arg_opt}))')
} else if field.typ.is_ptr() { } else if field.typ.is_ptr() {
fn_builder.write_string('((${left_arg}->len == ${right_arg}->len && ${left_arg}->len == 0) || string__eq(*(${left_arg}), *(${right_arg})))') fn_builder.write_string('((${left_arg}->len == ${right_arg}->len && ${left_arg}->len == 0) || fast_string_eq(*(${left_arg}), *(${right_arg})))')
} else { } else {
fn_builder.write_string('((${left_arg}.len == ${right_arg}.len && ${left_arg}.len == 0) || string__eq(${left_arg}, ${right_arg}))') fn_builder.write_string('((${left_arg}.len == ${right_arg}.len && ${left_arg}.len == 0) || fast_string_eq(${left_arg}, ${right_arg}))')
} }
} else if field_type.sym.kind == .sum_type && !field.typ.is_ptr() { } else if field_type.sym.kind == .sum_type && !field.typ.is_ptr() {
eq_fn := g.gen_sumtype_equality_fn(field.typ) eq_fn := g.gen_sumtype_equality_fn(field.typ)
@ -294,7 +294,7 @@ fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string {
if info.parent_type.has_flag(.option) { if info.parent_type.has_flag(.option) {
left_var = '*' + g.read_opt(info.parent_type, 'a') left_var = '*' + g.read_opt(info.parent_type, 'a')
right_var = '*' + g.read_opt(info.parent_type, 'b') right_var = '*' + g.read_opt(info.parent_type, 'b')
fn_builder.writeln('\treturn ((${left_var}).len == (${right_var}).len && (${left_var}).len == 0) || string__eq(${left_var}, ${right_var});') fn_builder.writeln('\treturn ((${left_var}).len == (${right_var}).len && (${left_var}).len == 0) || fast_string_eq(${left_var}, ${right_var});')
} else { } else {
fn_builder.writeln('\treturn string__eq(a, b);') fn_builder.writeln('\treturn string__eq(a, b);')
} }

View file

@ -129,7 +129,8 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.gen_plain_infix_expr(node) g.gen_plain_infix_expr(node)
} else if (left.typ.idx() == ast.string_type_idx || (!has_defined_eq_operator } else if (left.typ.idx() == ast.string_type_idx || (!has_defined_eq_operator
&& left.unaliased.idx() == ast.string_type_idx)) && node.right is ast.StringLiteral && left.unaliased.idx() == ast.string_type_idx)) && node.right is ast.StringLiteral
&& (node.right.val == '' || (node.left is ast.Ident && node.left.or_expr.kind == .absent)) { && (node.right.val == '' || (node.left is ast.SelectorExpr
|| (node.left is ast.Ident && node.left.or_expr.kind == .absent))) {
if node.right.val == '' { if node.right.val == '' {
// `str == ''` -> `str.len == 0` optimization // `str == ''` -> `str.len == 0` optimization
g.write('(') g.write('(')
@ -137,7 +138,7 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.write(')') g.write(')')
arrow := if left.typ.is_ptr() { '->' } else { '.' } arrow := if left.typ.is_ptr() { '->' } else { '.' }
g.write('${arrow}len ${node.op} 0') g.write('${arrow}len ${node.op} 0')
} else { } else if node.left is ast.Ident {
// vmemcmp(left, "str", sizeof("str")) optimization // vmemcmp(left, "str", sizeof("str")) optimization
slit := cescape_nonascii(util.smart_quote(node.right.val, node.right.is_raw)) slit := cescape_nonascii(util.smart_quote(node.right.val, node.right.is_raw))
var := g.expr_string(node.left) var := g.expr_string(node.left)
@ -147,6 +148,17 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
} else { } else {
g.write('_SLIT_NE(${var}${arrow}str, ${var}${arrow}len, "${slit}")') g.write('_SLIT_NE(${var}${arrow}str, ${var}${arrow}len, "${slit}")')
} }
} else {
// fast_string_eq optimization for string selector comparison to literals
if node.op == .ne {
g.write('!fast_string_eq(')
} else {
g.write('fast_string_eq(')
}
g.expr(node.left)
g.write(', ')
g.expr(node.right)
g.write(')')
} }
} else if has_defined_eq_operator { } else if has_defined_eq_operator {
if node.op == .ne { if node.op == .ne {
@ -636,8 +648,26 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) {
match elem_sym.kind { match elem_sym.kind {
.string, .alias, .sum_type, .map, .interface, .array, .struct { .string, .alias, .sum_type, .map, .interface, .array, .struct {
if elem_sym.kind == .string { if elem_sym.kind == .string {
g.write('string__eq(') is_auto_deref_var := left.is_auto_deref_var()
if left.is_auto_deref_var() || (left is ast.Ident && left.info is ast.IdentVar if left is ast.Ident && left.or_expr.kind == .absent
&& array_expr is ast.StringLiteral {
var := g.expr_string(left)
slit := cescape_nonascii(util.smart_quote(array_expr.val, array_expr.is_raw))
if is_auto_deref_var || (left.info is ast.IdentVar
&& g.table.sym(left.obj.typ).kind in [.interface, .sum_type]) {
g.write('_SLIT_EQ(${var}->str, ${var}->len, "${slit}")')
} else {
g.write('_SLIT_EQ(${var}.str, ${var}.len, "${slit}")')
}
unsafe {
goto end
}
} else if array_expr is ast.StringLiteral {
g.write('fast_string_eq(')
} else {
g.write('string__eq(')
}
if is_auto_deref_var || (left is ast.Ident && left.info is ast.IdentVar
&& g.table.sym(left.obj.typ).kind in [.interface, .sum_type]) { && g.table.sym(left.obj.typ).kind in [.interface, .sum_type]) {
g.write('*') g.write('*')
} }
@ -668,6 +698,7 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) {
g.expr(array_expr) g.expr(array_expr)
} }
} }
end:
if i < right.exprs.len - 1 { if i < right.exprs.len - 1 {
g.write(' || ') g.write(' || ')
} }

View file

@ -4,6 +4,7 @@
module c module c
import v.ast import v.ast
import v.util
fn (mut g Gen) need_tmp_var_in_match(node ast.MatchExpr) bool { fn (mut g Gen) need_tmp_var_in_match(node ast.MatchExpr) bool {
if node.is_expr && node.return_type != ast.void_type && node.return_type != 0 { if node.is_expr && node.return_type != ast.void_type && node.return_type != 0 {
@ -491,10 +492,19 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
g.write(')') g.write(')')
} }
.string { .string {
ptr_str := if node.cond_type.is_ptr() { '*' } else { '' } if expr is ast.StringLiteral {
g.write('string__eq(${ptr_str}${cond_var}, ') slit := cescape_nonascii(util.smart_quote(expr.val, expr.is_raw))
g.expr(expr) if node.cond_type.is_ptr() {
g.write(')') g.write('_SLIT_EQ(${cond_var}->str, ${cond_var}->len, "${slit}")')
} else {
g.write('_SLIT_EQ(${cond_var}.str, ${cond_var}.len, "${slit}")')
}
} else {
ptr_str := if node.cond_type.is_ptr() { '*' } else { '' }
g.write('fast_string_eq(${ptr_str}${cond_var}, ')
g.expr(expr)
g.write(')')
}
} }
.struct { .struct {
derefs_expr := '*'.repeat(g.get_expr_type(expr).nr_muls()) derefs_expr := '*'.repeat(g.get_expr_type(expr).nr_muls())