[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Apache Portable Runtime
- From: Rici Lake <lua@...>
- Date: Tue, 13 Mar 2007 21:50:40 -0500
Peter Odding wrote:
Rici Lake wrote:
> You might want to reconsider that. It is certainly a clean interface,
> but it might not be particularly useful in common cases. Off the top of
> my head, I'd say the common cases are: checking one or two permissions,
> and reporting all the permissions in some string representation.
> Now, consider these possibilities:
>
> local stat = apr.stat(filename)
> if stat.perms.world.readable then ... end
> -- or
> if stat.worldperms:match"r" then ... end
>
> Just a thought.
My current interface is apr.stat(...).permissions.user.readable, like
your first example. Which means my 1000/4000 reasoning was wrong; it's
actually 5000 tables: 1000 result tables, 1000 (container) permission
tables and then 1000 each for user/group/world. Are you suggesting the
latter of your examples is better (sorry, it's late :P)?
I don't know. I don't think it's bad, and it's certainly less creation
of tables. (But see below.) My only point was that there was an
alternative, which works reasonably well with Lua.
The part I didn't write was turning both of those representations into
something printable, which I think would be a lot simpler with the
string representation. Deeply nested tables are often awkward.
> I don't like functions which sometimes return tables and sometimes not;
> that's error-prone and confusing to the user.
This seems to be relatively common in Lua. Specifically in the reference
implementation, for example file:read and os.date. But you mention
tables specifically. Is this because of indexing errors when a scalar is
returned instead of a table, without the user expecting so?
Yes, precisely. It's like CGI interfaces which return scalars when there
is only one instance of a key and arrays when there are multiple
instances. That means you *always* need to check the type of the value,
unless you're being sloppy in which case you're going to get slammed
at some point. So it would actually have been better to have always
returned a table, despite the apparent "inefficiency" of creating the table.
Admittedly, creating (and then gc'ing) tables is not free, but neither
are bugs or incessant calls to type(). Obsessing over the cost of
creating tables often leads to annoying interfaces and more complicated
calling sequences.
The point of using a language like Lua, in my opinion, is: ease of use,
avoidance of bugs, and rapidity of development. Fast execution is way
down the list (my list, anyway). So a good interface, in my view, should
promote those virtues, and only then try to do so as efficiently as
possible.
Mike's suggestion is not bad, but it will prove to be awkward when you
want to save the entire stat structure, in which case a table will prove
to have been better.
Now let's put this into perspective.
Here's a quick test. In the first line, I call stat 100,000 times on
each of two files. That takes 4.5 seconds. In the second test, I create
and discard five tables 200,000 times -- roughly the nested format you
suggest, although I didn't add all the fields. That takes 1.1 seconds.
In the third test, I create and discard two tables 200,000 times, using
something closer to the less nested format I suggested. That takes 0.5
seconds.
Now, the test is biased: I'm statting the same files repeatedly, so
presumably the inodes are cached. In a real world example, the stat test
would probably take longer. Even so, it shows a couple of things:
1) 1000 stats will take a few milliseconds, regardless
2) The cost of building five useless tables is at most 25% of the cost
of the call to stat, so reducing it to zero tables will save at most 20%.
Test results:
rlake@freeb:~/src/stattest$ time ./stat 100000 *
stat: mtime 2007-03-14 02:33:22
stat.c: mtime 2007-03-14 02:33:21
real 0m4.483s
user 0m0.086s
sys 0m4.384s
rlake@freeb:~/src/stattest$ time lua -e \
'for i = 1, 200000 do \
local t = {mtime=7, \
perms = {user = {read = true, write = true, exec = true}, \
group = {read = true}, world = {read = true}}} \
end'
real 0m1.153s
user 0m1.134s
sys 0m0.018s
rlake@freeb:~/src/stattest$ time lua -e \
'for i = 1, 200000 do \
local t = {mtime=7, \
perms = {user = "rwx", group = "r", world = "r"}} \
end'
real 0m0.503s
user 0m0.491s
sys 0m0.010s
Here's the stat.c benchmark:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
int main (int argc, char **argv) {
struct stat sb[1];
int n = atoi(argv[1]);
int i;
while (n--) {
for (i = 2; i < argc; i++) {
int rc = stat(argv[i], sb);
if (n == 0) {
if (rc != 0)
printf("%s: error %s\n", argv[i], strerror(errno));
else {
char tmbuf[100];
struct tm mtime[1];
strftime(tmbuf, sizeof tmbuf - 1, "%F %T",
gmtime_r(&sb->st_mtime, mtime));
printf("%s: mtime %s\n", argv[i], tmbuf);
}
}
}
}
return 0;
}