Skip to content

str.startsWith should transpile into string.sub instead of string.find when the argument is a literal or constant #68

@tapple

Description

@tapple

These 2 scripts microbenchmark 2 possible implementations of str.StartsWith:

string.find (current implementation):

local _ = string.sub -- just to normalize the bytecode size comparison
_ = string.find -- just to normalize the bytecode size comparison
local t = os.clock()
for i = 1, 1700 do
    local l = ("hello world"):find("hello ") == 1
end
t = os.clock() - t
print(t, ll.GetUsedMemory()) -- 0.0002 2471

string.sub (my recommended implementation):

local _ = string.sub -- just to normalize the bytecode size comparison
_ = string.find -- just to normalize the bytecode size comparison
local t = os.clock()
for i = 1, 3200 do
    local l = string.sub("hello world", 1, #"hello ") == "hello "
end
t = os.clock() - t
print(t, ll.GetUsedMemory()) -- 0.0001 2472

As you can see from the above benchmark, the string.sub implementation runs about twice as fast as the string.find implementation, while using only 1 more byte of bytecode. It can fiit twice as many calls into a single SL timeslice as the other. This is because

  1. When # operates on a literal string, or a constant, the operator is constant-folded away into a number literal by the compiler
  2. string.sub is a luau fastcall, but string.find is not.

When the argument is not a constant, the current string.find implementation is faster than string.sub. I've no idea if the transpiler can differentiate between a literal/constant and a non-constant. If not, disregard this suggestion.

Tested on Aditi Luau 2026-05-12.25767303752

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions