[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