• This repository has been archived on 01/Jun/2021
  • Stars
    star
    704
  • Rank 64,316 (Top 2 %)
  • Language
    Elixir
  • Created over 10 years ago
  • Updated almost 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🏇 benchmark Sinatra-like web frameworks

Benchmarking Phoenix vs Rails vs Sinatra vs Express vs Martini...

I was curious about Chris McCord's Phoenix vs Rails article -- if you haven't read it, go do that now first. Go ahead, this will still be here when you get back. It won't make much sense unless you've read the original.

Like many other Elixir enthusiasts, I come from a Ruby background. That said, I found Rails to be a curious choice for comparison to Phoenix, since Rails is really a kitchen-sink framework meant to handle everything from database migrations to asset-pipeline compilation. Sinatra, on the other hand, is a more modular, semi-minimal framework that is appears to be pretty close in philosophy to what Phoenix looks like at the moment (at least to me).

While I didn't expect the performance to match Elixir/Phoenix, I was curious what would happen if I dropped those same ERB templates in a minimal Sinatra app instead of a Rails one.

And heck, while I was there, since Express is essentially Sinatra for Node, why not do a quick port over there for comparison too...

...And then I realized there was a Sinatra-inspired framework for Go called Martini, so what the heck, I went crazy and made one for that as well...

Important note: My benchmarking here was primarily for fun, not science. It was mostly an interesting exercise to write the identical application in many of the "Sinatra-like" web frameworks to see how similar they were across languages. More on this later.

Frameworks Compared

In the end, I had a comparison of the following:

  • Phoenix (Elixir) - EEx
  • Rails (Ruby) - ERB
  • Sinatra (Ruby) - ERB
  • Express (NodeJS) - EJS
  • Martini (Go) - template/html

Compared to @chrismccord's original tests

For the most part I tried to keep things as close as possible to the original comparison. I used the same exact templates, splitting them up into equivalent files. I tried to do things the same way in every instance, rather than getting clever and micro-optimizing for performance.

If you compare the application logic across version, you should (hopefully) find it to be very consistent.

Caveats

There was a few gotchas that I adjusted with the new additions to try to get at least a closer to an "apples to apples" comparison.

Sinatra / ERB

  • Since Phoenix didn't appear to be logging to STDOUT, Rack's default logger was disabled to be more comparable (netting a ~35% throughput increase right there).

Express / EJS

  • NodeJS cannot run multi-threaded, so cluster mode with multiple processes is required to take advantage of multi-core. I used express-cluster to make this fairly trivial to implement. I included benchmarks with this both enabled and disabled.

  • Express now pushes all partial/layout handling to the template engine. EJS doesn't really handle block layouts, only includes. Therefore, the "layout" template needs to be split into a _layout_header and _layout_footer which was manually included in the index template. This probably doesn't make a big difference but since it's the only template difference I figured I should mention it.

Martini / Go

I'm sure some people will complain about my usage of Martini versus a more idiomatic Go solution, however this is intended to be a comparison of similar style frameworks, so it makes sense to use the one that works in the same style.

Rails / ERB

I originally didn't find Rails to be as apt a comparison for this use case as Sinatra, since it contains a lot of functionality not being used here that contributes to overhead.

For that reason, I didn't do too much here. That said, there is probably a lot that can/should be disabled in Rails that is there by default for "free" if you wanted this to be a closer comparison. Pull requests welcome if you care!

Updates! Round two of benchmarks (January 2015)

Phoenix has evolved quite a bit since the first version of this post! It's taken on a lot of great functionality, moving it perhaps closer to being a Rails alternative than Sinatra. This somewhat obviates the point of these benchmarks (which were to compare to more minimal Sinatra like frameworks), but let's update them anyhow, and see if Phoenix can maintain its excellent performance characteristics while taking on more.

Additionally, I received PRs to add two new additional frameworks to the mix. Gin is another Sinatra-like for Go focused on performance. The Play Framework is a JVM platform that seems a bit more similar to Rails to me, but with the expanded functionality Phoenix has taken on, is probably a good comparison.

