Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
[mypyc] Add faster primitive for string equality
This speeds up self check by ~1.4%.  String equality is one of the top
five most common primitive function calls in self check.

We previously used a string comparison primitive that calculated the
relative order of two strings. Usually we only care about equality,
which we can do quicker since we can fast path using a length check,
for example.

I checked the CPython implementation of string equality in 3.9 (lowest
supported Python version) and 3.13, and both of them had a fast path
based on string object kind, and equality checks overall have the same
semantics.
  • Loading branch information
JukkaL committed Jul 8, 2025
commit 914757b5396b6a4e3faa5b631e9238c57fc2df7d
12 changes: 11 additions & 1 deletion mypyc/irbuild/ll_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,12 @@
unary_ops,
)
from mypyc.primitives.set_ops import new_set_op
from mypyc.primitives.str_ops import str_check_if_true, str_ssize_t_size_op, unicode_compare
from mypyc.primitives.str_ops import (
str_check_if_true,
str_eq,
str_ssize_t_size_op,
unicode_compare,
)
from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op, new_tuple_with_length_op
from mypyc.rt_subtype import is_runtime_subtype
from mypyc.sametype import is_same_type
Expand Down Expand Up @@ -1471,6 +1476,11 @@ def check_tagged_short_int(self, val: Value, line: int, negated: bool = False) -

def compare_strings(self, lhs: Value, rhs: Value, op: str, line: int) -> Value:
"""Compare two strings"""
if op == "==":
return self.primitive_op(str_eq, [lhs, rhs], line)
elif op == "!=":
eq = self.primitive_op(str_eq, [lhs, rhs], line)
return self.add(ComparisonOp(eq, self.false(), ComparisonOp.EQ, line))
compare_result = self.call_c(unicode_compare, [lhs, rhs], line)
error_constant = Integer(-1, c_int_rprimitive, line)
compare_error_check = self.add(
Expand Down
1 change: 1 addition & 0 deletions mypyc/lib-rt/CPy.h
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,7 @@ static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) {
#define RIGHTSTRIP 1
#define BOTHSTRIP 2

char CPyStr_Equal(PyObject *str1, PyObject *str2);
PyObject *CPyStr_Build(Py_ssize_t len, ...);
PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index);
CPyTagged CPyStr_Find(PyObject *str, PyObject *substr, CPyTagged start, int direction);
Expand Down
16 changes: 16 additions & 0 deletions mypyc/lib-rt/str_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ make_bloom_mask(int kind, const void* ptr, Py_ssize_t len)
#undef BLOOM_UPDATE
}

// Adapted from CPython 3.13.1 (_PyUnicode_Equal)
char CPyStr_Equal(PyObject *str1, PyObject *str2) {
if (str1 == str2) {
return 1;
}
Py_ssize_t len = PyUnicode_GET_LENGTH(str1);
if (PyUnicode_GET_LENGTH(str2) != len)
return 0;
int kind = PyUnicode_KIND(str1);
if (PyUnicode_KIND(str2) != kind)
return 0;
const void *data1 = PyUnicode_DATA(str1);
const void *data2 = PyUnicode_DATA(str2);
return memcmp(data1, data2, len * kind) == 0;
}

PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index) {
if (PyUnicode_READY(str) != -1) {
if (CPyTagged_CheckShort(index)) {
Expand Down
10 changes: 10 additions & 0 deletions mypyc/primitives/str_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
ERR_NEG_INT,
binary_op,
custom_op,
custom_primitive_op,
function_op,
load_address_op,
method_op,
Expand Down Expand Up @@ -69,6 +70,15 @@
steals=[True, False],
)

# str1 == str2 (very common operation, so we provide our own)
str_eq = custom_primitive_op(
name="str_eq",
c_function_name="CPyStr_Equal",
arg_types=[str_rprimitive, str_rprimitive],
return_type=bool_rprimitive,
error_kind=ERR_NEVER,
)

unicode_compare = custom_op(
arg_types=[str_rprimitive, str_rprimitive],
return_type=c_int_rprimitive,
Expand Down