carterbryden

carterbryden

With Flame out - what can we do to speed up Elixir startup times?

I saw with the initial announcement that, on Fly at least, a cold start is ~3 seconds, which is pretty good!

I’m wondering if there’s anything we can do as a community or individually to get that as low as possible. To that end, I have a few questions I hope people more knowledgeable than me can answer:

  1. Is that startup time more a Fly machines thing, or more due to Elixir’s startup time?

  2. Are there startup optimisations for Elixir that haven’t been worked on yet because it’s been more than good enough so far?

  3. What about changes to our individual setups, is there anything in configuration of our apps or beam settings that might lower startup times?

Most Liked

chrismccord

chrismccord

Creator of Phoenix

Nothing to do with Elixir in this case. I haven’t measured the beam release startup for a typical phoenix app in a while, but it’s not a concern in the context of this original question. The three seconds is for Fly to pull the image, place it, connect it to our proxy and the child to Node.connect back to the parent. The release start is in there somewhere, but complete unmeasured guess is less than 0.5s for a phoenix app to be up serving traffic. So I don’t think anything will move the needle much here with any beam/elixir optimizations.

Having said all that, the current 3s baseline is not a concern for me. Faster is always better of course, and it’s possible the fly team can work some optimizations on our side. For example, waking up idle (already created) machines is usually 500ms. But there is some nuance to comparing cold starts to typical FaaS. I know AWS also keeps their functions “hot” underneath but it’s much less 1:1 for us. Runners in the pool will almost always be serving many concurrent operations (the pools :max_concurrency) vs thinking of them as cold starting to serve 1 user. Also, the way elixir supervision trees start up also guarantee that :min runners will be hot before the app starts serving traffic, so you aren’t bouncing servers to then hit thundering herd of cold starts. You can also use a min: 0 , but still warm up a min number in your sup tree to get scale to zero, but warm startup. The runners would idle down after boot if no traffic is active.

The missing piece currently is our pool growth strategy waits for the pool to be at capacity before growing, so cold starts can be hit as the pool grows. Next steps are to mitigate these cases with more sophisticated strategies like observing growth rate / percentage based early starts / etc

So I hope that helps give some insight. Faster is always better, but current baseline is not a concern for me, and I don’t think beam/elixir startup has much to move the needle on the FLAME startup side.

10
Post #5
shanesveller

shanesveller

Better to say that it meets your tolerances - and those of many developers and organizations. But let’s not discuss it as if there’s no room for improvement, or for criticism, or that “slow startup” is not a matter of perspective. Dismissing the OP’s concern like that isn’t cool IMO.

Elixir’s a VM-based language with a runtime that takes measurable time to start, which is why we’re still suboptimal for things like adhoc scripting and creating CLI applications and wouldn’t have been a natural choice for actual Lambdas either. The BEAM is probably competitive with something like Python or Node for cold-start time but it is probably not quite the same level as something like Golang or Rust + Tokio is capable of. It’s a little better than non-Graal JVM IIRC.

Anecdata on my 2023 16" M2 Max:

hyperfine --warmup 2 -S none --export-markdown results.md -- 'elixir -e \'System.halt(0)\'' 'elixir -e \'IO.puts("Hello world")\''
Command Mean [ms] Min [ms] Max [ms] Relative
elixir -e 'System.halt(0)' 95.7 ± 0.9 93.9 98.4 1.00
elixir -e 'IO.puts("Hello world")' 97.2 ± 1.0 95.0 99.3 1.02 ± 0.01

That’s not loading an application or starting an Ecto pool or anything a lot of projects would need to be actually useful, just the most trivial things the runtime is capable of. So IMO it’s not a great floor to start from.

This Rust example starts a Tokio runtime (with a WAY smaller scope than OTP, so not particularly apples-to-apples in that regard), then does a TCP dial and writes to the conn before shutting down:

gh repo clone tokio-rs/tokio
cd tokio/examples
cargo build --release --example hello_world
cd ..
# https://nmap.org/ncat/
ncat -kl 6142
# new shell
hyperfine --warmup 3 -S none --export-markdown rust-results.md -- './target/release/examples/hello_world'
Command Mean [ms] Min [ms] Max [ms] Relative
./target/release/examples/hello_world 1.6 ± 0.2 1.3 4.0 1.00

On a platform like Fly or EC2 any of that is dwarfed by provisioning time, of course. And for “traditional” Elixir applications, no one much cares because the ratio between “time spent serving a purpose” and “time spent starting/restarting” is extreme.

adw632

adw632

I accept that there is a different setup process in how workloads get spun up on fly vs AWS.

The Firecracker VM (a KVM based VM tech that AWS developed and what fly use to run our workloads and “transmogrify” containers into).

Firecracker is awesome as it provides VM level isolation that containers can’t , but the VM abstraction is also different to allow incredibly fast boot times (eg no ACPI).

Fundamentally a Linux VM takes about 75ms to start on Firecracker.

In contrast FreeBSD boots in 25ms so a savings of 50ms in start time.

Phoenix server startup time for releases is quite fast but I haven’t timed it precisely either. It is may be as high as 50-100ms and I am interested if anyone has a precise metric to time taken to start phoenix and listen on the socket.

So accounting for VM start, OS boot and BEAM/phoenix start should be achievable in well under 100ms. I remember the Erlang Ling VM could boot on Xen and serve a request in about 50ms.

A 50ms target is certainly a possibility on FreeBSD but not on Linux as its boot time on Firecracker is 75ms, not including starting the BEAM. Fly doesn’t support FreeBSD currently so right now it’s more likely that integating FLAME on AWS offers the path to much lower startup latency.

The 3+ second start time is almost all provisioning overhead and further opportunity for fly to optimise. Certainly scheduling work, and copying images to the target instance adds some unavoidable overhead due to that approach.

I guess it’s a fly business decision for approaching this differently from a warm start service tier. I can imagine low latency startup for serving dynamic workloads is something people would pay for, but getting the balance right between a dynamic on demand service pricing vs commitment tier pricing is something that needs consideration also.

Where Next?

Popular in Discussions Top

ricklove
I was just introduced to Elixir and Phoenix. I was told about the 2 million websocket test that was done 2 years ago. From my research, t...
New
WolfDan
After doing a port from a c++ library to my project in phoenix I’ve seen that I need a faster way to run this algorithm and I found this ...
New
mbenatti
Following https://github.com/tbrand/which_is_the_fastest |> https://raw.githubusercontent.com/tbrand/which_is_the_fastest/master/imgs...
New
AlexMcConnell
The reason that Rails is as popular as it is is because it’s very easy for relatively inexperienced developers to get a lot of work done....
588 19675 166
New
andre1sk
A big advantage to Elixir is all the distributed goodness but for many applications running on multiple nodes having integrated Etcd, Zoo...
New
nburkley
AWS re:Invent is on at the moment with some interesting announcements. One new feature in particular is the Lambda Runtime API for AWS La...
New
fireproofsocks
This is more of a general question, but I’m wondering how other people in the community think about the pattern matching in function sign...
New
nunobernardes99
Hi there Elixir friends :vulcan_salute: In a recent task I was on, I needed to check in two dates which of them is the maximum and which...
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
arcanemachine
https://nitter.net/josevalim/status/1744395345872683471 https://twitter.com/josevalim/status/1744395345872683471
New

Other popular topics Top

vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New

We're in Beta

About us Mission Statement