It’s both.

Multithreading has been more ubiquitous in Mac apps for a long time thanks to Apple having offered mainstream multi-CPU machines very early on (circa 2000), predating even OS X itself, and has made a point of making multithreading easier in its SDK. By contrast multicore machines weren’t common in the Windows/x86 world until around the late 2000s with the boom of Intel’s Core series CPUs, but single core x86 CPUs persisted for several years following and Windows developer culture still hasn’t embraced multithreading as fully as its Mac counterpart has.

This then made it dead simple for Mac developers to adopt task prioritization/QoS. Work was already cleanly split into threads, so it’s just a matter of specifying which are best suited for putting on e-cores and which to keep on P-cores. And overwhelmingly, Mac devs have done that.

So the system scheduler is a good deal more effective than its Windows counterpart because third party devs have given it cues to guide it. The tasks most impactful to the user’s perception of snappiness remain on the P-cores, the E-cores stay busy with auxiliary work and keep the P-cores unblocked and able to sleep more quickly and often.

Windows has SetThreadPriority and SetThreadAffinityMask since at least Windows XP.

Yeah, this my guess as well. The other OSes have the ability to pin to specific cores, but first party Apple leaned hard into coding to that hardware vision. Since Apple would love to merge the desktop and mobile software, being very deliberate about what is background vs foreground work is essential. Windows and Linux have not had the hardware guarantees of differentiating between cores, so few programs have taken the effort to be explicit about how the work is executed.

When I ran Gnome, I was regularly annoyed at how often an indexing service would chew through CPU.

There was an article by Raymond Chen where he argued that giving app developers an API option to say "run me under high/low priority" rarely works because every developer views their program as the main character on the stage and couldn't care less about other programs' performance, and they are incentivized to enable the "high priority" option if given a chance because it makes their program run better (at the expense of other programs). So unless there's a strict audit on some kind of app store or some API rules which enforce developers don't abuse the priority API, sometimes it's better to let the OS decide all the scheduling dynamically as the programs run (say, a foreground UI window automatically is given a high priority by the OS), so that the scheduling was fair.

The way it’s conceptualized on Apple platforms is primarily user-initiated vs. program initiated, with the former getting priority. It’s positioned as being about tasks within a program competing for resources rather than programs competing with each other.

So for example, if in an email client the user has initiated the export of a mailbox, that is given utmost priority while things like indexing and periodic fetches get put on the back burner.

This works because even a selfish developer wants their program to run well, which setting all tasks as high priority actively and often visibly impedes, and so they push less essential work to the background.

It just happens that in this case, smart threading on the per-process level makes life for the system scheduler easier.