When parsing manifests, we merge the main hostclass' code. Each merge creates a new BlockExpression containing the children from the old and new codes: https://github.com/puppetlabs/puppet/blob/e0ea9a23bc867d337abf6c9d274e46729b058f15/lib/puppet/resource/type.rb#L145
This means we're copying the children from the code that has already been merged code into {{self}} N times and the array gets bigger each time. This accounts for about 11MB when running the The many_modules benchmark with creates 100 iterations modules each with 11 manifests and it generates an extra 11MB of allocated memory.
It seems like we should be * appending* the new code with the already merged code while preserving the flattening behavior.