Jump to content




Which method is lighter?


  • You cannot reply to this topic
17 replies to this topic

#1 skwerlman

  • Members
  • 163 posts
  • LocationPennsylvania

Posted 29 April 2014 - 03:17 PM

I need to check if a character 4 digits from the end of a string is a period. My question is which of the following two three methods is lighter (faster):

function foo(str)
return str:sub(-4,-4) == '.'
end
-or-
-- it doesnt look like string.byte supports negative indices
function bar(str)
return str:byte(str:len() - 4) == 46 -- the ascii value of '.' is 46
end
-or-
function foobar(str)
return str:find('.') == str:len() - 4
end

I know micro-optimization isn't terribly important, but it certainly doesn't hurt

Edited by skwerlman, 29 April 2014 - 03:24 PM.


#2 RoD

  • Members
  • 313 posts

Posted 29 April 2014 - 03:42 PM

i think the first will be faster

#3 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 29 April 2014 - 04:00 PM

I suspect it's unlikely anyone can give you an "off-the-top-of-their-head" answer to this, given that said answer is likely to be specific to ComputerCraft's version of LuaJ. The "simple" way to figure it out would be to call the functions in a loop a few thousand times each, while keeping tabs on os.clock() to see how long the loops take to complete.

#4 Lyqyd

    Lua Liquidator

  • Moderators
  • 8,465 posts

Posted 29 April 2014 - 04:47 PM

Of course, if the fastest of those three methods is really that important, you've probably made some less than optimal design choices to begin with. Are you having performance problems with your script? If so, it would be better to post the whole thing and describe where you're noticing the slowness. It will be easier to provide useful optimization suggestions. This kind of "optimizing" in unnecessary places often leads to unmaintainable code. It is more valuable to make maintainable code than to have the most highly optimized code possible, especially when less optimized and more readable code would run plenty fast enough.

#5 skwerlman

  • Members
  • 163 posts
  • LocationPennsylvania

Posted 29 April 2014 - 05:22 PM

View PostBomb Bloke, on 29 April 2014 - 04:00 PM, said:

I suspect it's unlikely anyone can give you an "off-the-top-of-their-head" answer to this, given that said answer is likely to be specific to ComputerCraft's version of LuaJ. The "simple" way to figure it out would be to call the functions in a loop a few thousand times each, while keeping tabs on os.clock() to see how long the loops take to complete.
Good point. I'll try that.

View PostLyqyd, on 29 April 2014 - 04:47 PM, said:

Of course, if the fastest of those three methods is really that important, you've probably made some less than optimal design choices to begin with. Are you having performance problems with your script? If so, it would be better to post the whole thing and describe where you're noticing the slowness. It will be easier to provide useful optimization suggestions. This kind of "optimizing" in unnecessary places often leads to unmaintainable code. It is more valuable to make maintainable code than to have the most highly optimized code possible, especially when less optimized and more readable code would run plenty fast enough.
Its really just a decision for the one line. In all three cases, it's fairly clear what the code does, so I don't think there will be any major code maintenance.issues. But you're right, it's not reasonable to do this for every function, its just that this one gets called a lot, and saving a few ms a loop could cut ~1s off execution time. I'm not having any particular slowness, this line just gets called a lot. There's really no way around it; I have to call this on every element in a huge array.

#6 skwerlman

  • Members
  • 163 posts
  • LocationPennsylvania

Posted 29 April 2014 - 06:36 PM

View Postskwerlman, on 29 April 2014 - 03:17 PM, said:

I need to check if a character 4 digits from the end of a string is a period. My question is which of the following two three methods is lighter (faster):

function foo(str)
return str:sub(-4,-4) == '.'
end
-or-
-- it doesnt look like string.byte supports negative indices
function bar(str)
return str:byte(str:len() - 4) == 46 -- the ascii value of '.' is 46
end
-or-
function foobar(str)
return str:find('.') == str:len() - 4
end

I know micro-optimization isn't terribly important, but it certainly doesn't hurt

In case anyone is interested, here are the results:
Spoiler

Edited by skwerlman, 29 April 2014 - 06:41 PM.


#7 Lyqyd

    Lua Liquidator

  • Moderators
  • 8,465 posts

