While MemoryHigh is an attribute of a cgroup, the throttling is done per process (more precisely, per task). The kernel throttles allocating tasks that cross the MemoryHigh boundary proportionally to the number of pages allocated above the MemoryHigh limit (current->memcg_nr_pages_over_high variable, where current is the current task).
Based on the comments in the kernel mm/memcontrol.c.
The allocating tasks in this cgroup will need to do reclaim or be throttled to prevent further growth of the memory or swap footprints. Target some best-effort fairness between the tasks, and distribute reclaim work and delay penalties based on how much each task is actually allocating.
The function mem_cgroup_handle_over_high in mm/memcontrol.c has more details:
The allocating task should reclaim at least the batch size, but for subsequent retries we only want to do what's necessary to prevent oom or breaching resource isolation
memory.high is breached and reclaim is unable to keep up. Throttle allocators proactively to slow down excessive growth.
The function calculate_high_delay
We use overage compared to memory.high to calculate the number of jiffies to sleep (penalty_jiffies). Ideally this value should be fairly lenient on small overages, and increasingly harsh when the memcg in question makes it clear that it has no intention of stopping its crazy behaviour, so we exponentially increase the delay based on overage amount.
Factor in the task's own contribution to the overage, such that four N-sized allocations are throttled approximately the same as one 4N-sized allocation.
CGroupmight look in this regard is shown in my answer to a different topic here.