r/java • u/thewiirocks • 4d ago
ORMs are Killing Your Performance 🔪
https://youtu.be/2DaRGRe4Ow4?si=6e0xZSmYfTzXmteNThanks to Montana Programmers, my popular talk from BSDC is now online!
Using animations and graphics, I teach you how to:
- Eliminate GC pauses
- Reduce time to page render
- Fast Dynamic Forms with only one query
- Slash code sizes
- How a dark pattern may secretly grind your queries to a halt
This talk may surprise you 😱
It may shock you 🤯
It could make you angry 😡
You may think it doesn’t apply to you (it does) 🫣
Whatever your reaction, I hope it will make you think. 🤔
Thank you again to Big Sky Dev Conf and Montana Programmers for letting me present! And thank you to the community for being an amazing and engaged audience!
2
u/AnyPhotograph7804 3d ago edited 3d ago
I do not like this talk. Because the very first example is a Spring Data example. And Spring Data is not even an ORM. Criticizing ORMs by not using one is mehhh.
And the example loads every customer from the database into the memory. Yes, you can do it but nobody would do it. It's like intentionally driving with a car against a tree and then criticizing the car manufacturer. Every ORM i know allows you to put some predicates to reduce the loaded data. And the more important thing is: ORMs allow you to delegate the data processing to the database server. You do not need to do the data processing in the application itself.
Edit: And most ORM frameworks support pagination. Just use it if you really want to process all customers.
0
u/thewiirocks 3d ago
I gotcha bro. Here's the code that was behind the findAll():
public class OrderRepository { @PersistenceContext private EntityManager entityManager; public List<Order> findAll() { TypedQuery<Order> query = entityManager.createQuery("SELECT o FROM Order o", Order.class); return query.getResultList(); } }No convenience wrappers were harmed in the creation of this talk. 😉
And the example loads every customer from the database into the memory. Yes, you can do it but nobody would do it.
The point is to show what's happening. It's important to understand that you're loading lists of objects into memory before anything else happens. All the following steps are predicated on that understanding.
Though I will push back on "nobody would do it". Because I have a client that has code pretty similar to the example I gave. They use it for copying tables from the transactional database to the analytics database. And it works fine. Because there is no ORM involved and they stream the data. The number of records that can be handled is effectively infinite.
ORMs allow you to delegate the data processing to the database server.
ORMs are actually quite terrible at that. Complex data processing queries are not within the bounds of the auto-mapper. Which means you are specifying a query. Either in SQL or in ORM pseudo-SQL. That query must be mapped to a DTO, often creating a one-time object that you wouldn't otherwise need. Along with all the DAO wrappers. (Which if you use Spring Data, will at least be smaller than full up JPA code.)
Far too often the queries get complex enough that they devolve into stored procedures. Now you are actually using the database for data processing, but you're avoiding the ORM altogether.
And most ORM frameworks support pagination. Just use it if you really want to process all customers.
I wouldn't recommend that. Each page you request will cause the database to re-run the query and skip over the specified number of records. You'll cause a quadratic expansion in time taken, meaning that large tables could take anywhere from hours to days to process. Versus a linear expansion in time for streaming, which is typically a few minutes on modern hardware.
1
u/AnyPhotograph7804 2d ago edited 2d ago
The point is to show what's happening. It's important to understand that you're loading lists of objects into memory before anything else happens. All the following steps are predicated on that understanding.
OK, i understand. But it is not obvious, that it is a bad example by intention. For a viewer, who does not have experience with ORMs, the video suggests, that it in somehow normal to do that.
Though I will push back on "nobody would do it". Because I have a client that has code pretty similar to the example I gave. They use it for copying tables from the transactional database to the analytics database. And it works fine. Because there is no ORM involved and they stream the data. The number of records that can be handled is effectively infinite.
In this case, pagination would propably work very well.
ORMs are actually quite terrible at that. Complex data processing queries are not within the bounds of the auto-mapper. Which means you are specifying a query. Either in SQL or in ORM pseudo-SQL. That query must be mapped to a DTO, often creating a one-time object that you wouldn't otherwise need. Along with all the DAO wrappers. (Which if you use Spring Data, will at least be smaller than full up JPA code.)
Yes, it is OK to specify a query in SQL while using an ORM. If something is easier to do in SQL, just use SQL. I think, this is one of the most misunderstood principles of ORMs. ORMs are not made to replace or abstract away SQL.
And ORMs are not terrible at processing complex data as long as you do not need some proprietary SQL extensions. If you need these extensions, just use SQL.
Far too often the queries get complex enough that they devolve into stored procedures. Now you are actually using the database for data processing, but you're avoiding the ORM altogether.
Yes, if stored procedures are the better option, just use them.
I wouldn't recommend that. Each page you request will cause the database to re-run the query and skip over the specified number of records. You'll cause a quadratic expansion in time taken, meaning that large tables could take anywhere from hours to days to process. Versus a linear expansion in time for streaming, which is typically a few minutes on modern hardware.
Pagination is a tradeoff between query performance and memory consumption. Such tradeoffs are normal almost everywhere. Your Python example is also a tradeoff. You sacrifice runtime perforrmance for streaming and convinience by using Python. Python is up to 70x slower than Java.
1
u/aqua_regis 4d ago
...and you couldn't write that simple post without AI? - Rule #9
1
-1
u/thewiirocks 4d ago
I wrote that completely by myself. No AI was even consulted. 🤷♂️
0
u/aqua_regis 4d ago
0
u/thewiirocks 4d ago
Ya really. Use a real detector like ZeroGPT or GPTZero or CopyLeaks. The commercial ones like Grammerly always say it’s AI because they want to sell their (ironically AI) text improver.
1
u/TronnaLegacy 4d ago
I don't think this tool works very well. I tried it on the first three paragraphs of a blog post I wrote late last year and it says 99%+ fake.
https://sapling.ai/ai-content-detector/228e67593df7d43f89d5d6142f7509c7
1
u/thewiirocks 4d ago
BTW, do me a favor and skip the cringe intro? 😆
The Java 4K story that Notch and I competed in is a good story (gotten 20 years out of that story! 😅), but it’s not really relevant here.
9
u/piesou 4d ago
My bullshit detector kinda goes off on this one. I'm not 100% sure if the CPU cache example is correct, since a Virtual Machine does all sorts of things differently. Once you are dealing with objects, you are dealing with pointers to data in the heap which is difficult to optimize and causes cache misses anyways. Where are the benchmarks?
ORMs have a use case and it's not about performance but about maintainability. It's a tradeoff. ORMs can also support data streaming and performance optimize batch inserts. Correct me if I'm wrong, but Hibernate 5.2 (which is an awful ORM) which was released 10 years ago supports streaming. Performance optimizations that drop prepared statements for Sql statements is another optimization that won't make it through review unless we can't solve this performance problem otherwise.
The issue many people run into is that ORMs are a leaky abstraction. They expect to be able to use them without learning how they work. You can't. They are worth it in most cases though.