I tried Compiled Typescript
363 segments
Hey, you know how TypeScript is
it's a language in which is not really
actually executed JavaScript is and so
TypeScript is a language in which is
transpiled interpreted and just-in-time
compiled language that executes in
usually in environments such as V8 or
JavaScript Core or QuickJS Fabrice, huh?
Recipient of programmer of the month.
Any who, what if just hear me out here.
What if instead of having TypeScript be
a transpiled language, what if
you could have it be a compiled
language?
Huh? Sounds tantalizing, sounds
delightful, sounds actually like
Electron could stop eating 1 GB of my
memory is what that actually sounds
like. Well, hey. If you look right here
scriptsy, that's exactly what it does.
It actually takes TypeScript and
produces either a debuggable C backend
or LLVM IR and badabing badaboom a
little bit of clanging and we got
ourselves binary, a static one too. So,
of course I couldn't resist the
temptation. I had to do a little bit of
testing, produced a little bit of graphs
and the results they were actually
pretty interesting and there's actually
a really good reason why this thing
exists and why I think it actually could
be quite useful. And at the end of the
day, you know, you probably have known
me for a little bit and known that I
don't necessarily love the TypeScript /
JavaScript ecosystem. It's It's somewhat
of a dumpster fire, but hey, anything
that could reduce memory in today's day
and age where AI has effectively just
destroyed all pricing sounds like quite
the lovely invention. So, I figured we
just had to take a look at it and I
thought you might be kind of excited
about this cuz hey, is this going to
make your server significantly faster?
Maybe. But, of course before we begin, a
quick thank you from the sponsors. The
next 100 billion commits are coming
faster than anyone has expected and that
means that your CI it needs to be
blazingly fast. Namespace optimizes for
fast coding, testing, and build
environments across Windows, Linux, and
Mac. Mac?
They even have Mac?
That is their singular focus, and that
focus runs down to the metal because
they design and deploy their own server
racks around the world, which runs their
GitHub action runners. You can remote
desktop or even SSH in, and you know I
love SSH, and debug why a test is
failing. You or your agent can optimize
performance issues. Namespace is trusted
by 11 labs, ramp, warp, vanta, framer,
zed, fowl, sierra, whisper flow, duck
db, even ghosty uses them. You should
try it out today at namespace.so.
All right, welcome back hooligans. So,
for this I decided to make quick three
experiments just to kind of showcase
where I think it's really powerful, and
then a fourth experiment that really
shows that not everything that glitters
is gold. So, the first three
experiments, the first one is the
minimum memory test. Now, for those that
don't know, if you just literally write
out while true, and you launch node,
it's going to use something like 40 to
80 gigabytes.
>> [music]
[bell]
>> It's depending on the version. If you
launch bun, it's going to be smaller,
but still it uses a lot of memory. And
so, it's kind of a fun experiment. What
does something like script c do where
they can strip away a lot of bun? Are
you going to actually see like a pretty
small program? Maybe. The second test is
pretty obvious. Is now instead of just a
little bit of memory, let's allocate a
million objects. What does that do? Does
that make us have a just a little bit of
memory? Do we have a lot of bit of
memory? Is there actually a substantial
difference between the two? I don't
know. The third test is actually got
startup time test where I start a
server, have a single endpoint, start a
little listening, and until I can make a
TCP connection to it, it's not
considered started. And so I just do
this as fast as I can and see how long
does each one of these different
environments take to launch. And of
course, the different environments are
Node, Bun, and Script C. Now, the reason
why I chose these three particular tests
is because I think it's going to
highlight where Script C is going to
shine the most. And if you don't
understand why, well, this is from
Vercel Labs. Now, you can imagine that
Vercel, they make a lot of requests,
okay? A lot of things happen. If you can
improve startup time in Vercel land,
those extra 50, 60, 100 milliseconds
that you would be paying in other
environments, this could dramatically
make a large difference, which I think
we can all agree. Also, memory, if you
could dramatically reduce memory, again,
one could imagine that this would make a
huge, huge difference. But here's the
thing is I didn't just want test that
only kind of favor the startup of uh say
a serverless environment. I also wanted
a test that was a little bit wonky. So,
I created this one. This is actually a
hydration from an old test, which is a
one of the my old performance videos,
which would create a game that would run
at 60 ticks per second. And player one
and player two would spawn in. They're
both web socket connections. Then
there's a game tick in the background
using set timeout zero or set timeout,
however much time it needs to sleep to
be able to maintain effectively 60 ticks
per second on the server, and it plays
through the game. Now, as you can see in
this beautiful animation, player one won
because player one shot faster than
player two. It's a very simple game.
Bullets collide with each other. Almost
nothing actually is happening, but it's
kind of this fun experiment to run on.
Now, this is obviously a very unusual
test case for TypeScript or JavaScript
because this is not the average thing
people are making. The average thing
people are making is a site that calls
out to a couple other things, such as
Redis or a database or wherever, and
then aggregates that information, maybe
does something with React, and then
hands it back. So, for each one of the
three first tests, I ran them several
times, and then I said, "Okay, hey, put
yourself into P10, P25, P50, P75, P90,
and P99." Some beautiful quantiles, and
I want you to show me kind of where the
memory's at. As you can see with the
minimum memory test, this one, of
course, is the one where I allocate
nothing and just simply start up and
then take a memory snapshot using VMRSS.
As you can see, Node in the kind of
best-case P10 takes about 70 MB. Now, of
course, this is going to be Node 26. It
would be a lot different if you're using
something like Node 22. But,
nonetheless, this is where it's at. Bun
is 40 MB, but Script C 2.2 MB. Actually,
pretty dang good. Like, this makes a lot
of sense. This is fantastic. This is
what you'd want to see. And this pattern
effectively holds through throughout the
whole thing. The P99 for Node is almost
the exact same amount of memory the P99
for Bun and the P99 for uh Script C,
almost all identical. Pretty much right
around in the exact same area. The same
test, except for the one where I create
a million of those objects, this is
where things actually get pretty dang
interesting. If we look at Node, it's
259
MB in the smallest, and of course, at
the 99 it's 265. So, not a lot of change
between any of the percentiles,
everything seems to be about the same.
But, at Script C, only 88 MB. Now, that
seems really, really low, right? Well,
that's because with Script C, what it
can do is it can actually compile based
on the TypeScript definitions, it can
actually represent your types as C
structs. So, if we actually look at the
JavaScript code, I don't know you know,
I asked the LLM to generate it, okay? It
just chose this as the way it generated.
I don't know. Crazy, whatever. It
doesn't really matter. Anyways, this is
the code that was written for the
TypeScript item. It's obviously pushing
in one of these particles. If we go to
out, you can see right here that out is
a type particle. If you go to particle,
you can see right here that particle is
this beautiful thing right here, okay?
Now, what makes things interesting is
actually looking at this inside of the C
code. As you can see, it's the exact
same effectively set of properties
except for now they are defined with
real types. Since this thing doesn't
have a cycle, since the struct is
effectively used and defined well,
you're going to get something that looks
like this, which means that you're
actually getting a really optimal use
case. You're actually getting memory
that's not crazy. The only unusual thing
is this right here. Turns out that it
actually uses reference counting
underneath the hood as opposed to
garbage collection.
Hey,
that's I guess that's pretty neat. And
that's why you see the difference here
is because the type that is defined
becomes an actual struct of only size
72. Whereas with V8, it's 140 bytes
effectively for that struct. That can be
a little bit different depending on the
versions, blah blah blah blah blah, but
that's kind of like what the assumed or
kind of calculated amount of bytes is.
For something like JavaScript Core, it
could be 88 bytes. So, it's a little bit
smaller, but nonetheless, as you can
see, the C representation is
significantly better. And that obviously
bears itself out in these graphs. You
see that the memory used is 88 megabytes
versus 176 for Bun and 260 for Node. V8
loves that memory, okay? V8 just They
want all the memory in the universe. I
know that ScriptEase isn't something
that's like meant to be used to replace
Electron apps or anything, but there is
something that at at least is minimally
interesting in the fact that maybe one
day, deep into the future, Electron apps
can quit using like 1 to 10 gigabytes of
my system memory every single one of
them. There is a future that this could
actually be pretty pretty sweet to stop
destroying everything that I love. You
understand why I hate you, TypeScript,
right? The more interesting one is this
startup time. So, remember, with the
startup time, we start a server and we
wait until we can do a TCP connection to
that server. When it comes to node, it
takes about 56 milliseconds for it to
start up, create a server, and for us to
connect to. When it comes to bun, that's
about 15.8 milliseconds, significantly
faster. But with ScriptC, it's only 2.3
milliseconds. Now you can imagine that
since this is created by Vercel, you can
see why this is so appealing. You could
imagine a world in which these things
are compiled and, you know, behind the
scenes, and now they're going from, say,
tens of milliseconds down to just like
ones of milliseconds. That's a big
difference. That actually makes cold
starts significantly better. Now the
program that we are running, obviously,
is really small, can be statically
compiled. This will look different, I
assume, with actual, like, real
applications. But it was pretty cool to
see this. Now you're probably wondering,
"Well, what about THE SHOOT GAME? YEAH,
WHAT ABOUT THE SHOOT GAME WITH THE
people that are shooting each other?"
And like, "Well, what was the result of
that one?" Because that one was way
different than the rest of the tests.
It's not some simple little memory test.
Well, you're right. a simple memory
test, but we did turn it into a memory
test. As far as the performance goes,
they all kind of performed the same,
whether it was, you know, 1,600
concurrent games, 1,200 concurrent
games. I didn't really get a strong
read. Now I could have done some things
to probably make this show up a little
bit better. Probably could have isolated
it down to like a single CPU and really
reduce the amount of available memory,
and maybe that would have made node much
slower and then bun and then ScriptC.
But, you know what? Just running it on
my laptop, I didn't see much difference
as far as like performance goes. They
all were the same. It's actually the
memory that's pretty wild. So the three
basic tests we did here is that we had
800 concurrent games, 1,200 concurrent
games, and 1,600 concurrent games. Using
VMRS, you can see that node, even in
like the 50th percentile, used 170 megs
for 800 concurrent games, 222 megs for
1,200, and 228 for 1,600. Not very good.
If you look at bun, bun's 131 to 132 to
140. Okay, a little bit more consistent,
but it's actually look at this script C
only 30 megs during that. So, it
actually is something that is, you know,
better. 44 for 1,200 concurrent games
and 58 for 1,600 concurrent games. It
honestly
not that bad. Anyways, it was a lot of
fun playing with this. I'm not actually
sure if it is any sort of
production-ready type stuff. I'm not
telling you to go and run over and throw
all your crap on there for an
instantaneous memory savings, but it is
kind of interesting. Like minimally,
this is something that is pretty cool
and maybe at some point Electron will
finally stop sucking butt.
Just two cheeks.
Electron. Hey, is that an Electron app
right there? Yeah, I didn't like my
memory either. And there was a boss one
time at Netflix who was famous for
saying, "You got to use all the memory
that you're given." You know, that was
said. Like it was some version of that
where instead of saving the memory, we
had to use all the memory on television
devices. Yeah, well, needless to say, it
turns out using memory was actually
really bad. The name
is it's pretty cool. Hey, Purcell, I'll
give you one. I did not like Zero that
language you created. At least I made
fun of it. Uh sorry if it hurt your
feelings, but this one
this one, hey, it's at least a cool
direction.
A gen.
Ask follow-up questions or revisit key timestamps.
The video explores 'ScriptC', a project from Vercel that compiles TypeScript into static binaries using LLVM IR or C backends. Through a series of performance tests, the creator compares the memory usage and startup times of ScriptC against Node.js and Bun. The results show that ScriptC significantly reduces memory consumption and drastically improves startup times, especially by utilizing C structs instead of traditional garbage-collected JavaScript objects. While the creator cautions that it may not be production-ready yet, they highlight the potential for this technology to significantly improve resource efficiency for future applications, including potentially addressing the high memory usage of Electron apps.
Videos recently processed by our community