HomeVideos

I tried Compiled Typescript

Now Playing

I tried Compiled Typescript

Transcript

363 segments

0:00

Hey, you know how TypeScript is

0:02

it's a language in which is not really

0:04

actually executed JavaScript is and so

0:07

TypeScript is a language in which is

0:08

transpiled interpreted and just-in-time

0:11

compiled language that executes in

0:13

usually in environments such as V8 or

0:15

JavaScript Core or QuickJS Fabrice, huh?

0:17

Recipient of programmer of the month.

0:19

Any who, what if just hear me out here.

0:22

What if instead of having TypeScript be

0:25

a transpiled language, what if

0:28

you could have it be a compiled

0:30

language?

0:31

Huh? Sounds tantalizing, sounds

0:34

delightful, sounds actually like

0:36

Electron could stop eating 1 GB of my

0:39

memory is what that actually sounds

0:41

like. Well, hey. If you look right here

0:43

scriptsy, that's exactly what it does.

0:46

It actually takes TypeScript and

0:48

produces either a debuggable C backend

0:51

or LLVM IR and badabing badaboom a

0:54

little bit of clanging and we got

0:56

ourselves binary, a static one too. So,

0:59

of course I couldn't resist the

1:00

temptation. I had to do a little bit of

1:03

testing, produced a little bit of graphs

1:05

and the results they were actually

1:07

pretty interesting and there's actually

1:08

a really good reason why this thing

1:11

exists and why I think it actually could

1:12

be quite useful. And at the end of the

1:14

day, you know, you probably have known

1:16

me for a little bit and known that I

1:17

don't necessarily love the TypeScript /

1:20

JavaScript ecosystem. It's It's somewhat

1:23

of a dumpster fire, but hey, anything

1:25

that could reduce memory in today's day

1:28

and age where AI has effectively just

1:30

destroyed all pricing sounds like quite

1:33

the lovely invention. So, I figured we

1:35

just had to take a look at it and I

1:37

thought you might be kind of excited

1:39

about this cuz hey, is this going to

1:40

make your server significantly faster?

1:43

Maybe. But, of course before we begin, a

1:46

quick thank you from the sponsors. The

1:47

next 100 billion commits are coming

1:49

faster than anyone has expected and that

1:51

means that your CI it needs to be

1:53

blazingly fast. Namespace optimizes for

1:55

fast coding, testing, and build

1:58

environments across Windows, Linux, and

2:00

Mac. Mac?

2:02

They even have Mac?

2:04

That is their singular focus, and that

2:06

focus runs down to the metal because

2:08

they design and deploy their own server

2:10

racks around the world, which runs their

2:12

GitHub action runners. You can remote

2:15

desktop or even SSH in, and you know I

2:18

love SSH, and debug why a test is

2:20

failing. You or your agent can optimize

2:23

performance issues. Namespace is trusted

2:25

by 11 labs, ramp, warp, vanta, framer,

2:28

zed, fowl, sierra, whisper flow, duck

2:31

db, even ghosty uses them. You should

2:34

try it out today at namespace.so.

2:36

All right, welcome back hooligans. So,

2:38

for this I decided to make quick three

2:40

experiments just to kind of showcase

2:42

where I think it's really powerful, and

2:44

then a fourth experiment that really

2:46

shows that not everything that glitters

2:47

is gold. So, the first three

2:49

experiments, the first one is the

2:50

minimum memory test. Now, for those that

2:52

don't know, if you just literally write

2:55

out while true, and you launch node,

2:57

it's going to use something like 40 to

3:00

80 gigabytes.

3:08

>> [music]

3:11

[bell]

3:14

>> It's depending on the version. If you

3:16

launch bun, it's going to be smaller,

3:18

but still it uses a lot of memory. And

3:21

so, it's kind of a fun experiment. What

3:23

does something like script c do where

3:25

they can strip away a lot of bun? Are

3:27

you going to actually see like a pretty

3:28

small program? Maybe. The second test is

3:31

pretty obvious. Is now instead of just a

3:33

little bit of memory, let's allocate a

3:35

million objects. What does that do? Does

3:37

that make us have a just a little bit of

3:39

memory? Do we have a lot of bit of

3:41

memory? Is there actually a substantial

3:42

difference between the two? I don't

3:44

know. The third test is actually got

3:46

startup time test where I start a

3:48

server, have a single endpoint, start a

3:50

little listening, and until I can make a

3:52

TCP connection to it, it's not

3:54

considered started. And so I just do

3:56

this as fast as I can and see how long

3:59

does each one of these different

4:01

environments take to launch. And of

4:02

course, the different environments are

4:04

Node, Bun, and Script C. Now, the reason

4:06

why I chose these three particular tests

4:08

is because I think it's going to

4:09

highlight where Script C is going to

4:11

shine the most. And if you don't

4:13

understand why, well, this is from

4:14

Vercel Labs. Now, you can imagine that

4:16

Vercel, they make a lot of requests,

4:18

okay? A lot of things happen. If you can

4:20

improve startup time in Vercel land,

4:23

those extra 50, 60, 100 milliseconds

4:26

that you would be paying in other

4:27