Posted 29 April 2014 - 07:00 PM

Could also throw a str:match("(%.)....$") into the mix. Would return nil if it doesn't find the character there.

#8 skwerlman

  • Members
  • 163 posts
  • LocationPennsylvania

Posted 29 April 2014 - 07:12 PM

View PostLyqyd, on 29 April 2014 - 07:00 PM, said:

Could also throw a str:match("(%.)....$") into the mix. Would return nil if it doesn't find the character there.
~6.0E-7 S

It looks like str:len() is what was killing str:byte() and str:find()... I'm gonna try with #str and see if its faster somehow.

EDIT:
Using #str cut str:byte()'s time down to ~3.75E-7 S, making it the fastest.
str:find() dropped to ~4.5E-7 S when #str was used instead of str:len().

I'm gonna use str:byte(#str - 4) == 46 because it's the fastest, and also because I've never had a reason to use string.byte before.

Edited by skwerlman, 29 April 2014 - 07:26 PM.


#9 AgentE382

  • Members
  • 119 posts

Posted 29 April 2014 - 10:14 PM

Have you tried this?
function foobartoo(str)
  return string.find(str, '.', -4, true)
end


#10 Link149

  • Members
  • 46 posts
  • LocationQuebec, Canada

Posted 30 April 2014 - 12:54 AM

function foo(str)
  return str:match("%....$") and true or false
end

I thought I would shorten Lyqyd's code snippet by using "{3}" as a quantifier instead of writing three periods in a row, but it seems Lua does not support them. :(

Still, instead of returning a period '.' when successful and nil when unsuccessful, it shall return true or false. :)

#11 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 30 April 2014 - 02:34 AM

View PostLink149, on 30 April 2014 - 12:54 AM, said:

function foo(str)
  return str:match("%....$") and true or false
end
Still, instead of returning a period '.' when successful and nil when unsuccessful, it shall return true or false. :)

function foo(str)
  return str:match("(%.)...$") ~= nil
end


#12 Lyqyd

    Lua Liquidator

  • Moderators
  • 8,465 posts

Posted 30 April 2014 - 03:38 AM

View PostLink149, on 30 April 2014 - 12:54 AM, said:

function foo(str)
  return str:match("%....$") and true or false
end

I thought I would shorten Lyqyd's code snippet by using "{3}" as a quantifier instead of writing three periods in a row, but it seems Lua does not support them. :(

Still, instead of returning a period '.' when successful and nil when unsuccessful, it shall return true or false. :)

How is ".{3}" shorter than "..." anyway?

#13 skwerlman

  • Members
  • 163 posts
  • LocationPennsylvania

Posted 30 April 2014 - 10:12 AM

View PostAgentE382, on 29 April 2014 - 10:14 PM, said:

Have you tried this?
function foobartoo(str)
  return string.find(str, '.', -4, true)
end
~6.0E-7 S
Still slower than str:byte()

View Posttheoriginalbit, on 30 April 2014 - 02:34 AM, said:

View PostLink149, on 30 April 2014 - 12:54 AM, said:

function foo(str)
  return str:match("%....$") and true or false
end
Still, instead of returning a period '.' when successful and nil when unsuccessful, it shall return true or false. :)

function foo(str)
  return str:match("(%.)...$") ~= nil
end
Also ~6.0E-7 S; meaning link's (which is heavier, since there's more math) is slower too.

I'm not particularly worried about how the output is; as long as I can say something like 'if foo(str) then', the function will work for me.

View PostLyqyd, on 30 April 2014 - 03:38 AM, said:

View PostLink149, on 30 April 2014 - 12:54 AM, said:

function foo(str)
  return str:match("%....$") and true or false
end

I thought I would shorten Lyqyd's code snippet by using "{3}" as a quantifier instead of writing three periods in a row, but it seems Lua does not support them. :(

Still, instead of returning a period '.' when successful and nil when unsuccessful, it shall return true or false. :)

How is ".{3}" shorter than "..." anyway?
Link - What syntax is that? It's regex, right? Cause match isn't regex. It's not even particularly close.

