DI definition list optimisation - Cache classes upfront where possible to reduce iterations

Definition lists are currently expensive with multiple definitions as they iterate over all of the definitions, often multiple times. The attached changes store which classes a definition knows about up front and looks them up via an array key in, iirc, constant time.

In some cases it may be possible that the lookup from the array isn't complete, e.g. in the case of getting a `PartialMarker`, in which case it falls back to iterating. It also takes into account the possibility of classes existing in `RuntimeDefinition`s which won't know upfront which classes it can handle.

This has made a significant impact to time spent in Di for our requests.