environments, this could dramatically

4:29

make a large difference, which I think

4:31

we can all agree. Also, memory, if you

4:33

could dramatically reduce memory, again,

4:36

one could imagine that this would make a

4:38

huge, huge difference. But here's the

4:41

thing is I didn't just want test that

4:43

only kind of favor the startup of uh say

4:47

a serverless environment. I also wanted

4:50

a test that was a little bit wonky. So,

4:52

I created this one. This is actually a

4:53

hydration from an old test, which is a

4:55

one of the my old performance videos,

4:58

which would create a game that would run

5:00

at 60 ticks per second. And player one

5:03

and player two would spawn in. They're

5:04

both web socket connections. Then

5:06

there's a game tick in the background

5:08

using set timeout zero or set timeout,

5:10

however much time it needs to sleep to

5:12

be able to maintain effectively 60 ticks

5:14

per second on the server, and it plays

5:16

through the game. Now, as you can see in

5:18

this beautiful animation, player one won

5:20

because player one shot faster than

5:22

player two. It's a very simple game.

5:25

Bullets collide with each other. Almost

5:27

nothing actually is happening, but it's

5:29

kind of this fun experiment to run on.

5:31

Now, this is obviously a very unusual

5:34

test case for TypeScript or JavaScript

5:35

because this is not the average thing

5:37

people are making. The average thing

5:39

people are making is a site that calls

5:40

out to a couple other things, such as

5:43

Redis or a database or wherever, and

5:45

then aggregates that information, maybe

5:48

does something with React, and then

5:49

hands it back. So, for each one of the

5:51

three first tests, I ran them several

5:52

times, and then I said, "Okay, hey, put

5:54

yourself into P10, P25, P50, P75, P90,

5:58

and P99." Some beautiful quantiles, and

6:00

I want you to show me kind of where the

6:02

memory's at. As you can see with the

6:04

minimum memory test, this one, of

6:06

course, is the one where I allocate

6:07

nothing and just simply start up and

6:09

then take a memory snapshot using VMRSS.

6:12

As you can see, Node in the kind of

6:14

best-case P10 takes about 70 MB. Now, of

6:17

course, this is going to be Node 26. It

6:19

would be a lot different if you're using

6:21

something like Node 22. But,

6:22

nonetheless, this is where it's at. Bun

6:25

is 40 MB, but Script C 2.2 MB. Actually,

6:30

pretty dang good. Like, this makes a lot

6:32

of sense. This is fantastic. This is

6:34

what you'd want to see. And this pattern

6:35

effectively holds through throughout the

6:37

whole thing. The P99 for Node is almost

6:40

the exact same amount of memory the P99

6:42

for Bun and the P99 for uh Script C,

6:45

almost all identical. Pretty much right

6:48

around in the exact same area. The same

6:50

test, except for the one where I create

6:52

a million of those objects, this is

6:53

where things actually get pretty dang

6:55

interesting. If we look at Node, it's

6:57

259

6:59

MB in the smallest, and of course, at

7:01

the 99 it's 265. So, not a lot of change

7:03

between any of the percentiles,

7:04

everything seems to be about the same.

7:06

But, at Script C, only 88 MB. Now, that

7:10

seems really, really low, right? Well,

7:13

that's because with Script C, what it

7:15

can do is it can actually compile based

7:17

on the TypeScript definitions, it can

7:19

actually represent your types as C

7:23

structs. So, if we actually look at the

7:24

JavaScript code, I don't know you know,

7:26

I asked the LLM to generate it, okay? It

7:28

just chose this as the way it generated.

7:30

I don't know. Crazy, whatever. It

7:31

doesn't really matter. Anyways, this is

7:34

the code that was written for the

7:35

TypeScript item. It's obviously pushing

7:38

in one of these particles. If we go to

7:40

out, you can see right here that out is

7:42

a type particle. If you go to particle,

7:44

you can see right here that particle is

7:46

this beautiful thing right here, okay?

7:48

Now, what makes things interesting is

7:49

actually looking at this inside of the C

7:52

code. As you can see, it's the exact

7:54

same effectively set of properties

7:57

except for now they are defined with

7:59

real types. Since this thing doesn't

8:00

have a cycle, since the struct is

8:02

effectively used and defined well,

8:05

you're going to get something that looks

8:06

like this, which means that you're

8:07

actually getting a really optimal use

8:09

case. You're actually getting memory

8:11

that's not crazy. The only unusual thing

8:13

is this right here. Turns out that it

8:15

actually uses reference counting

8:17

underneath the hood as opposed to

8:18

garbage collection.

8:20

Hey,

8:21

that's I guess that's pretty neat. And

8:23

that's why you see the difference here

8:24

is because the type that is defined

8:26

becomes an actual struct of only size

8:29

72. Whereas with V8, it's 140 bytes

8:33

effectively for that struct. That can be

8:34

a little bit different depending on the

8:36

versions, blah blah blah blah blah, but

8:37

that's kind of like what the assumed or

8:40

kind of calculated amount of bytes is.

8:42

For something like JavaScript Core, it

8:44

could be 88 bytes. So, it's a little bit

8:46

