lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Fri, Jun 16, 2017 at 08:18:10PM +0000, Jonathan Goble wrote:
<snip>
> Let's break it down:
> 
> > local string = ""
> 
> The variable `string` is now an empty string and, in this scope, no longer
> references the `string` standard library.
> 
> > print(string.format("%X", 16777216))
> 
> Here, Lua first looks up `string.format`. While `string` no longer refers
> to the string standard library, it is an actual string, and strings (since
> Lua 5.1) have a metatable with an __index metamethod pointing to the actual
> string library (which isn't changed by your local `string` assignment, nor
> would an assignment to the global `string` affect it). So the lookup of
> `string.format` produces, via metamethod, the string standard library
> function `format`. From there, you can call it with whatever arguments you
> want.
> 
> So why is it a bad idea? One, because indexing through a metamethod is
> slower than directly indexing the actual `string` table, and two, because
> shadowing a built-in global (whether with a global or local assignment) is
> usually a code smell (in any language) and generally shouldn't be done
> without a very good reason.

OTOH, using the string global is actually more like _G.string.find,
requiring two VM index operations. Whereas an index through a local (or
immediate) value is a single VM index operation; the loading and indexing of
the metatable is direcly implemented as compiled C code that doesn't require
stepping through the VM interpreter and so theoretically should be much
faster.

... and my hypothesis checks out:

  $ /tmp/bench.lua 
  256           subject:find    0.0001s
  256           string.find     0.0001s
  4096          subject:find    0.0010s
  4096          string.find     0.0013s
  65536         subject:find    0.0109s
  65536         string.find     0.0114s
  1048576       subject:find    0.1455s
  1048576       string.find     0.1827s
  16777216      subject:find    2.2815s
  16777216      string.find     2.8463s
  268435456     subject:find    36.6191s
  268435456     string.find     45.4249s

Code:

  local unix = require"unix"
  local CLOCK_MONOTONIC = unix.CLOCK_MONOTONIC
  local clock_gettime = unix.clock_gettime
  
  for e=8,28,4 do
  	local n = math.pow(2, e)
  	do
  		local start = clock_gettime(CLOCK_MONOTONIC)
  		local subject = "foo"
  		for i=1,n do
  			subject:find("foo")
  		end
  		local elapsed = clock_gettime(CLOCK_MONOTONIC) - start
  		print(string.format("%-8d", n), "subject:find", string.format("%.4fs", elapsed))
  	end
  
  	do
  		local start = clock_gettime(CLOCK_MONOTONIC)
  		local subject = "foo"
  		for i=1,n do
  			string.find(subject, "foo")
  		end
  		local elapsed = clock_gettime(CLOCK_MONOTONIC) - start
  		print(string.format("%-8d", n), "string.find", string.format("%.4fs", elapsed))
  	end
  end