Skip to content

Commit

Permalink
v: optimize literal string comparison (match, in and ==) (vlang…
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp authored Oct 25, 2024
1 parent f715d49 commit 4e9f7c2
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 12 deletions.
8 changes: 4 additions & 4 deletions vlib/v/gen/c/auto_eq_methods.v
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,11 @@ fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string {
if field.typ.has_flag(.option) {
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)
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() {
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 {
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() {
eq_fn := g.gen_sumtype_equality_fn(field.typ)
Expand Down Expand Up @@ -294,7 +294,7 @@ fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string {
if info.parent_type.has_flag(.option) {
left_var = '*' + g.read_opt(info.parent_type, 'a')
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 {
fn_builder.writeln('\treturn string__eq(a, b);')
}
Expand Down
39 changes: 35 additions & 4 deletions vlib/v/gen/c/infix.v
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,16 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
g.gen_plain_infix_expr(node)
} 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
&& (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 == '' {
// `str == ''` -> `str.len == 0` optimization
g.write('(')
g.expr(node.left)
g.write(')')
arrow := if left.typ.is_ptr() { '->' } else { '.' }
g.write('${arrow}len ${node.op} 0')
} else {
} else if node.left is ast.Ident {
// vmemcmp(left, "str", sizeof("str")) optimization
slit := cescape_nonascii(util.smart_quote(node.right.val, node.right.is_raw))
var := g.expr_string(node.left)
Expand All @@ -147,6 +148,17 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
} else {
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 {
if node.op == .ne {
Expand Down Expand Up @@ -636,8 +648,26 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) {
match elem_sym.kind {
.string, .alias, .sum_type, .map, .interface, .array, .struct {
if elem_sym.kind == .string {
g.write('string__eq(')
if left.is_auto_deref_var() || (left is ast.Ident && left.info is ast.IdentVar
is_auto_deref_var := left.is_auto_deref_var()
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.write('*')
}
Expand Down Expand Up @@ -668,6 +698,7 @@ fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) {
g.expr(array_expr)
}
}
end:
if i < right.exprs.len - 1 {
g.write(' || ')
}
Expand Down
18 changes: 14 additions & 4 deletions vlib/v/gen/c/match.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
module c

import v.ast
import v.util

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 {
Expand Down Expand Up @@ -491,10 +492,19 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
g.write(')')
}
.string {
ptr_str := if node.cond_type.is_ptr() { '*' } else { '' }
g.write('string__eq(${ptr_str}${cond_var}, ')
g.expr(expr)
g.write(')')
if expr is ast.StringLiteral {
slit := cescape_nonascii(util.smart_quote(expr.val, expr.is_raw))
if node.cond_type.is_ptr() {
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 {
derefs_expr := '*'.repeat(g.get_expr_type(expr).nr_muls())
Expand Down

0 comments on commit 4e9f7c2

Please sign in to comment.