# Clojure Web Server Performance

**URL:** https://clojureverse.org/t/clojure-web-server-performance/4516
**Category:** Your Projects and Libraries
**Created:** [July 10, 2019, 7:55pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516 "2019-07-10T19:55:55Z")
**Posts on this page:** 17
**Page:** 1

<div class="post-metadata">

### Author: ![ikitommi](https://clojureverse.org/user_avatar/clojureverse.org/ikitommi/32/5797_2.png) [@ikitommi](https://clojureverse.org/u/ikitommi)
#### Post date: [July 10, 2019, 7:55pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/1 "2019-07-10T19:55:55Z")

</div>

[Round18](https://www.techempower.com/benchmarks/#section=data-r18&hw=ph&test=json) of TechEmpower Web Framework Benchmarks are out. [reitit entry](https://github.com/TechEmpower/FrameworkBenchmarks/blob/master/frameworks/Clojure/reitit/src/hello/handler.clj) did well, being fastest of all Clojure, Scala and Kotlin entries (my favourite JVM languages) on physical hardware and second or first on cloud setup in all tests it was submitted. The JSON entry was only 3% slower than the fastest c++ entry. Not bad for a dynamically typed language using immutable data structures.

Clojure libraries used:

- [reitit](https://github.com/metosin/reitit) for routing
- [jsonista](https://github.com/metosin/jsonista) for handling JSON
- [porsas](https://github.com/metosin/porsas) for JDBC & Async SQL
- [hikari-cp](https://github.com/tomekw/hikari-cp) for JDBC pooling
- [immutant-nio](https://github.com/ikitommi/immutant) (a perf fork of immutant) as web server

The async sql test was badly configured and it was much slower than the JDBC version, will try to make it good too, should be much faster (and non-blocking). Ideas and help on tuning the tests most welcome. Also, will start using [pohjavirta](https://github.com/metosin/pohjavirta) as a simple and fast web server. Have toyed also with [vert.x](https://vertx.io/) clojure bindings as the async SQL library used already uses it under the hoods.

Benchmarks shouldn’t be taken too seriously, but I think we can say Clojure can be fast too 😉

* * *

Top Clojure, Scala & Kotlin entries on physical hardware:

 ![json](https://clojureverse.org/uploads/default/original/2X/e/ef37207d58240191e9f7a72ba80d9444d8bf8af8.png)

 ![db](https://clojureverse.org/uploads/default/original/2X/7/7b39a9635b99249b9954ed88b02209e4459733f6.png)

 ![plaintext](https://clojureverse.org/uploads/default/original/2X/c/c333e73ca6b01ba3fb426259266fed61568029b7.png)

---

<div class="post-metadata">

### Author: ![seancorfield](https://clojureverse.org/user_avatar/clojureverse.org/seancorfield/32/195_2.png) [@seancorfield](https://clojureverse.org/u/seancorfield)
#### Post date: [July 10, 2019, 8:20pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/2 "2019-07-10T20:20:17Z")

</div>

Nice to see that you can “supercharge” `next.jdbc` by providing a custom builder!

---

<div class="post-metadata">

### Author: ![ikitommi](https://clojureverse.org/user_avatar/clojureverse.org/ikitommi/32/5797_2.png) [@ikitommi](https://clojureverse.org/u/ikitommi)
#### Post date: [July 10, 2019, 8:27pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/3 "2019-07-10T20:27:18Z")

</div>

… thanks to the awesome extension api in `next.jdbc`!

---

<div class="post-metadata">

### Author: ![ericnormand](https://clojureverse.org/user_avatar/clojureverse.org/ericnormand/32/1099_2.png) [@ericnormand](https://clojureverse.org/u/ericnormand)
#### Post date: [July 16, 2019, 3:42pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/4 "2019-07-16T15:42:20Z")

</div>

Wow, @ikitommi ! This is awesome work. Very important for the community.

---

<div class="post-metadata">

### Author: ![ShiTianshu](https://clojureverse.org/user_avatar/clojureverse.org/shitianshu/32/1276_2.png) [@ShiTianshu](https://clojureverse.org/u/ShiTianshu)
#### Post date: [July 22, 2019, 4:06pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/5 "2019-07-22T16:06:34Z")

</div>

what about widely using datafy on Java Object -\> Clojure Data, will it faster? can be a wrapper for undertow run as fast as undertow?

---

<div class="post-metadata">

### Author: ![ikitommi](https://clojureverse.org/user_avatar/clojureverse.org/ikitommi/32/5797_2.png) [@ikitommi](https://clojureverse.org/u/ikitommi)
#### Post date: [July 25, 2019, 2:53pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/6 "2019-07-25T14:53:26Z")

</div>

Good question. datafy is an extra abstraction and doesn’t make things any faster, most likely, slower. Pohjavirta has currently basically the same perf as Undertow in the tests (±2%). In database test, the reitit entry is actually faster than the Undertow entry, with 360k TPS.

The tested apps are kinda bare bones, real life application perf is heavily effected by the middleware/interceptor libraries used.

Undertow3 will swap [XNIO to netty](http://undertow.io/blog/2019/04/15/Undertow-3.html), looking forward to seeing how it effects things.

---

<div class="post-metadata">

### Author: ![ShiTianshu](https://clojureverse.org/user_avatar/clojureverse.org/shitianshu/32/1276_2.png) [@ShiTianshu](https://clojureverse.org/u/ShiTianshu)
#### Post date: [July 27, 2019, 4:35am UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/7 "2019-07-27T04:35:43Z")

</div>

So that means converting from java object to clojure data structure isn’t a problem? I was always thinking there’s a cost when parsing request and building response.

---

<div class="post-metadata">

### Author: ![ikitommi](https://clojureverse.org/user_avatar/clojureverse.org/ikitommi/32/5797_2.png) [@ikitommi](https://clojureverse.org/u/ikitommi)
#### Post date: [July 30, 2019, 7:37pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/8 "2019-07-30T19:37:43Z")

</div>

Copying from Java to Clojure is an issue performance wise. Both Aleph and Pohjavirta use a zero-copy request from [Potemkin](https://github.com/ztellman/potemkin). With that, you pay an increased price on reading from request, but creating one is basically free. Ideal would be to get a ~free creation and pay a constant protocol method overhead to java classes on read, e.g. no map lookups involved, at the library level.

Related discussion in the [ring repo](https://github.com/ring-clojure/ring/issues/372).

---

<div class="post-metadata">

### Author: ![Devices\_For](https://clojureverse.org/user_avatar/clojureverse.org/devices_for/32/2247_2.png) [@Devices\_For](https://clojureverse.org/u/Devices_For)
#### Post date: [July 31, 2019, 5:00pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/9 "2019-07-31T17:00:35Z")

</div>

Any reason the most important benchmark (more “realistic”), fortunes is not there?

---

<div class="post-metadata">

### Author: ![ikitommi](https://clojureverse.org/user_avatar/clojureverse.org/ikitommi/32/5797_2.png) [@ikitommi](https://clojureverse.org/u/ikitommi)
#### Post date: [July 31, 2019, 7:53pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/10 "2019-07-31T19:53:50Z")

</div>

Fortunes involves server side templating, would need to find a fast clojure (java wrapper) for that first. My best guess would be to wrap [rocker](https://github.com/fizzed/rocker/blob/master/README.md). Maybe later.

---

<div class="post-metadata">

### Author: ![ikitommi](https://clojureverse.org/user_avatar/clojureverse.org/ikitommi/32/5797_2.png) [@ikitommi](https://clojureverse.org/u/ikitommi)
#### Post date: [August 14, 2019, 8:55am UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/11 "2019-08-14T08:55:02Z")

</div>

Small update, tests are now ported to `pohjavirta` & async postgresql client pooling works now correctly. The `porsas` async client seems bit faster than the synchronous one with much smaller connection pool (16 vs 256).

 ![porsas](https://clojureverse.org/uploads/default/original/2X/d/d7d0f20fc7a93d04bb9ec7f7c145b07a60920c40.png)

---

<div class="post-metadata">

### Author: ![teodorlu](https://clojureverse.org/user_avatar/clojureverse.org/teodorlu/32/2896_2.png) [@teodorlu](https://clojureverse.org/u/teodorlu)
#### Post date: [August 14, 2019, 9:59am UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/12 "2019-08-14T09:59:37Z")

</div>

Hello @ikitommi!

It’s been interesting to follow your work in this thread. Thanks for posting!

Do you like the experience of working with performance sensitive code in Clojure compared to other languages? Does the REPL play a central role?

I’m kind of curious. I would think that ease of experimentation and ease of testing were advantages, but other ecosystems like Rust do have speed ingrained in their DNA, to a sense.

Teodor

---

<div class="post-metadata">

### Author: ![ikitommi](https://clojureverse.org/user_avatar/clojureverse.org/ikitommi/32/5797_2.png) [@ikitommi](https://clojureverse.org/u/ikitommi)
#### Post date: [August 14, 2019, 2:22pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/13 "2019-08-14T14:22:52Z")

</div>

Writing performant code with Clojure requires some extra knowledge on how things work under the hood (e.g. read the source, both clojure & java), some extra discipline, jvm interop and performance measurement tools. It’s much easier to be fast with statically typed languages, where things are usually fast by default. With Clojure, it’s not usually the default/idiomatic way to do thigs. Our open source library code looks very much different than the business code in the projects, performance versus clarity.

If you want to go fast with Clojure, good place to start would be [http://clojure-goes-fast.com/](http://clojure-goes-fast.com/) 😉

For workflow, you don’t normally have to leave the REPL, profilers can hook into your running REPL and tools like [criterium](https://github.com/hugoduncan/criterium) and [clj-async-profiler](https://github.com/clojure-goes-fast/clj-async-profiler) are just libs you invoke when you need them.

---

<div class="post-metadata">

### Author: ![teodorlu](https://clojureverse.org/user_avatar/clojureverse.org/teodorlu/32/2896_2.png) [@teodorlu](https://clojureverse.org/u/teodorlu)
#### Post date: [August 14, 2019, 2:43pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/14 "2019-08-14T14:43:12Z")

</div>

Thanks for the explanation. I wasn’t aware that you could use the profiling tools from within the REPL – that’s encouraging!

---

<div class="post-metadata">

### Author: ![ikitommi](https://clojureverse.org/user_avatar/clojureverse.org/ikitommi/32/5797_2.png) [@ikitommi](https://clojureverse.org/u/ikitommi)
#### Post date: [October 6, 2019, 8:44am UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/15 "2019-10-06T08:44:06Z")

</div>

All tests are now ported to use [pohjavirta](https://github.com/metosin/pohjavirta), and after changing the strategy of Ring request mapping from buggy ZeroCopyRequest to PartialCopyRequest, the stack is starting to pretty fast 🙂

 ![TFB%20JSON](https://clojureverse.org/uploads/default/original/2X/5/53b320ec56e1869ad5b97c7e29d5f81fc17bb46d.png)

(we are still 40% behind in the db-round-trip, have some ideas for that too)

> **[TechEmpower Web Framework Performance Comparison](https://www.techempower.com/benchmarks/#section=test&runid=42f65a64-69b2-400d-b24e-20ecec9848bc&hw=ph&test=json)**
>
> Performance comparison of a wide spectrum of web application frameworks and platforms using community-contributed test implementations.

---

<div class="post-metadata">

### Author: ![daver](https://clojureverse.org/user_avatar/clojureverse.org/daver/32/3333_2.png) [@daver](https://clojureverse.org/u/daver)
#### Post date: [October 22, 2019, 1:16am UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/16 "2019-10-22T01:16:21Z")

</div>

@ikitommi, it has been fun watching you optimize this over the last several months. F\*ck yeah! 😀

---

<div class="post-metadata">

### Author: ![system](https://clojureverse.org/uploads/default/original/2X/5/51079bf9e4b7d9466242c06cf1e43b9f8bd6da14.png) [@system](https://clojureverse.org/u/system)
#### Post date: [April 21, 2020, 1:16pm UTC](https://clojureverse.org/t/clojure-web-server-performance/4516/17 "2020-04-21T13:16:23Z")

</div>

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.