And finally, since Phoenix has evolved and taken on so much functionality, José Valim contributed an example of using Plug for an ultra minimal Elixir based solution.

On a side note: I never expected these tests to get so much attention, a sincere thanks to everyone who has contributed PRs, feedback, and commentary!

Benchmarking

I ran these on my iMac Intel Core i7 (4.0 GHz 4 Cores) 32 GB RAM.

Note: All benchmarks run on a local dev machine are highly suspect. If you want more scientific results these should really be done in a production server environment.

Comparative Benchmark Numbers

Framework Throughput (req/s) Latency (ms) Consistency (σ ms)
Gin 51483.20 1.94 0.63
Phoenix 43063.45 2.82 (1) 7.46
Express Cluster 27669.46 3.73 2.12
Martini 14798.46 6.81 10.34
Sinatra 9182.86 6.55 3.03
Express 9965.56 10.07 0.95
Rails 3274.81 17.25 6.88
Plug (1) 54948.14 3.83 12.40
Play (2) 63256.20 1.62 2.96
  1. Consistency for both Erlang solutions have become more unstable in this round of tests compared to previous (where it was rock solid). It appears to be somewhat specific to my Erlang install, but I haven't been able to locate a reason for it yet. Some others are experiencing similar results, we have been discussing this in the #elixir-lang IRC channel if you want to help.

  2. Play consistently appeared to generate hundreds of socket read errors (see the detailed output), so I believe it should probably be semi-disqualified from the results for now.

You can view the detailed results, or see the original round of benchmarks to compare.

Client-server benchmarks on a dual 10 core Xeon were contributed by Juliano Solanho and are also available for comparison.

UPDATE JULY 2016: There is a now a slightly more recent version update run as Round #3. Please note I do not intend to continue updating these benchmarks further past this point.

Conclusions

The only benchmarks that really matter are the ones that apply to your own environment and use case. This was really just for curiosity and fun.

That said, some things stuck out to me:

  • As seen in the numerous caveats, looking into tuning things like caching or the impact of logging strategies will have a major impact on your overall app performance, quite possibly more than platform. Think about your own use case and pick a strategy accordingly.

  • The default performance of Phoenix/Elixir is quite impressive, especially for such young projects. Given that Elixir is also one of the most conceptually enjoyable languages that I've personally coded with in years, I'm pretty bullish about their future.

  • Looking at the NodeJS/Express performance, it's clear that if you use NodeJS in production on multi-core boxes, you should probably be using cluster workers (but do your own research, they won't be appropriate for every situation.)

  • Sinatra is pretty awesome to begin with. The similarity in writing the code for all these frameworks was pretty astonishing (check out how similar they all are!), and it shows how influential the modular approach taken by Sinatra has been in inspiring other frameworks. If you are a Ruby user currently using Rails, check out Sinatra for a more bottom-up approach and compare.

Finally, really, please never focus on contrived benchmarks as a way to pick the best environment for your project. Think about your use case, but also optimize for developer productivity and happiness. I've used most of these languages / frameworks in production, and each has cases which make it more desirable in certain circumstances. There is no "best" language/framework for all situations.

Discussion / Feedback

I've worked with all of the languages involved in production systems (with the exception of Elixir, which I'm just getting started with). That said, I'm nowhere near an expert in any of them -- so feedback, comments, and pull requests are encouraged.

I'm publishing this as a GitHub repo rather than a blog post since the intended audience is nerds. If you want to comment, in lieu of a comment thread, just open a GitHub issue with the subject of your comments (or participate in one on the same topic if it already exists).

