Você está fazendo certo -- é apenas lento, porque a abstração adicionada do ORM significa que você não pode fazer os tipos de otimizações que gostaria.
Dito isso, o EntityManager fica lento em transações tão grandes. Se você não precisa absolutamente de todos eles em uma grande transação, você provavelmente pode obter um código de melhor desempenho fazendo flush() e depois limpando o EM a cada 20-200 iterações do seu loop.
Se isso não lhe der desempenho suficiente, a única alternativa que consigo pensar é reverter para o código personalizado que executa o SQL personalizado diretamente no seu DBMS.
Eu sei que essa não é uma ótima resposta, mas pelo menos posso dizer que você não é louco.
------ editar ------
Do artigo oficial do Doctrine2 em Processamento em lote :
Além disso, há uma diferença significativa no desempenho ao usar remoto x local banco de dados, pois a sobrecarga de enviar cada consulta para o servidor remoto é bastante grande. A sobrecarga é muito menor ao usar o banco de dados local graças a transações e otimizações de banco de dados. (por exemplo, 70 segundos reduzido para 300 ms no caso de exemplo na pergunta)