Is postMessage slow? - HTTP 203
Skills:
Frontend Performance90%
Key Takeaways
Investigates the performance of postMessage() in web workers
Full Transcript
[Laughter] [Music] all right so what I basically did is I wrote a blog post right that was interesting it was it good excellent nobody else seemed to agree with that brilliant so you can gonna shove it down everyone's throat by putting it into a 2 or 3 episode boom good tactic excellent what we're talking about so Oh your article yes yes we're talking once again I'm talking about Lucas yeah stop brilliant I've been doing some research and figuring out the rough edges with workers what actually is the performance benefit and the performance impact where's the cost worst again in a minute it was basically at the very least I want most apps to use workers to manage the state because state most of the time is completely decoupled from any Dom or most main thread ap is it's just a state object yeah we see that with like the Redux stores kind of stuff there's very little in there that is like Dom specific so why isn't Redux on your worker for example right right would see one example ok let's let's talk about a fictional another to do this app because we don't have enough though ok let's assume we would put our state in a class to do state it has all the method you would want like you added to do you talked a little you could even subscribe to changes so you get a call that gets called whenever something changes in the to-do list and to Jason Bay see turns this class into a small JSON object that you can serialize or send over the wire and then you have an internal notify function that notifies all subscribers and so if you put a callback in to subscribe it's gonna add it to the subscribers rate presumably right right ok ok so I know what we're doing here is just on the other I add a comm link because it makes it easier so in the sense we are just exposing an instance of to do state which means we can use it in other threads without having to worry about post message make sense alright alright so that means on the main thread it would look like this we create a worker we wrap it in comlink and now we have is this instance of the even though it actually lives somewhere else Tom blink is magic so what we can do we can you know and who say we can call subscribe pass on our callback so this callback will called every time to say changes we could call render which could be a react render or HTML or whatnot doesn't really matter right now really inconsistent use of semicolons in this slide I not very actually very much bothering me only I'm missing one on to Oh fine I'm missing two so it there once they invented prettier like my skills are ruined because it I just I just didn't run it on this for some reason I should've yep um and we have if you have like a new task button you can add a click listener and you know call add to do because that's the that's the benefit that come wing gives you yeah okay gotcha so everybody on the same page roughly how I imagine people using a worker like literally just write a class it has your state you just call the methods on it and suddenly you have your logic of main thread which has lots of benefits but I'm not gonna go into I wrote blog post about them we can link them in description more reads for me alright yes but I want to take a closer look at this notify function because the fact and we're calling a function that is on the main thread while we are in the work is actually that's the core magic of comlink right and that is kind of comparable to this you know there's more details happening under the hood but really what comlink is doing it just sends a message via a post message to the other thread saying you know what invoke this function with these arguments and let me know what the return values a lot of comlink is just knowing like the thing that called it where to send the reply back to you exactly that's the most of the core logic of Emily true what most people worry about for some reason is is the slow or or if they actually have the the kind of the assumption that this is slow or your thread hopping the serialization something about civilization but nobody really I think knows what they mean specifically in this head so I thought I want to dig into this and I want to measure it and I want to see at what point we're actually entering a problem zone okay okay so so I looked at the spec and I kept looking at the spec the and I was like this is a bit overwhelming no HTML spec I tell by the colors exactly so I I duck through it I kinda get it now but I'm gonna for the sake of this episode I'm time to read that I'm gonna turn into pseudocode oh oh the spirit right okay so when you send call post message the parameter is the message that you want to send the target is kind of implicit if it's work at a post message you obviously do the work yes in the world it's self dot post message which implicitly means the main thread that spawned you you will receive the message what is happening is the parameter which I call data yet the message one is sent gets serialized with a function called structured serialize the function is not real at least not in the seventh exposed to JavaScript but it exists in the spec so I guess we should say that New Yor to Jason function that we used before was returning an object not just a string you know an object right yes it also be a string it doesn't like structured CS doesn't care but it wasn't doing its own serialization no not really so much just turn you into a adjacent impossible objects yeah right so the structure that will turn this into a serialized format that serialized format is not aspect it's JavaScript engine internal writes gotcha um but it basic just means it is some form of binary representation that is not a Java object in its current state but and capsulate s-- all the keys and values and all the things right contain the next step is queue a task in the target realm now the target realm is basically the threat that will receive the message and we're now putting something into the task queue that will run some code once that task gets scalar which we don't need or when that's gonna be and that's that's how it's going to show you we could currently or in the HTML document that it needs to be doing stuff in the work on the worker thread and this is how it gets onto a thread exactly right now the first step of the task is to turn that serialize data back into a new instance of data so this is add vertically a deep copy of our original message object and yes this is kind of important because in JavaScript you can share objects like the one of the basic assumption of childhood is everything is single threaded so there's no synchronized or parallel access to the same data object or same memory with JavaScript so we can't just send the same object over it has to be a copy right and these two steps is how that is achieved gotcha and this is a slight this is different to JSON serialization because it supports more formats it handles stuff like cyclic data structures yes it can your blobs Maps sets array buffers all these things that Jason can not do yes and then the last step here is basically dispatching the actual event so at that point your message event handler will get called and we'll have the data object on the event object which you can just access and now you own it and it's in your own and all its good cool brain yeah right so this is how postmessage works now the interesting bit of course is structured serialize and structured deserialize which are the functions that are most likely the expensive ones yes of course but even more importantly and something I didn't realize before I wrote an article is that structured serialize is a function that will block the sending realm while structured deserialize will block the receiving realm and I guess ok so for this for the serialize that makes sense because it doesn't want to be doing that work while you're editing the object right right ok ok well let's see you even with deserialize it makes sense because it is using the object of your realm like the arrays and whatnot so it can't really run those necessarily while your code is still running right so if you changed like I don't know the prototype or something halfway through you wouldn't expect that to be reflected exactly but this will be calling this won't be calling like stuff on the global like the now race stuff not sure I feel like that could be I think I've heard they have considered doing that but as of now it is not current will block the receiving realm gotcha so that's actually interesting because it means so far I've always been measuring I I I keep measuring the moment from when I start sending an object to when I receive it but the number I get out is actually two parts it's one part is the serializing and one part is the deserializing rights which are happening in different realms and so i would like to measure them separately but i haven't found a good way to do that so i'm still measuring that in my bench we're going to talk about but just something to keep in mind that the number we are going to talk about while I do the dreaded micro benchmark these numbers will represents the sum of serialization and deserialization so the actual cost on each thread will be something lower than that number so how are you measuring that it's basically I you just make a marker just before you call post message and it's a your n or less okay yeah I got the message pretty pretty much that's what I'm measuring right and I decided to do that because I found a couple of ways how to maybe measure just deserializing isolation I found no way to just measure serialized in isolation but I could only figure out these ways use these ways in Chrome and Safari so I wouldn't have been able to do Firefox and so I thought the end-to-end test gives me an upper bound what an either side of postmessage give you the serialized time not sure okay maybe maybe I might add of other stuff as well right okay okay so everybody do end-to-end is it like this is the upper bound like it will definitely be less expensive than this so it gives you something it gives you a worst case to reason about which I exercise is probably better if we're talking about resilience and similar issues the first thing that I wanted to find out it's not necessarily get hard numbers but just figure out what shape of a message will make post message slow is it general really complex object lots of notes or can't also be very simple object with very big strings as values and so what I did is Betty I wrote a function that generates very different objects with sometimes very small with long long keys between like two to four kilobytes and sometimes very complex graphs of objects with am just very short keys and short values something those lines it feels like deep would be more effort because it's trying to he's gonna spin round these serialized you guys know if you think about it both functions will have to somehow traverse the entire object so my hunch was as well that simple object with longer values would be faster to copy then complex object with simpler as it turns out it's actually fairly linear with this serialized payload size so if you have an object like a kind of wave what's going on all right I'm but it'll explain that so but if you basically Jason stringify your payload and look at the length of that string yes as a size that's a very very strong indicator for how long it's going to really so even just like if it's a thing which is one massive string yes even long strings take a long time to copy it seems or the other way aren't it's just as fast to copy a complex object in a simple object if the same size so which one you go there keep in mind both scales are logarithmic because otherwise as they are um so this correlation kind of holds mathematically mmm but only really four objects above 10 kilobytes because if you look at it you can see it's curving inwards there's a couple of outliers yeah and it's for a couple of reasons one is that we still have reduced precision timer's due to spectra meltdown right yes also even add some jitter to the timing so that you can took you know make it less useful for these high precision timing attacks that were involved of course but also that's at the lower end just some weird fluctuations and setting overhead just an excuse the numbers more dot sure so this was run on my macbook pro we're ended up with about five microseconds per kilobyte this number is not really useful I it will be different on any other device right it's not something it's worth measuring if you're targeting a targeting a low-end for right so I definitely don't measure these numbers and make decisions of that I just want it interesting to see like which kind of scale we're talking well sure so in other words you what I did so now that I knew basically it doesn't matter just like the the string if I'd size is a good indicator of how long it works I just want to use that across a couple of devices and see at what point do we run into trouble in terms of rail budgets okay so we're talking when it starts getting over sixteen for example yeah that's basically it I started on my macbook in chrome that a thousand runs for each constellation here and basically to see at what point do we run into rail budget problems so basically we're looking for this numbers around or bigger than 60 milliseconds so if you look at this over here that the green area which means anything between her kilobytes and one megabyte of payload size that's where on a MacBook Pro in Chrome we are in trouble zone and lower than that we're absolute I'm reborn when you say payload Brett for what's the unit's here it's not just likes free objects D for it I mean it's a t it's like every note on breath every note has three values or four values or five values and the depth is how deep do we go so how complex is the tree that I'm building here I have more examples on my blog post but basically what really just looking at is because we know now that object size is good anyway we just look at the colors really so sick sick so we're talking 36 what's the how many six to the six six to the six coughs of course of course of course right okay so it's quite a big object that is yeah that's why we came up with like ten megabytes and I think each key leaf key is a two kilobytes string so it adds up to a lot a lot of data gotcha right okay I'm following um yeah we can see if we ten cent megabytes we're blocking 474 milliseconds which is quite a lot and if we have animations running that will be too much 47 47 milliseconds so it's only format German Germany yes amazing I did not so 32 in German is silent as a spy oh right I thought you were just joking no like after four years here it screws me up yeah for free and twenties and they're like I don't know why like everything else in German is logical mostly so it's it takes seven and forty milliseconds yes exactly 47 um but anything below hard kilobytes we will be absolutely fine we won't block the main threat too long to like make our animations jank however and this is the most important bit this is on a MacBook Pro like this is not representative of the average device device especially oh so this is gonna work when you're on a phone right it's a danger zone I ran it on a Nokia too so it's actually pretty representative of the 50th percentile device across the world yes very much in the middle despite the hardware being stuck in like 2014 roughly speaking mm-hmm go home but yeah so well circles got bigger yeah so if we now look for you know numbers around the 60 millisecond mark we can now see that you know what to be safe we can't go much bigger than 10 kilobytes all right good this this here we have a 12 up here which might be but okay 12 it's you know if we look at the blue and turquoise that's between yes oh we can't go much bigger than 10 kilobyte or in our rail budget but the real boundary hasn't shifted that much it's that much like this number has got bigger right but if you send in megabytes you're bonkers yes it's gonna take a long time it's gonna take over half a second foot but generally it's that's actually we're really happy because I feel like if you have any nations running you're limited to 10 kilobytes actually 10 kilobytes is quite a lot you can put out of stuff anything kind of like yeah if you're just like shifting around billions and numbers which yeah that being said do you remember this game that we built oh my word it's procs yeah so because this is the the maximum possible feel it doesn't even actually fit on the screen it's 40 by 40 that's the biggest field we currently allow yes 1,600 cells right yeah and each of these cells has a couple of flags to store indeed exactly these that we have in the code yes of course and that is basically our game state like we have this 40 by 40 two-dimensional array and each cell has this data stored in it so we know what the game field looks like now turns out that that json stringify tactually and adds up to about a hundred 30 kilobytes of Jason oh so we are way too big to send the entire state over yes and it would say that we're being somewhat like well not lazy but like we could just have one or a behalf per cell for example or something yeah it could be an array buffer because yeah what about like 32 or how many bits of information do we need here I mean maybe 32 would be fine I mean because like the touching mind switching Flags only goes up to 8 bits these 3 bits and then you've got 9 bits 9 bits 9 bits right okay so this could just be an array buffer it doesn't need to be all one number it doesn't need to two dimensional because we as long as we know the width like yeah yeah yeah that being said we ended up noticing that we take at the start you were just set whenever somebody taps something we have to send the updated state to the main thread yes so we can rear ender turns out that took too long on the low-end phones like we couldn't do it and we know so much from the on the performance panel and dev tools that that was too long and now what we did is something else now this is again I gave you but we said that we would instead of sending the entire state we are sending the fields that changed yes so we basically did a DIF and that's kind of cool because it means our the amount that we have to send it's not proportional to the game set anymore but only to the amount of changes that we do to the game state yes now even that's in some situations especially on the first click could add up a lot of data cuz you a big reveal at the start right and up to in theory something about 70 kilobytes if we assume that like 80% of a field gets revealed which is unlikely but you know assume the worst case yeah all right so we did another thing and that's actually the thing I want to talk about because I find it really smart we've gotten to the thing you want to talk about may we have been recording for quite now we get to the meat of the episode right somebody taps a field yep our game logic basically traverses through the game forth and figures out which fields need to change the state mm-hmm and we record these changes because we want to send over the changes but whenever we have found 10 changes or more which we just like 10 why not previous it's the number we pull them out of thin air yeah we sent those 10 changes immediately for the main thread can start rendering and doing stuff while the worker keeps going and keeps traversing because it's not just the serialize and deserialize cost it's it's the cost of just crawling the grid yeah well right exactly yeah and then we realized that the way we wrote that algorithms to traverse the fields actually looks really nice so on slow phones which I have a recording here it looks like your really nice reveal animation even though on bigger device this will be instance like it does any matrices able so we'll just be wouldn't be there immediately but on slower devices you'll get a nice animation yes and this is how we made this huge state object actually work on the lowest of low end devices you know I thought I was pretty proud of us for doing that well and we to the point where when we found this when we saw this happening we made it happen on desktop as well artificially with the an amazing that's up to get this animation because it was the second pass of this algorithm because the first time we were you doing depth-first yeah and we ran into stack problems and then so we switched to EQ based and that's when we ended up with my the point is that for you for managing your cert in a worker there's so many little tricks you can do without bending over backwards that I think there's no good excuse to not put your signal work and help is so many on this loan device we've actually we have done a test or we ran procs all on main thread and it performed horribly so we actually already got which shows to use a worker right from the start especially because we have animation happening all the time yes it's difficult see probably from from this angle but there's a well because this is the no animated view as well but usually these little squares have got in in a row show like we actually of WebGL running we need all the budget and the main that we can get to the WebGL so everything else as much as we can we moved it somewhere else and we're quite happy we did that so I think postmessage has a cost or another point where it makes off main thread like completely unavailable so I'm hoping that you know with this people will be kind of inspired to try it out and give off main thread a shot for managing this day yeah we have some jitter that is artificially added and of course not after artificially added jitter sorry and say you also say it's artificially added again because I interrupt it and also you said I'd be honest today yeah sure you did
Original Description
Jake and Surma look at workers and the performance of their messaging primitive postMessage().
Surma’s blog post on the topic: https://dassur.ma/things/is-postmessage-slow/
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Uploads from Chrome for Developers · Chrome for Developers · 0 of 60
← Previous
Next →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Polymer Performance Patterns (The Polymer Summit 2015)
Chrome for Developers
Polymer Power Tools (The Polymer Summit 2015)
Chrome for Developers
Chrome Dev Summit 2014 – Chrome Case Studies
Chrome for Developers
Web Directions Code 2015 round up
Chrome for Developers
Maintainable Code - HTTP203
Chrome for Developers
iron-ajax… wat?! -- Polycasts #26
Chrome for Developers
The Guardian - Supercharged
Chrome for Developers
ES2015 (next version of JavaScript), Totally Tooling Tips (S2 Ep1)
Chrome for Developers
#AskPolymer: Rob answers all the questions ever -- Polycasts #27
Chrome for Developers
The Future of JavaScript - HTTP203
Chrome for Developers
Data Binding 101 -- Polycasts #28
Chrome for Developers
The Guardian part 2 - Supercharged
Chrome for Developers
The Future of Web Audio: with Chris Wilson and Chris Lowis
Chrome for Developers
Chrome 46: New motion-path animations, client hints and service worker improvements
Chrome for Developers
Sublime Snippets, Totally Tooling Tips (S2 Ep2)
Chrome for Developers
#AskPolymer: How do you make the show? -- Polycasts #29
Chrome for Developers
Critical Path CSS, Totally Tooling Tips (S2 Mini Tip #1)
Chrome for Developers
Binding to Objects -- Polycasts #30
Chrome for Developers
Player FM - Supercharged
Chrome for Developers
Where’s the Designer? #AskPolymer -- Polycasts #31
Chrome for Developers
Jake Beats Wikipedia - HTTP203
Chrome for Developers
Supercharged Observers! -- Polycasts #32
Chrome for Developers
Jai's Web blog - Supercharged
Chrome for Developers
Windows Command-line Tooling, Totally Tooling Tips (S2, Ep4)
Chrome for Developers
What about internationalization? #AskPolymer -- Polycasts #33
Chrome for Developers
Developing for Billions (Chrome Dev Summit 2015)
Chrome for Developers
Google+ Performance Improvement Comparison
Chrome for Developers
Deploying HTTPS: The Green Lock and Beyond (Chrome Dev Summit 2015)
Chrome for Developers
Progressive Web Apps (Chrome Dev Summit 2015)
Chrome for Developers
Instant Loading with Service Workers (Chrome Dev Summit 2015)
Chrome for Developers
Increase Engagement with Web Push Notifications (Chrome Dev Summit 2015)
Chrome for Developers
Engaging with the Real World: Web Bluetooth and Physical Web (Chrome Dev Summit 2015)
Chrome for Developers
Asking for Permission: respectful, opinionated UI (Chrome Dev Summit 2015)
Chrome for Developers
Polymer - State of the Union (Chrome Dev Summit 2015)
Chrome for Developers
Building Progressive Web Apps with Polymer (Chrome Dev Summit 2015)
Chrome for Developers
Introduction to RAIL (Chrome Dev Summit 2015)
Chrome for Developers
DevTools in 2015: Authoring to the max (Chrome Dev Summit 2015)
Chrome for Developers
RAIL in the real world (Chrome Dev Summit 2015)
Chrome for Developers
#ChromeDevSummit talks are up - W00T! -- Polycast #34
Chrome for Developers
V8 Performance from the Driver's Seat (Chrome Dev Summit 2015)
Chrome for Developers
Quantify and improve real-world RAIL (Chrome Dev Summit 2015)
Chrome for Developers
Owning your performance: RAIL (Chrome Dev Summit 2015)
Chrome for Developers
HTTP/2 101 (Chrome Dev Summit 2015)
Chrome for Developers
Leadership Panel (Chrome Dev Summit 2015)
Chrome for Developers
Build Processes, Totally Tooling Tips (S2, Ep 5)
Chrome for Developers
Accessibility (Chrome Dev Summit 2015)
Chrome for Developers
Binding to Arrays -- Polycasts #35
Chrome for Developers
HTTP2 - HTTP203
Chrome for Developers
Chrome 47: Splash Screens, requestIdleCallback and better desktop notifications (New in Chrome)
Chrome for Developers
Call For Submissions - Supercharged
Chrome for Developers
Cross Device Testing, Totally Tooling Tips (S2 Ep6)
Chrome for Developers
Testing AJAX with Web Component Tester -- Polycasts #37
Chrome for Developers
Slack: Extended Xmas Special - Supercharged
Chrome for Developers
Browser testing with Travis & Sauce Labs -- Polycasts #38
Chrome for Developers
Optimize for production with Vulcanize -- Polycasts #39
Chrome for Developers
Highlights from Chrome Dev Summit 2015
Chrome for Developers
Chrome 48: Custom buttons in notifications, DevTools Security panel, and Presentation mode
Chrome for Developers
Crisper: Protecting your Polymer app with CSP -- Polycasts #40
Chrome for Developers
How do I use Sass with Polymer? #AskPolymer -- Polycasts #41
Chrome for Developers
Colors – DevTools Tonight #0 (Pilot)
Chrome for Developers
More on: Frontend Performance
View skill →Related Reads
🎓
Tutor Explanation
DeepCamp AI