[luci] High latency caused by full tree creation
Bryan Mayland
bmayland at leoninedev.com
Sun Jul 17 19:48:38 CEST 2011
I've been doing a lot of experimentation with lucid / luci+uhttpd (cgi)
and have been somewhat concerned with the latency I'd been seeing when
performing a request. I've got a controller that returns a static bit
of text that takes 800-900ms to complete so I started digging into what
takes so long before the server starts to respond.
What it seems is that even once the indexcache is read in, luci calls
the index function of every module in the controller. It of course does
this because it doesn't know which controllers make which nodes in the
tree so the entire tree must be built to determine which controller will
service the request. Makes sense but some index functions take a
relatively long time to run which slows down every page load.
For example, on my WRT54GL with luci-admin-full and full source, the
admin/network page alone adds 300ms to the overall load time of every
page. 200ms of that is just spent enumerating wifi adapters for
admin/network/wifi/radio*, even if the user isn't in the admin section
of the site.
For a simple test I modified the first line of all the admin controller
index functions and added:
if not luci.util.contains(context.request, "admin") then return end
And the request time for my pages (which don't live in /admin) dropped
from 800-900ms to ~260ms.
Because the performance penalty is so great, I've been trying to work up
some sort of way that dispatcher.createtree() would only build the tree
for the path of the request. Obviously one problem is that any
controller is not restricted to its physical path and can put a node in
any url path (e.g. controller/foo/bar.lua is not restricted to only /foo
or /foo/bar urls) so the dispatcher can't automatically decide that
bar.lua:index() should not be called for a request to a /baz url.
I was thinking that entry() and node() could return nil if the node is
not needed to satisfy the request, and then each module could use this
to restrict its own subnode creation such as:
function index()
if entry({"foo"}, alias("foo", "bar"), "TFoo", 10) then
entry({"foo", "bar1"}, call("action_bar1"), "TBar1", 10)
entry({"foo", "bar2"}, call("action_bar2"), "TBar2", 20)
entry({"foo", "bar3"}, call("action_bar3"), "TBar3", 30)
end
end
However many controllers rely of entry()/node() always returning a value
so it would be a pretty big change. I'm just sort of thinking out loud
here. Is this or any optimization worth perusing? Of course,
precompiling the lua speeds all this up a bit, but it seems an area for
vast improvement.
More information about the luci
mailing list