square/dagger — pull request #349 by cgruber (2013-11-13 23:06:04+00:00)
Title: Reduce the cost of subsequent calls to plus() to that of processing the added bindings.

`objectGraph.plus(newModules)` forces a traversal of all bindings to ensure they are all linked before a new graph is created. This is to ensure that any JIT bindings required by the objects in the original graph are linked in that graph, so those objects can see them. While the full work of linking them is not done, some amount of work is done under synchronization that is O(N) where N is the number of bindings. This makes each .plus() action O(N+M) with a big fat lock around the O(M). Under high contention wiht a lot of `.plus()`ed graphs, such as a server making request-scoped graphs, this massively inflates the cost of .plus().

This change adds double-checking around the synchronization on the linker (which happens anyway), and inserts a flag into the process which says that the graph has already been fully linked. If so, the graph can safely return the binding map which should be logically immutable once fully linked.

To avoid re-introducing the cost of that scan (though not the cost of the locking at least) I've also backed out the change which copies the binding set into the child and eliminates walking the linker chain to do lookups, on the contention that adding a very small amount of additional lookup time relative to depth (which is very unlikely to be higher than 4 or 5 in any app, and which only happens during binding) is vastly preferable to winding through a potentially massive list of bindings to evaluate whether they've been linked.