LuaJIT is not the standard mainline Lua interpreter that most people think of as "Lua the language". It's still a fairly young, experimental project and you wouldn't want to write production code in it. However, the standard Lua distribution is still much faster than Ruby.
But even though both are dynamic interpreted languages, it's still apples to oranges - they have very different design goals.
Still, I think it's a copout to say performance doesn't matter in any general-purpose language the way the core Ruby team seems to. Micro-optimization is a waste of time, but efficiency and intelligent implementation choices are necessary.
Without some discussion of why Lua is faster, this isn't very interesting. Lua is known for being small and fast, so this benchmark shouldn't be news to most people here.
SBCL is faster still, averaging about half the speed of GCC and about the same as Java.
The important difference is memory. It's generally possible to trade space for speed in a JIT and accelerate anything. SBCL is lisp with a JIT. It's as fast as Java - it's as fat as Java (to within 5x either way). Lua is both small and fast, and is more or less on its own as an interpreted language in that category. (Forth is smaller and faster, but forth is about as low level as macro assembler.)
The "History of Lua" paper written a few years ago for a presentation at HOPL is also great reading and gives good insight into why Lua is the way it is.
If you use Lisp’s static annotation
system, your code becomes uglier than
Java, and much less safe (I don’t think
Lisp does static checking of parameter
types, it just goes ahead and passes you
an object and lets you think it’s an integer).
Additionally, Lua only has "one data structure type" - that's also "higher-level" than what you have to do in Lisp, deciding among singly-linked lists, arrays, hash tables, binary trees, lexical trees, etc.
So while SBCL has its place, there's something about Lua you have to admire, don't you think?
I don't know Lua, but this is the first time I've heard Lua and Ruby equated in terms of expressibility/flexibility (i.e. "high-level"?). Is that a particularly relevant comparison?
Having done a lot of both, Lua feels more pythonic than rubyish in many small ways. Objects are just syntactic sugar where self is the first arg (though unlike python, lua has the syntactic sugar to make it prettier to look at), etc. Interestingly, typical OO Lua is prototype based, not class based -- in that way it is more javascriptish.
Most lua codebases I have seen tend to be written in what I call an "imperative functional" style, where the language isn't purely functional, but functions are the most common means of abstraction.
Yeah, actually. Lua isn't as clean as Ruby (or Python, or your_favorite_language), but it has the advanced features you'd expect from a powerful scripting language: closures, coroutines, etc. Rather, Lua is like a really experienced prostitute. Not necessarily beautiful, but can do all the tricks and can get the job done efficiently and inexpensively.
Are you serious? Lua has an extremely clean and orthogonal core. It's got serious CS mojo. The "batteries not included" bit is probably the greatest detraction to Lua as a general-purpose scripting language.
Certainly. I didn't mean to deride anyone's work with the comparison. I meant, some of the implementation decisions seem arbitrary, and seem like they were made "for the sake of being different".
For example, Lua's use of two dashes for line comments, and --[[ and --]] for block quotes. That lets you remove the first --[[ in a block quote without causing the program to break, because the "closing" block quote is commented out at that point (because --]] is a comment at that point). But really, that's so different from other languages that it seems like a liability rather than an asset. That's what I meant by "not necessarily beautiful". It is definitely beautiful in certain other aspects, though.
Also, if I were writing a scripting language, I'd love for it to be "like a prostitute". Even if nobody respected it, everyone would be using it.
the [[ and ]] are used for literal/multiline strings, so they make sense in block quotes. and you don't have to end the block with --]]. ]] is fine, which is the only thing i've ever used since it would never have occurred to me, especially as an anemic commenter, to use that quoting trick
the [[ and ]] can be extended to nest strings/comments like so:
--[==[
function add( a, b )
--[[
return a + b
]]
return a - b
end
]==]
Lua doesn't even really have arrays per se, but rather "tables," which are a light, flexible data structure somewhat similar to Javascript's objects. You're free to index tables starting with 0 if you really want to, but the convention in Lua is to use 1.
This is slightly less true than it used to be. The table length "#" operator returns the index of the largest integer key continuously reachable from the index at 1. (i.e. there can be no integer keys with nil values in between). This, as well as a handful of standard library operators, rely on the index-from-1 convention.
Tables in Lua can be thought of as discrete mappings of any value as key, to any value as value. The reference implementation happens to have the property that if you index from 1 upwards by increments of 1 that all of those values are kept in a single C array which is then subscripted in the usual fashion. It's a handy thing to know when you decide how to lay out your data. The "dictionary" portion of the table only springs into existence when something is actually inserted into it.
Actually if I understand correctly, an empty Lua table is basically just a pointer to a minimal structure in memory, so it can be used for identity comparisons and so on where only uniqueness is necessary, rather than a full array+dict functionality with the associated memory overhead.
Lua tables are extremely flexible and powerful. Especially when combined with the "metatable" concept.
The table length "#" operator returns the index of the largest integer key continuously reachable from the index at 1. (i.e. there can be no integer keys with nil values in between). This, as well as a handful of standard library operators, rely on the index-from-1 convention.
That does not always hold true. The '#' operator is weird. Here is an example:
/*
** Try to find a boundary in table `t'. A `boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
int luaH_getn (Table *t) {
unsigned int j = t->sizearray;
if (j > 0 && ttisnil(&t->array[j - 1])) {
/* there is a boundary in the array part: (binary) search for it */
unsigned int i = 0;
while (j - i > 1) {
unsigned int m = (i+j)/2;
if (ttisnil(&t->array[m - 1])) j = m;
else i = m;
}
return i;
}
/* else must find a boundary in hash part */
else if (t->node == dummynode) /* hash part is empty? */
return j; /* that is easy... */
else return unbound_search(t, j);
}
I did not get the same behavior as you when I tried this just now - rather, I had:
> x.a = 10
> print(#x)
0
>
Which makes more sense than 1. At the same time, 3 is also a valid value of #x, because it is the integer index of a value directly preceding an index with a nil value.
This behavior has been hashed over on the mailing list multiple times... the exact wording of the documentation allows for it but it's not exactly what people expect of the length operator.
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be zero. For a regular array, with non-nil values from 1 to a given n, its length is exactly that n, the index of its last value. If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider _any_ such nil value as the end of the array). -- From the manual
My expectation is that there will be some changes to the handling of the # operator in 5.2 increasing its usefulness and allowing it to be overridden via metatables for user-defined types.
In any case, this is one of the few currently ugly corners of the language itself. Another would be the handling of destructuring varargs via the select(...) function, which can be really funky.
This is one of the things expected to be fixed in 5.1.4, IIRC.
# works with an array-style table (consecutive integer keys, starting from 1) and not hash-style tables. Once you're familiar with Lua, you rarely confuse them -- they tend to be used very differently. Using the same variable type is mostly an efficiency trick.
I agree that counting from 1 is kind of annoying, but part of their reasoning was that Lua is targeted as a scripting language for niches that can include people who don't really program. They tried to keep "weird" programming stuff from obfuscating the tiny amount necessary for writing config or data files in Lua. It has coroutines, lexical scope, metatables, etc. when you use it for real programming, but they don't get in the way when you don't need them.
For example, from the data file for my laptop's wifi script:
personally i think 0-indexing is weird. it makes sense as a pointer offset in lower-level languages. but in higher-level languages we have the opportunity to index the first element by the number 1, the second by the number 2, and so on instead of having to do that little -1 every time which is something that has to be learned
that's really weird. was it written at a time when most programming was for scientific purposes? i don't know my comp history
if i parsed it correctly, he is saying that things should be 0-indexed because a particular mathematical notation corresponding to it is more sensical, but i don't see what mathematical notation has to do with programming notation, both of which are man-made, arbitrary, and for different purposes, such that reasoning about either with respect to the other is imo fallacious
he compares subscript ranges in a notation where 0-indexing appears to be better, but choosing a different notation (such as b in his examples) makes it look better for 1-indexing ( 0 < i <= N versus -1 < i <= N-1 )
One advantage of 0-based is that using array[mod(i, length)] works. It mostly seems to me like 0-based vs. 1-based is a relatively arbitrary decision, though. Each just leads to fencepost errors in different places.
if i read correctly, that's talking about the mathematical notations, not arrays or the indexing of such, of that programming language
and while we're considering sources of "clumsiness and mistakes," 0-indexing is an exceptional candidate, from my experiences helping a friend learn C++ and my own memories treading through those early days
The first one is that indexAfterLastElement - firstElementIndex equals the number of elements being scanned.
The second advantage (which is the most debatable, and personal) is that the initial boundary condition is trivial (it's the first element) and the end boundary condition is natural i.e. just look past the element you are looking at whether there is a next one. That's what Dijkstra is talking about when telling the example of the student who would not look past the end of the page.
I used the epithet natural because for most of the objects we interact with, the end is not part of the object, rather it is the transition in between the last element and nothingness.
Now that said (in too many words) if you compare 1-based arrays and 0-based arrays you have:
for (i = 0; i < N; i++) {
...
}
for (i = 1; i < N + 1; i++) {
...
}
Both would be acceptable. However 0 based arrays have the advantage of giving i two meanings:
1. the index of the current element
2. the number of elements scanned so far.
At the end of the iteration, i also conveniently contains the number of elements having been scanned. Which can be useful in algorithms where there is an extra condition which might let you leave the iteration early.
This to me is the most important part, zero-based indexing is more expressive.
These are some of the best arguments for 0-based indexing, and I agree that these are very useful properties. In my day-to-day programming though, the 1-based indexing has some (admittedly, less math-based) advantages too. For instance when I have an array my brain appreciates the simplicity of the "oh, this array has 10 elements. What's the last element? Number 10! Yay, that was easy" thought process.
In many cases I don't care about specifying boundary conditions at all; I will just use something along the lines of
for i, v in ipairs(t) do something_interesting(i, v) end
or even just a map or fold or some other iteration technique where I'm not explicitly specifying boundary conditions. If I'm implementing a heap or some other data structure which uses calculations to interact with an array I will be double-checking my math anyway because I don't trust myself to get it right the first time, 0-based indexing or not ;)
The last element of an array is foo[-1] regardless of it's length. :-) And try implementing a circular buffer using an array of N elements with 1-based indexing and then 0-based. Sorry in advance for the loss of hair due to the former.
Me too. It motivated me to spend the last 15 minutes looking into Lua.
Similarities: Block/lambda support and syntax, optional parentheses, control statements as block, prefix, or postfix.
OTOH, "how come Lua is not as widespread as say, Python ? I'd say the answer is this: there is no real standard library, and as opposed to Python, Lua doesn't come with batteries included." -- http://www.ivy.fr/blog/index.php/2008/03/17/83-kahlua-lua-on...
Very true, Lua is optimized for being very small, fast, and portable. It optimizes for embedding, which means you cannot make assumptions about what you are allowed to do, so it errs on the side of very little.
On the other hand, interfacing to C is the nicest of anything I have done, by a long shot, so the lack of batteries isn't that painful in practice. It fills a different niche than python or ruby, in its, it shines.
Lua presumes you've already got your own batteries, thank you, and you don't want those crummy off-brand ones that always seem to come "included".
Thus, I feel a Lua - to - Ruby/Python comparison is sort of erroneous. Use Lua when you already have all your tools. Use Ruby/Python when you don't have any tools, and you want cool whizbang higher-level stuff.
Honestly the benchmark is wholly unsurprising to me; Lua is a much more minimal language. If you need another language to lump Lua with, lump it with Javascript sooner than you would Ruby or Python.
However, as a longtime user of Lua, I would still greatly appreciate a default set of "batteries-included" libraries as an option, like what you'll find in Python, so that it could/would be that much faster to write simple applications in pure Lua, without needing to deal with compiling/bridging to C/C++ libraries...
In short, Lua's philosophy is to keep the core language as small as possible, but to include features that make it easy to add major language features as necessary. Adding an object system to the language is trivial, for example. (It doesn't have hygienic macros, though there are some side projects that attempt to add them. No experience with them, though...I'd just use Scheme.) Lua feels to me like a minimalistic, more internally consistent Python.
Lua is generally the fastest scripty language, ruby is generally the slowest. Ruby is much more general purpose though -- Lua is very optimized for embedding.
Well, LuaJIT only runs on x86, and only in 32-bit mode. Though, the work-in-progress LuaJIT 2 is supposed to be easily portable to ARM or other architectures.
While it would be an interesting project, the Ruby wouldn't necessarily be faster for it. I don't know Ruby well enough to say, but there are probably aspects in the language design itself that make it slower (as a trade-off for expressiveness). Lua is very fast, but that comes from aggressively simplifying the core of the language. Most things are ultimately built on only a few central constructs, and they have been very throughly optimized.
Which version of Ruby? 1.8 or 1.9? With my own projects Ruby 1.9 performs much better. Lua is well known for being small and fast, so I doubt Ruby 1.9 would beat it... Nevertheless, it'd be a lot more interesting comparison that Lua vs Ruby ???.
Looks like perf comparison between both Python and Python 3 against Lua isn't far off from Ruby and Lua... not really surprised. Like everyone else said, Lua is smaller and leaner.
This year my new year's resolution was to learn Lua. I think with some developer attention it has the potential to become a "better PHP" for web development because it beats PHP in performance, simplicty and elegance.
Although I doubt it's going to ever replace PHP I think it's poised to make some inroads now that the next version of Apache will include mod_lua out of the box.
"Fair" comparisons of the sort you're talking about are not useful when choosing a language. If I'm choosing a language for an application, I don't care if it's unfair that one is much faster than the other because it has a JIT compiler instead of an interpreter - I only care which one is the most suitable.
That's true, but I'll live with wondering a little longer. In the meantime, it seems that Lua is still quite a bit faster than ruby 1.9.0, even though ruby 1.9.0 is approaching Python 2 territory (tested 7 months ago):
I'm sure, but luajit wins handily over all the scripting interpreters. I'm not much concerned with ruby in particular. I'm suspicious that luajit is constrained in features. I doubt you can dynamically load in object code for modules, for example. This makes it not quite a straightforward comparison to the other interpreters.
Since it is able to use certain non-ANSI C features on the various platforms it supports it can do other neat things like enable coroutine calls across Lua/C stack boundaries.
But even though both are dynamic interpreted languages, it's still apples to oranges - they have very different design goals.
Still, I think it's a copout to say performance doesn't matter in any general-purpose language the way the core Ruby team seems to. Micro-optimization is a waste of time, but efficiency and intelligent implementation choices are necessary.