Propel 1.3 (beta) in Symfony!

Well, there we go. I have Propel 1.3 (beta) working happily (so far!) in Symfony. I'm not using it for anything massive just yet, but it's still early days. This is very much a pre-pre-alpha plugin using beta software, so I'm expecting it's going to be far from perfect - gotta start somewhere, though, right?

I'm going to start with some benchmarks, to (again) convince myself (and hopefully others) that Propel is worth sticking to. Nothing against Doctrine, but I really prefer the Propel syntax (helloooo code-completion!). It's all about the right tool for the job, in my opinion, and until Propel sorts out some easier m:m joins, I'll have a place in my heart for Doctrine (which looks to be a fair way off for now!). Onwards!

So, my awfully crude benchmark pretty much does the following:

  • Creates n objects

  • Selects all of the just-added objects

  • Loops through all of the objects, and performs a 'retrieve by PK' on each of them

  • Loops through all of the objects, and deletes them



Yes, I told you it was crude. I'm really not sure the best way to test it, but to me this gives it a thorough workout (enough to notice a difference, anyway).

The objects I'm creating are generic 'Post' objects (as in a weblog post) - has a title, an excerpt, and a body. Nothing too fancy.

After each of the operations, I grab the microtime() difference from when it started, and use that to gauge performance.

The results? Well, let's start with Propel 1.2. Yes, the bottleneck in Symfony. The version that's using Creole to slow it to a crawl.

Over 500 iterations, we get the following:
Insert: 0.994376897812
List: 0.0413889884949
Read: 0.599091053009
Delete: 0.645387887955
Total: 2.28024482727

Over 2000 iterations, we get:
Insert: 3.39400696754
List: 0.124103069305
Read: 3.10692286491
Delete: 2.10164809227
Total: 8.72668099403

The total scales to about 4x, as you'd expect with 4x the work.

Okay, what about Doctrine? I'm using the latest sfDoctrine plugin (which, I assume pulls out the latest Doctrine library with it). 500 iterations yeilds:
Insert: 0.736166954041
List: 0.159096956253
Read: 0.274580001831
Delete: 0.28937792778
Total: 1.4592218399

Faster, awesome! 2000 iterations:
Insert: 2.96746706963
List: 1.11887407303
Read: 1.21426391602
Delete: 1.23314905167
Total: 6.53375411034

Sweet, that's pretty good. What's Propel 1.3 got for us? 500 iterations:
Insert: 0.671382188797
List: 0.0208058357239
Read: 0.269011974335
Delete: 0.264425992966
Total: 1.22562599182

Awesome, fastest so far! But how's it go with 2000 iterations? Let's see:
Insert: 2.05528903008
List: 0.0838809013367
Read: 1.01248407364
Delete: 0.981089830399
Total: 4.13274383545

Wow! Around 100% faster than Propel 1.2!

So what does that show us? I don't know how much it shows us, really. I do know that it shows that Propel seems to execute things faster (I guess mostly due to less magic methods and whatnot), but that's only half of the story. Hrm.

Okay, well, my server just died. But I have a few more stats (from a couple of nights ago) which are actually quite phenomenal. I'd really like to replicate them, but that's not to be tonight.

I jumped into the _dev environment to grab some entire run time duration, from the start of execution until the end. According to Symfony, using the Doctrine plugin, it takes around 15000ms to run 2000 iterations of the same benchmark. Wait for it.. It took Propel 1.3 a touch under 5000ms on average to do the exact same thing. I tried to do the same on Propel 1.2, which died a 'out of memory' death (128M isn't enough, and I don't intend on finding out what is enough!).

So what do we know? Propel 1.3 is fast (well, in terms of PHP ORMs). Due to a number of factors that I can deduce, namely that it's using PDO instead of Creole now (which also means it's loading/parsing/handling a whole lot less PHP code, which inherently implies a performance boost). It's also using the generated code files, instead of relying on magic methods for getting/setting properties.

There are a few breaking changes in 1.3 over 1.2, but for most simple apps, you're not likely to run into them. If you're writing your own SQL (using ->prepareStatement() and ->executeQuery() etc), then you'll need to modify those just a little, but for the most it works just fine. I used the same schema.yml and code to run the benchmarks on 1.2 and 1.3, so it's pretty backward compatible!

I'll have some code for release very soon, I just need to tidy it up a little first. I'm expecting it to be broken on all sorts of configurations, but hopefully before too long it'll be stable enough to get by with for non-critical applications. Stay tuned!

Wednesday, May 9. 2007

3 Comments