smaller, but nonetheless, as you can

8:47

see, the C representation is

8:49

significantly better. And that obviously

8:51

bears itself out in these graphs. You

8:54

see that the memory used is 88 megabytes

8:56

versus 176 for Bun and 260 for Node. V8

9:00

loves that memory, okay? V8 just They

9:02

want all the memory in the universe. I

9:04

know that ScriptEase isn't something

9:06

that's like meant to be used to replace

9:08

Electron apps or anything, but there is

9:10

something that at at least is minimally

9:12

interesting in the fact that maybe one

9:14

day, deep into the future, Electron apps

9:17

can quit using like 1 to 10 gigabytes of

9:19

my system memory every single one of

9:21

them. There is a future that this could

9:23

actually be pretty pretty sweet to stop

9:25

destroying everything that I love. You

9:27

understand why I hate you, TypeScript,

9:28

right? The more interesting one is this

9:30

startup time. So, remember, with the

9:32

startup time, we start a server and we

9:34

wait until we can do a TCP connection to

9:37

that server. When it comes to node, it

9:38

takes about 56 milliseconds for it to

9:41

start up, create a server, and for us to

9:44

connect to. When it comes to bun, that's

9:46

about 15.8 milliseconds, significantly

9:49

faster. But with ScriptC, it's only 2.3

9:52

milliseconds. Now you can imagine that

9:54

since this is created by Vercel, you can

9:55

see why this is so appealing. You could

9:58

imagine a world in which these things

10:00

are compiled and, you know, behind the

10:02

scenes, and now they're going from, say,

10:04

tens of milliseconds down to just like

10:07

ones of milliseconds. That's a big

10:09

difference. That actually makes cold

10:10

starts significantly better. Now the

10:12

program that we are running, obviously,

10:14

is really small, can be statically

10:15

compiled. This will look different, I

10:17

assume, with actual, like, real

10:19

applications. But it was pretty cool to

10:21

see this. Now you're probably wondering,

10:24

"Well, what about THE SHOOT GAME? YEAH,

10:25

WHAT ABOUT THE SHOOT GAME WITH THE

10:26

people that are shooting each other?"

10:28

And like, "Well, what was the result of

10:29

that one?" Because that one was way

10:31

different than the rest of the tests.

10:32

It's not some simple little memory test.

10:34

Well, you're right. a simple memory

10:36

test, but we did turn it into a memory

10:38

test. As far as the performance goes,

10:40

they all kind of performed the same,

10:42

whether it was, you know, 1,600

10:44

concurrent games, 1,200 concurrent

10:46

games. I didn't really get a strong

10:48

read. Now I could have done some things

10:49

to probably make this show up a little

10:51

bit better. Probably could have isolated

10:53

it down to like a single CPU and really

10:55

reduce the amount of available memory,

10:56

and maybe that would have made node much

10:58

slower and then bun and then ScriptC.

10:59

But, you know what? Just running it on

11:01

my laptop, I didn't see much difference

11:02

as far as like performance goes. They

11:04

all were the same. It's actually the

11:06

memory that's pretty wild. So the three

11:08

basic tests we did here is that we had

11:10

800 concurrent games, 1,200 concurrent

11:12

games, and 1,600 concurrent games. Using

11:14

VMRS, you can see that node, even in

11:17

like the 50th percentile, used 170 megs

11:20

for 800 concurrent games, 222 megs for

11:24

1,200, and 228 for 1,600. Not very good.

11:28

If you look at bun, bun's 131 to 132 to

11:32

140. Okay, a little bit more consistent,

11:34

but it's actually look at this script C

11:36

only 30 megs during that. So, it

11:37

actually is something that is, you know,

11:41

better. 44 for 1,200 concurrent games

11:44

and 58 for 1,600 concurrent games. It

11:46

honestly

11:48

not that bad. Anyways, it was a lot of

11:50

fun playing with this. I'm not actually

11:51

sure if it is any sort of

11:52

production-ready type stuff. I'm not

11:54

telling you to go and run over and throw

11:56

all your crap on there for an

11:58

instantaneous memory savings, but it is

12:00

kind of interesting. Like minimally,

12:02

this is something that is pretty cool

12:04

and maybe at some point Electron will

12:06

finally stop sucking butt.

12:09

Just two cheeks.

12:11

Electron. Hey, is that an Electron app

12:14

right there? Yeah, I didn't like my

12:16

memory either. And there was a boss one

12:18

time at Netflix who was famous for

12:20

saying, "You got to use all the memory

12:22

that you're given." You know, that was

12:24

said. Like it was some version of that

12:26

where instead of saving the memory, we

12:28

had to use all the memory on television

12:30

devices. Yeah, well, needless to say, it

12:33

turns out using memory was actually

12:34

really bad. The name

12:37

is it's pretty cool. Hey, Purcell, I'll

12:39

give you one. I did not like Zero that

12:41

language you created. At least I made

12:43

fun of it. Uh sorry if it hurt your

12:45

feelings, but this one

12:47

this one, hey, it's at least a cool

12:49

direction.

12:52

A gen.

Interactive Summary

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.

Suggested questions

3 ready-made prompts