# Julia can be better at doing web: A benchmark

**URL:** https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300
**Category:** Web Stack
**Tags:** performance, benchmark, sockets, http
**Created:** [August 28, 2023, 4:47pm UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300 "2023-08-28T16:47:14Z")
**Posts on this page:** 8
**Page:** 2

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [September 2, 2023, 7:52am UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300/21 "2023-09-02T07:52:18Z")

</div>

Won’t this start nproc servers all listening on the same port? I wonder if that is the best way to set things up, as that probably won’t have any load balancing or similar between the different processes.

I’m also interested in potential problems in this domain and to solve them. I applaud your effort and willingness to solve this.  
I just want to make sure that the benchmark is using the best setup according to the current design of HTTP.jl. Maybe you can raise even more awareness and involve HTTP.jl maintainers as well, by opening an issue over on GitHub?

---

<div class="post-metadata">

### Author: ![pankgeorg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pankgeorg/32/21549_2.png) [@pankgeorg](https://discourse.julialang.org/u/pankgeorg)
#### Post date: [September 5, 2023, 7:34am UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300/22 "2023-09-05T07:34:38Z")

</div>

> [@simsurace](#):
>
> Won’t this start nproc servers all listening on the same port? I wonder if that is the best way to set things up, as that probably won’t have any load balancing or similar between the different processes.

Yes - EDIT: actually the Linux kernel does provide “free” load balancing for processes listening on a `REUSEPORT` socket (so “no” to the second part 😅)

> [@simsurace](#):
>
> I just want to make sure that the benchmark is using the best setup according to the current design of HTTP.jl.

Me too, I’m trying to raise awareness and get eyes on this.

So, I’ve been busy with trying various stuff, like

- Accepting faster: [feat(server): spawn task sooner in listenloop + fast accept by pankgeorg · Pull Request #1103 · JuliaWeb/HTTP.jl · GitHub](https://github.com/JuliaWeb/HTTP.jl/pull/1103)
- Make LibPQ more async: [feat(nonblocking) implement non blocking mode for connections by pankgeorg · Pull Request #280 · iamed2/LibPQ.jl · GitHub](https://github.com/iamed2/LibPQ.jl/pull/280)

but the performance improvements are not that great (around 5% more throughput in some scenarios), and also, the non-blocking version of LibPQ is `3ms` slower than the blocking one (which is consistent with python’s asyncio, which I tested)

So, my current curiosity frontier is:

1. How much time should I expect between [starting listening on a socket](https://github.com/iamed2/LibPQ.jl/pull/280/files#diff-40aa26d41b0c99b2f88018c7c8e914ee958ce3cb5e7fb1c11902eef04f06627fR823) (FDWatcher) and actually [being notified about its state](https://github.com/iamed2/LibPQ.jl/pull/280/files#diff-40aa26d41b0c99b2f88018c7c8e914ee958ce3cb5e7fb1c11902eef04f06627fR833) (I notice a 3ms overhead on the LibPQ’s async\_execute that relies on watching the connection socket). Are there lower/upper bounds?
2. Is this [load-specific](https://github.com/JuliaLang/julia/pull/50880/files)?
3. Can I make it faster/tweak it for shorter tasks?
4. [When does the (julia) scheduler become overwhelmed](https://github.com/JuliaWeb/HTTP.jl/pull/647)?
5. When can we say “I can’t accept any more connections”?
6. Are cross-thread Channels lighter than Task.@spawn tasks that migrate (oxygen [streamUntil approach](https://github.com/ndortega/Oxygen.jl/blob/master/src/streamutil.jl#L31-L43))?

any pointers are very very welcome!

---

<div class="post-metadata">

### Author: ![simsurace](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simsurace/32/30216_2.png) [@simsurace](https://discourse.julialang.org/u/simsurace)
#### Post date: [September 5, 2023, 9:28pm UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300/23 "2023-09-05T21:28:28Z")

</div>

> [@pankgeorg](#):
>
> actually the Linux kernel does provide “free” load balancing for processes listening on a `REUSEPORT` socket

That‘s interesting. How can we be sure that a REUSEPORT socket is being used? Sorry if this should be obvious.

---

<div class="post-metadata">

### Author: ![pankgeorg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pankgeorg/32/21549_2.png) [@pankgeorg](https://discourse.julialang.org/u/pankgeorg)
#### Post date: [September 7, 2023, 10:25am UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300/24 "2023-09-07T10:25:46Z")

</div>

> [@simsurace](#):
>
> How can we be sure that a REUSEPORT socket is being used? Sorry if this should be obvious.

`HTTP.listen(; reuseaddr=true)` or `serve` or `Sockets.bind(...;reuseaddr=true)` will request it, but to be sure sure, start julia with strace `strace -e trace=setsockopt julia server.jl` (on Linux; on Windows I have no idea). Let me know if that helps!

---

<div class="post-metadata">

### Author: ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)
#### Post date: [September 24, 2023, 8:29pm UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300/25 "2023-09-24T20:29:54Z")

</div>

> [@Pangoraw](#):
>
> EDIT: I did not look at right code! The new PQ version indeed uses a pool ([here](https://github.com/pankgeorg/FrameworkBenchmarks/blob/pg/julia-new/frameworks/Julia/Http.jl/Pool.jl)).

While a “web benchmark” under discussion here, partly DB related, then connecting can possibly be improved.

I didn’t look at the pool functionality, but is it redundant with new feature in just released PostgreSQL 16, or its libpq (note, latest libpq might work for older PostgreSQL, it usually does, maybe, maybe not, for this too? Pg\_pool is also available, seems not partially redundant project): [PostgreSQL: Documentation: 16: E.2.&nbsp;Release 16](https://www.postgresql.org/docs/16/release-16.html)

> - Allow multiple libpq-specified hosts to be randomly selected (Jelte Fennema)  
> This is enabled with load\_balance\_hosts=random and can be used for load balancing.

FYI: It also has a number of good new features, such as more complete support for the SQL/JSON standard (I was proposing recently in another thread, to rather than serialize Julia data/arrays to BLOBs, i.e. its bytea proprietary type). Also off-topic:

> **[PostgreSQL 16 Released!](https://www.postgresql.org/about/news/postgresql-16-released-2715/)**
>
> September 14, 2023 - The PostgreSQL Global Development Group today announced the release of PostgreSQL 16, the latest version of …

> PostgreSQL 16 improves general support for text collations, which provide rules for how text is sorted. PostgreSQL 16 builds with ICU support by default, determines the default ICU locale from the environment, and allows users to define custom ICU collation rules.

Maybe ICU is new, or new by default, but it reminded my of that at least the latest ICU has e.g. “significant changes for GB18030-2022 compliance support”, i.e. for Chinese. That latest Chinese standard is slightly incompatible, I’m not sure if it affects Julia users, i.e. Unicode/UTF-8 too in some way.

> Version 23.1 of Ora2Pg, a free and reliable tool used to migrate an Oracle database to PostgreSQL, has been officially released and is publicly available for download.  
> […]  
> New command line option --lo\_import. By default Ora2Pg imports Oracle BLOB as bytea, the destination column is created using the bytea data type.

Even more off-topic (unless it helps some actual real-world users, if not benchmarks, maybe some Julia code should be off-loaded to the database, I think there’s a PL/Julia out there):

> [@PL/Julia extension ( minimal )](https://discourse.julialang.org/t/pl-julia-extension-minimal/34232):
>
> " The following is only the beginning of an example to create a new procedural language for PostgreSQL. At some point later we will have something that enables user defined functions and stored procedures written in the [Julia programming language](https://julialang.org/) to actually execute." [Creating a PostgreSQL procedural language - Part 1 - Setup - 2ndQuadrant | PostgreSQL](https://www.2ndquadrant.com/en/blog/creating-a-postgresql-procedural-language-part-1-setup/) ( by Mark Wong ; February 5, 2020 ) [GitHub - pljulia/pljulia](https://github.com/pljulia/pljulia) “This is enough to build, install, and create a PL/Julia extension. While you…

It’s not one of the official, but with precompiled code could be getting increasingly relevant to use (and maybe get into PostgreSQL as official?):

> **[Chapter 42. Procedural Languages](https://www.postgresql.org/docs/16/xplang.html)**
>
> Chapter 42. Procedural Languages Table of Contents 42.1. Installing Procedural Languages PostgreSQL allows user-defined functions to be written in other languages besides …

---

<div class="post-metadata">

### Author: ![pankgeorg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pankgeorg/32/21549_2.png) [@pankgeorg](https://discourse.julialang.org/u/pankgeorg)
#### Post date: [September 25, 2023, 4:46am UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300/26 "2023-09-25T04:46:55Z")

</div>

> [@Palli](#):
>
> I didn’t look at the pool functionality, but is it redundant with new feature in just released PostgreSQL 16, or its libpq (note, latest libpq might work for older PostgreSQL, it usually does, maybe, maybe not, for this too? Pg\_pool is also available, seems not partially redundant project): [PostgreSQL: Documentation: 16: E.1. Release 16](https://www.postgresql.org/docs/16/release-16.html)

If I understand the feature correctly, it works with a comma separated list of hosts ([PostgreSQL: Documentation: 16: 34.1.&nbsp;Database Connection Control Functions](https://www.postgresql.org/docs/16/libpq-connect.html#LIBPQ-CONNECT-HOST) - last paragraph) to connect to one of them randomly. To use that you need a cluster of databases that are all replicas of each other or something. You still get one connection back, and you’ll have to put it in a pool locally, for that to count. So it doesn’t make a connection pool on the client side redundant.

Note that this is not about changing the environment of the benchmark; if we change the environment (I.e. add a database cluster) we run a different experiment (and then our competitors will also get the opportunity of x10 db operations, for example).

Updating LibPQ version on the other hand isn’t something I would object too 😅. LibPQ\_jll is at version 14 IIRC

---

<div class="post-metadata">

### Author: ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)
#### Post date: [September 25, 2023, 12:21pm UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300/27 "2023-09-25T12:21:09Z")

</div>

> [@pankgeorg](#):
>
> LibPQ\_jll is at version 14 IIRC

It’s actually at version 16, “since last week”. The JLL, and I assume the underlying C wrapped library (and its version number in sync with the database version).

I would also consider wrapping: [libpqxx: the official C++ language binding for PostgreSQL](https://pqxx.org/libpqxx/#2023-01-12-at-last-faster-than-c)

> 2023-01-12: At last! Faster than C.

Then in July:

> Welcome to libpqxx 7.8.0. Lots of goodies for you. Probably enough that I could have called it 8.0 — except libpqxx 8.0 is going to require C++20. For now you’re still fine with C++17.
> 
> In 7.8 you get, among other things:
> 
> - Streaming large data sets now benchmarks faster than similar C/libpq code! […]

since it’s claims faster, and it’s “official”: [GitHub - jtv/libpqxx: The official C++ client API for PostgreSQL.](https://github.com/jtv/libpqxx)

> **The 7.x versions require at least C++17.** Make sure your compiler is up to date. For libpqxx 8.x you will need at least C++20.
> 
> Also, **7.0 makes some breaking changes in rarely used APIs:**

---

<div class="post-metadata">

### Author: ![rdavis120](https://avatars.discourse-cdn.com/v4/letter/r/b5a626/32.png) [@rdavis120](https://discourse.julialang.org/u/rdavis120)
#### Post date: [October 27, 2023, 2:23pm UTC](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300/28 "2023-10-27T14:23:35Z")

</div>

There is another web benchmark [the-benchmarker](https://github.com/the-benchmarker/web-frameworks) which covers almost all languages in a simpler test; in case there is interest in adding HTTP.jl in addition to Merly.jl

[Results](https://web-frameworks-benchmark.netlify.app/result)

[Previous page](https://discourse.julialang.org/t/julia-can-be-better-at-doing-web-a-benchmark/103300.md?page=1)