Lyqyd - I think Link wanted to know which was faster. (shorten == less time) It would make sense if it were, since '....' is four operations, and '.{3}' should be two (if this were regex).

Edited by skwerlman, 30 April 2014 - 10:13 AM.


#14 Engineer

  • Members
  • 1,378 posts
  • LocationThe Netherlands

Posted 30 April 2014 - 11:47 AM

View Postskwerlman, on 30 April 2014 - 10:12 AM, said:

Link - What syntax is that? It's regex, right? Cause match isn't regex. It's not even particularly close.

http://lua-users.org...LibraryTutorial
String.match definitely uses patterns, though patterns != regex :P
I think .{3} is supported though

#15 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 30 April 2014 - 12:26 PM

By the way, are you performing each test multiple times? I assume you're aware that MineCraft's performance tends to be a bit variable, so you're unlikely to get the same result with each pass.

#16 skwerlman

  • Members
  • 163 posts
  • LocationPennsylvania

Posted 30 April 2014 - 02:41 PM

View PostBomb Bloke, on 30 April 2014 - 12:26 PM, said:

By the way, are you performing each test multiple times? I assume you're aware that MineCraft's performance tends to be a bit variable, so you're unlikely to get the same result with each pass.
I perform each command 10000000 times twice and average the results.
Since os.clock's resolution is .05 S, my times are accurate +- .0000000025 (2.5E-9) S. Since we're dealing with times that are 100 to 300 times larger, my ordering can be said to be accurate. The timings themselves are not, because I don't adjust for the overhead of the function call and for loop. Also, the timings are specific to my machine, and are likely affected by the other mods I have installed. So the only useful thing I get is the order of the timings, which tells me what routine is faster.

View PostEngineer, on 30 April 2014 - 11:47 AM, said:

View Postskwerlman, on 30 April 2014 - 10:12 AM, said:

Link - What syntax is that? It's regex, right? Cause match isn't regex. It's not even particularly close.

http://lua-users.org...LibraryTutorial
String.match definitely uses patterns, though patterns != regex :P
I think .{3} is supported though
I wish patterns == regex...

Edited by skwerlman, 30 April 2014 - 02:44 PM.


#17 electrodude512

  • Members
  • 167 posts
  • LocationEastern USA

Posted 30 April 2014 - 09:54 PM

I would expect, and your experiments seem to support this, that whatever code has the least table indexes will be the fastest. What if you make all of the string functions local? How much faster is it then? Try this with all 4 methods - I'll bet they'll all have faster (and similar) speeds.
local sub  = string.sub  -- saves indexing _G to get string and string to get sub - saves (2 indexes)*(however many times you call string.sub) indexes
local byte = string.byte
local len = string.len
local find = string.find
local match = string.match

function foo(str)
  return sub(-4,-4) == '.'
end

function bar(str)
  return byte(len() - 4) == 46
end

function foobar(str)
  return find('.') == len() - 4
end

function baz(str)
  return match("%....$") and true or false
end
By the way, find('.') == len() - 4 won't work if there's another dot before the one at position str:len() - 4

Edited by electrodude512, 30 April 2014 - 09:54 PM.


#18 skwerlman

  • Members
  • 163 posts
  • LocationPennsylvania

Posted 01 May 2014 - 08:08 PM

View Postelectrodude512, on 30 April 2014 - 09:54 PM, said:

I would expect, and your experiments seem to support this, that whatever code has the least table indexes will be the fastest. What if you make all of the string functions local? How much faster is it then? Try this with all 4 methods - I'll bet they'll all have faster (and similar) speeds.
local sub  = string.sub  -- saves indexing _G to get string and string to get sub - saves (2 indexes)*(however many times you call string.sub) indexes
local byte = string.byte
local len = string.len
local find = string.find
local match = string.match

function foo(str)
  return sub(-4,-4) == '.'
end

function bar(str)
  return byte(len() - 4) == 46
end

function foobar(str)
  return find('.') == len() - 4
end

function baz(str)
  return match("%....$") and true or false
end
By the way, find('.') == len() - 4 won't work if there's another dot before the one at position str:len() - 4
I'll check, but it looks to me more like it scales the most with function calls, as is common in most languages.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users