Hi,
There is a problem with spawning code, which can be clearly visible on the following clip:
https://youtu.be/GGIoeO5btng
tl;dr; in 1.14 A LOT has been added to entity contructor, which causes lots of CPU. Most mobs won't spawn anyways, so e.g. initializing their brains etc at Constructor rather than with some init() method afterwards is not a good idea. Second component to the CPU drain are collision checks (even before entity is created). 1.14 collision check code seems to be much more complex comparing to 1.13, and despite 1.13 calling it twice for each mob, one call in 1.14 costs about 10 times more.
Reproduction steps: create a flat world with one block (white concrete, why not), press Alt F3, observe TPS graph, ->doMobSpawning false, observe graph.
Evidences:
Full code (replaced factory method with simple cow constructor for clarity - no effect on the final result)
[See Exhibit 1]
Commented out collision check with predefined BBs (without creating a mob)
[See Exhibit 2]
Commented out entity creation code - taking a sample cow instead (so skipping CTOR)
[See Exhibit 3]
To overcome high memory turnaroundn and high CPU usage problem in creating mobs, I propose to use a level-specific cache of one mob per mob type and until they are not spawned, they remain in the cache, and get reused to check the spawning conditions.
On top of that, to combat high CPU requirements to measure block collisions, since most worlds are created mainly with solid and 'non-collidable' blocks, I propose an extra check if a spawn mob is in a non-collidable area. This on the other end is very cheap and allows to accept most cases quickly.
You might need to test this solution, but I don't think its incorrect:
https://youtu.be/hxWk72xfAjY
The following test were performed based on this partifular modification to the source code.