String.lower(str) or str:lower()?

Should I be using something like

print(string.lower("Hello")) -- > "hello"

or

print(("Hello"):lower()) -- > "hello"

by default in my scripting?


I am aware that using string.lower works on numbers (eg. string.lower(2)) whereas calling :lower() on numbers (eg. (2):lower()) will result in an error.

However, using :lower() keeps the code shorter when not working with numbers (which is almost always the case here).

So, in terms of performance (which way is more efficient, and is this difference significant?) and conventions (is there a general preference by scripters?), would string.lower(str) or str:lower() be the better default option?

This question extends to several other string library functions that can also be called as methods of a string type, such as upper, sub, format, split, gsub, byte, et cetera. For example, string.format("Hello %s world!", "cruel") versus ("Hello %s world!"):format("cruel").

2 Likes

I do not think there is a performance difference between the both of these.

Theoretically, the static call string.lower should be faster than an object method call like someString:lower, although the difference would be incredibly minimal.

3 Likes

True, but that tiny performance shouldn’t be an issue unless you’re calling it multiple times. The dot operator is often the best choice to use.

3 Likes

You can fix this issue by doing:

local lower = string.lower

print(lower(5))

It looks cleaner than writing string.lower as well as having the ability to convert numbers into string.

For any other of the string methods, just put all the string methods you need at the top like so:

local lower, upper, split, format, sub, gsub, byte = string.lower, string.upper, string.split, string.format string.sub, string.gsub, string.byte

actually this had no purpose as being a message lol.
I use string.lower and (‘aaa’):lower()

1 Like

Saving the string funcs as a var then using them is the fastest:

lower = string.lower
lower("myString")

2nd fastest is plain using the string library string.lower("myString")
Last Place is method calling the library: s = "myString" ; s:lower()

Not in the slightest. My test setup called the functions 30 million times and the difference between first and last place is half a sec.

I prefer straight using the string library, then using method calls. I do not advice you save the function call unless you are using the function like 500000 times a sec, this goes for any library function.

Test Setup:

timer = require(game.ReplicatedStorage.Timer).new()
wait(5)

local lower, rep = string.lower, string.rep
local loops, strlen = 3e7, 1
local char = "F"
local var

timer:start()
for i=1,loops do 
	var = string.lower(string.rep(char,strlen))
end
timer:stop()
wait(1)

timer:start()
for i=1,loops do 
	var = char:rep(strlen):lower()
end
timer:stop()
wait(1)

timer:start()
for i=1,loops do 
	var = lower(rep(char,strlen))
end
timer:stop()
wait(1)

timer:results()

Results:
image

2 Likes

this doesn’t effect luau anymore btw

My test disproves that statement.
Saving the function call was .2 sec faster over 30 million calls than using the string library

Being 0,2 s faster on 30 mln iterations is a micro-optimsation. It cannot be a real issue performance-wise.

Luau already optimized standard library functions like string.lower when the script is loaded so the difference is even more minimal.

It’s always possible to “localize” the global accesses by using local max = math.max , but this is cumbersome - in practice it’s easy to forget to apply this optimization. To avoid relying on programmers remembering to do this, Luau implements a special optimization called “imports”, where most global chains such as math.max are resolved when the script is loaded instead of when the script is executed.

This optimization relies on being able to predict the shape of the environment table for a given function; this is possible due to global sandboxing, however this optimization is invalid in some cases:

3 Likes

str:lower() → fast
string.lower → faster
local lower = string.lower; lower(str) → fastest

I believe this is the order of operations.

However the performance difference is likely negligible and you won’t need to worry about it.
(you’d need to be lowering millions of characters per step to notice a difference)

It’s not the numbers of chars that matter in this case, it the number of time the func is called

I agree, that why I don’t recommend the pattern because it only serves to cutter the global space of the script

3 Likes

Does not matter. Won’t ever matter. If you’re doing millions of operations on strings and feel the need to use a certain way of calling a function then you’re bottlenecking yourself by using strings in the first place.

I should not that optimizations to luau are being made such that people are less inclined to localize library functions because they are equivalent in speed for many many operations.

This occurs because string’s have an __index metamethod set in their metatable. Numbers do not have this.

Use whatever feels most idiomatic to you. Don’t waste time by believing you need to be obsessed with micro-optimization. You wasting time if you are. If you’re stuck micro-optimizing then I would suggest that you have bigger issues with your code.

3 Likes

In this case, you had to put a string in parenthesis to :lower it. However the string.lower looks much nicer. You can basically use both. Whatever works for you. If you need a fast conversion (faster to code I mean.) then use :lower. Example: local lowerName = game.Players.LocalPlayer.Name:lower()
Other example: string.lower("MaKE THIs LoWERCasE")

Basically, if it’s a string by itself (e.g. "Testing") then use string.lower("Testing") but if it’s a string from an object (e.g. workspace.Part.Name) then probably use :lower for efficiency.

2 Likes

It doesn’t matter which one you use. They both do the same thing, but str:lower() is faster to write, so that’s the one I prefer to use.

In term of perfermance, you don’t even need to be slightly concerned about them because the difference (if there even is one) will be so extremely small that it won’t effect anything.

1 Like

Yep, that’s what I was considering after understanding that performance should be barely ever an issue for this.