Dynamic Backing Groups: efficient assignment of parachains

As discussed in Dynamic Backing Groups: Preparing Cores for Agile Scheduling - #9 by rphmeier we want to dynamically assign parachains with different CoreTime consumption rates to backing groups.

Because multiple parachains can be assigned to the same backing group, we want on average to maximize utilization of backing cores (fewer and fuller) , while at the same time avoiding overcommit (CoretimeRate > 57600/57600),.

One approach by @rphmeier is presented here: Dynamic Backing Groups: Preparing Cores for Agile Scheduling - #10 by rphmeier for which I have one concern regarding the balancing. AFAIU the CoretimeRate consumption refers to full cores (RFC-3 and RFC-1 1/57600 seems to be a block per week for sufficiently large regions for example).

type PartsOf57600 = u16;
enum CoreAssignment {
    InstantaneousPool,
    Task(ParaId),
}
fn assign_core(
    core: CoreIndex,
    begin: BlockNumber,
    assignment: Vec<(CoreAssignment, PartsOf57600)>,
    end_hint: Option<BlockNumber>,
)

For example, 3 different parachains each getting 1/3 fraction of the block space, could be scheduled on the same backing group and produce blocks of 1/3 of a core every time , but they could also just take turns on the core and use it fully.

So, it seems obvious that even in a perfectly balanced scenario, parachains assigned to the same backing group can produce blocks much larger than we assumed based on assignment consumption rate. From an accounting perspective this is fine if we do it based on the actual execution time, but to prevent overloading backers we should have a limit of maximum parachains per backing group. If we only account for the core being assigned at specified frequency, parachains are actually incetivised to use 100% of it.