(I'm also currently working on a similar showdown for Twitter Streaming API libraries, and it could use your help!)

One last thing

If you enjoyed these benchmarks / programs or found them useful, please consider following me on GitHub or Twitter to see when I post new projects. ✌️👨✌️

More Repositories

1

unindexed

🔎❔ website that irrevocably deletes itself once indexed
JavaScript
1,315
star
2

emojitracker

💫 track ALL the emoji
743
star
3

weightedrand

⚖️ Fast weighted random selection for Go
Go
378
star
4

scmpuff

🔢 Numeric file shortcuts for common git commands
Go
373
star
5

slacknimate

👯 Realtime text animation for Slack chatops
Go
286
star
6

evalcache

🐣 zsh plugin to cache eval loads to improve shell startup time
Shell
173
star
7

bootslap

☕ bootstraps macOS to be usable
Shell
120
star
8

sseserver

🏄 High-performance Server-Sent Events endpoint for Go
Go
107
star
9

emojistatic

💩 CDN static asset generation for emoji
CSS
98
star
10

exmoji

😎 Emoji encoding swiss army knife for Elixir/Erlang
Elixir
95
star
11

emoji_data.rb

😎 Emoji encoding swiss army knife for Ruby
Ruby
87
star
12

emoji-data-js

😎 Emoji encoding swiss army knife for NodeJS
CoffeeScript
64
star
13

ramdisk

🐏 Convenience wrapper for managing RAM disks
Go
47
star
14

benchwarmer

⌛ Elixir micro-benchmarking library
Elixir
47
star
15

bump

🌻 CLI tool to draft a GitHub Release for the next semantic version
Go
46
star
16

git-muzak

🎶 Background music for your git commits
Shell
32
star
17

cameraform

📹 Simple Flash+JS library for webcam capture and submission.
ActionScript
24
star
18

recyclebin

♻️ measures usage of a particular term on twitter
Go
21
star
19

grayratio

💬 gray:blue ratio for iMessage conversations
Shell
16
star
20

twitter-streaming-showdown

🚣 benchmark Twitter Streaming API libraries
Ruby
15
star
21

cssquirt

💧 Embeds images (or directories of images) directly into CSS via the Data URI scheme.
Ruby
14
star
22

tinygeoip

🐉 tiny geoip microservice
Go
13
star
23

momocode

🍑 Visual fingerprinting for 20-byte Ethereum addresses via emoji
Solidity
12
star
24

xkcdpass

🔏 xkcd style password generator for iOS
Swift
12
star
25

pullcrusher

🎩 optimizes all images in a GitHub repo & sends a pull request with the changes.
Ruby
10
star
26

emojidoll

🎎 twitter bot to generate Emoji dolls
Ruby
10
star
27

pigstream

🐷🐤 Twitter bot that insta-taunts frustrated people who can't beat a level on Angry Birds, using the streaming API.
Ruby
10
star
28

consider

🤔 quickly check code comments for subtle (racism|sexism|ableism)
Shell
9
star
29

golang-challenge-1

Solution for Go Challenge #1 (in Go and Elixir)
Go
9
star
30

deepclean

🗑️ scan and remove junk files from your source code directories
Go
9
star
31

scalafmt-native

Statically-linked GraalVM "native image" binaries of scalafmt
Dockerfile
8
star
32

my-boxen

👔 MacOSX system provisioning via Boxen [DEPRECATED, see mroth/bootslap]
Ruby
8
star
33

git-prompt-useremail

💂‍♂️ zsh plugin adds prompt reminders for git user.email
Shell
8
star
34

hurricanecamp

🌀 Hurricane Dev Camp is a stay-at-home hackathon to coincide with Hurricane Irene.
8
star
35

subtleist

🎏 Anonymously remind of subtle-isms and other Recurse Center social rules in Slack
Go
8
star
36

nanogeoip

🐉 tiny and blazing fast experimental geoip microservice
Rust
8
star
37

stardotws

🌟 source for a fun EMOJI DOMAIN
HTML
7
star
38

upcoming-cloud-warrior

☁️ Quick script/instructions to to get the Upcoming.org Archive Team Project running on multiple Heroku instances in the cloud.
Shell
7
star
39

hubhumans

👪 Automatically create a `humans.txt` file based upon public members of a GitHub organization.
Ruby
6
star
40

bogan-martin-award

🐲 old-skool Flickr staff award website
HTML
6
star
41

goodvsevil

🐱🐶 A quick hack to compare keyword counts in the Twitter Streaming API, using puppies and kittens.
Ruby
5
star
42

portfolio

🎨 my portfolio website
HTML
5
star
43

personalappeals

🏩 Hot or Not of the Wikipedia contributors.
JavaScript
5
star
44

semverdesc

🎯 git describe with semantic version compatible names
Go
4
star
45

dotfiles

⭕ My dotfiles repository. There are many like it, but this one is mine.
Shell
4
star
46

howami

💉 command line tool to show a summary of Fitbit health data
Ruby
4
star
47

pdftotextcloud

📚 pdftotext as a web service
JavaScript
4
star
48

pybaztag

🐰 Python convenience wrapper for the Nabaztag API.
Python
4
star
49

readtime

⌚ estimate how long it will take to read text
Go
4
star
50

shopmon

🏪 Monitor for in-stock products from Shopify powered stores
Go
4
star
51

forafriendbot

🙋 twitter bot that poses questions.... for a friend.
CoffeeScript
4
star
52

flickr-nearby-webos

📱 Quick and dirty location services test for WebOS, source code to support blog post.
JavaScript
3
star
53

github-cli-xref

♻️ Tool to make it easy to cross-reference GitHub issues.
Ruby
3
star
54

fashionhack

👠 fashion hackday project with @kellan
Ruby
3
star
55

flickr-wholovesyou

💞 see which Flickr members most frequently favorite photos of you (or someone else you know!)
Ruby
3
star
56

tamanegi

🌰 generates tor .onion hashes
Go
3
star
57

hubfavor

🙇 determine who in a GitHub org is most likely to do you a favor
Ruby
3
star
58

meatballtracker

🍝 monitor and alert for delicious meatballs at a local restaurant.
Ruby
3
star
59

isgregdead

💀 Check whether a coworker (who occasionally keeps odd hours) is alive.
Ruby
3
star
60

foursquare-token-echo

4️⃣💭 simple webpage to receive and echo the client_token for a Foursquare OAuth2 request.
2
star
61

flickr-socialvenn

👬 Generates a weighted contact intersection venn diagram for a Flickr member.
2
star
62

poidh

👀 pics or it didn't happen!
Ruby
2
star
63

sse-bench

📊 benchmarks Server-Sent Events endpoints
CoffeeScript
2
star
64

dees-colors

🌈 preview text as seen by a friend with grapheme-color synesthesia.
2
star
65

jitter

👯 Go timers with random jitter
Go
2
star
66

emojicompare

😄⁉️😄 A comparison of the Apple and Twitter emoji glyphs
2
star
67

go2go-docker

🐳 Docker image for the dev.go2go experimental Go branch
Dockerfile
2
star
68

loremfile

📜 Generate lorem ipsum text of a specific size
Go
1
star
69

.github

😈 Default community health files
1
star
70

dinnermint

🍬 automated metadata processing of personal photos on Flickr.
Ruby
1
star
71

tordesc

Parser for Tor network data descriptors
Rust
1
star
72

scalafmt-docker

Simple scalafmt packaging as a Docker Hub automated build
Dockerfile
1
star
73

rando-slackrisian

🎲 super quick CLI hack to get a random member of your Slack Team
JavaScript
1
star
74

base100-go

💯 Go implementation of Base100 emoji encoding
Go
1
star
75

mta2json

🚋 Proxies reqs to the MTA for Realtime Transit Feed data, converts results to JSON.
JavaScript
1
star
76

howlong

Quick self reference to answer a question I frequently get on calls.
HTML
1
star
77

sunnyinphilly

⛅ is it sunny in Philadelphia?
1
star
78

timeduration

🕐 simple CLI tool to convert humanized time durations
Go
1
star
79

xsort

80% faster versions of Go sort.Search* wrappers
Go
1
star
80

lolcapture

📹 experimental capture tool for lolcommits
Swift
1
star
81

fitdump

📉 Parse Fitbit data export files
Go
1
star
82

khan-bootstrap

🎓 A minimal setup for hacking the Khan Academy codebase
Makefile
1
star