Live data from Hacker News

Writing a Non-Blocking JavaScript Quicksort

breck-mckye.com

21–30 of 43 posts

Re: Writing a Non-Blocking JavaScript Quicksort

#21

Is computation becoming a special case of being blocked (on I/O)? I remember seeing this in an event-driven server framework (computation as the special case), this usage of "non-blocking" suggests it might be a trend.

In the browser, Javascript (outside of webworkers) is single-threaded, and while running it blocks the browser run-loop (for the given webpage.) This means the page can't accept or respond to any incoming events. It's "blocked" from interaction.

Re: Writing a Non-Blocking JavaScript Quicksort

#22
post #16
post #2

The obvious solution is to use a Web Worker, var code = "onmessage = function (evt) {evt.data.sort(); postMessage(evt.data)}"; function asyncSort(data, cb) { var worker = new Worker(URL.createObjectURL(new Blob([code]))); worker.onmessage = function (evt) { cb(evt.data); }; worker.postMessage(data); } Example, asyncSort([3, 2, 1], function (res) { console.log(res); }); Prints, "[1, 2, 3]"

It is not efficient to use a webworker when transferring the data into it and out of it takes O(N) time.

Not with transferable objects.

Re: Writing a Non-Blocking JavaScript Quicksort

#23
post #21

Is computation becoming a special case of being blocked (on I/O)? I remember seeing this in an event-driven server framework (computation as the special case), this usage of "non-blocking" suggests it might be a trend.

In the browser, Javascript (outside of webworkers) is single-threaded, and while running it blocks the browser run-loop (for the given webpage.) This means the page can't accept or respond to any incoming events. It's "blocked" from interaction.

Yes, I am aware of that.

It used to be "blocked" meant "process is waiting for an external event, usually I/O". Doing computation was typically referred to as "busy". "Non-blocking" meant "without putting the process to sleep".

There seems to be a shift in the meaning of this particular piece of terminology that I find interesting, because it seems to reflect the reality that "computers" actually do very little "computing". Instead computers mainly communicate, and even the CPUs themselves probably spend most of their time waiting on main memory (60ns) rather than doing actual arithmetic (sub ns in most cases).

Re: Writing a Non-Blocking JavaScript Quicksort

#24
post #16

Earlier quoted context omitted.

It is not efficient to use a webworker when transferring the data into it and out of it takes O(N) time.

Not with transferable objects.

From [1]:

> when transferring an ArrayBuffer from your main app to Worker, the original ArrayBuffer is cleared and no longer usable

This is really too restrictive in many situations.

[1] https://developers.google.com/web/updates/2011/12/Transferab...

Re: Writing a Non-Blocking JavaScript Quicksort

#25
post #18

Earlier quoted context omitted.

You can sort of polyfill web workers by providing the same API but running the web worker in the main thread. Edit: I assume I'm being downvoted because you're still running the script in the main thread, and therefore lose the concurrency. Obviously this is an issue with a polyfill, but the point of polyfilling web workers would be to keep the one codebase rather than writing your your web app with and then without…

Which misses the point entirely. The problem the author had was doing sorting of many items in IE8 without a warning. Using a real web worker would be a solution, but using a web worker polyfill that ran in the main thread would put him right back where he started, with IE8 triggering a warning.

I wasn't clear enough: I'd be polyfilling web workers not for this specific problem, but for other cases where I could use web workers and not lose IE8 support.

Re: Writing a Non-Blocking JavaScript Quicksort

#26

Is computation becoming a special case of being blocked (on I/O)? I remember seeing this in an event-driven server framework (computation as the special case), this usage of "non-blocking" suggests it might be a trend.

Javascript is a special problem child there because a large part of it's raison d'être is DOM manipulation and yet that's the thing that cannot be parallelized, even with web workers because the DOM has not been designed with any kind of parallelism in mind.

Other ecosystems also have problems with computations on the UI thread but running most of the application logic on different threads comes far more natural there and standard libraries offer a much richer support for multi-threading/fine-grained task execution (be it mutable or immutable shared state).

Re: Writing a Non-Blocking JavaScript Quicksort

#27
post #12

Earlier quoted context omitted.

Eek... "[setImmediate] is not expected to become standard, and is only implemented by recent builds of Internet Explorer and Node.js 0.10+. It meets resistance both from Gecko (Firefox) and Webkit (Google/Apple)." https://developer.mozilla.org/en-US/docs/Web/API/Window/setI...

Wah! I just skimmed through the Gecko bug that is linked from the above link. I recommend reading reading it through if you are interested in using these techniques on multiple browsers. It's a bit of a mess. I think there is some misunderstanding here of what the requirements are. I should be able to queue callbacks and have them execute using 100% of the CPU, but still yield to browser and IO events so that the bro…

One of the comments in the Gecko bug links to a post by Chromium developer James Robinson, which explains a lot of the technical details quite clearly: https://groups.google.com/a/chromium.org/d/msg/blink-dev/Hn3...

Re: Writing a Non-Blocking JavaScript Quicksort

#28

Is computation becoming a special case of being blocked (on I/O)? I remember seeing this in an event-driven server framework (computation as the special case), this usage of "non-blocking" suggests it might be a trend.

This confused me quite a bit as well. "Wait, what? There's such a thing as a blocking quicksort? Maybe with mutexes or something, but this is JavaScript..."

Apparently "non-blocking" is used here to mean roughly the opposite of what it normally means (that your computation doesn't slow down the I/O). Oddly enough, this seems kind of reasonable for interactive programs: the I/O is the more important part, so it's the thing you want to not interfere with.

It's still a little confusing.

Re: Writing a Non-Blocking JavaScript Quicksort

#29
post #12

Earlier quoted context omitted.

Eek... "[setImmediate] is not expected to become standard, and is only implemented by recent builds of Internet Explorer and Node.js 0.10+. It meets resistance both from Gecko (Firefox) and Webkit (Google/Apple)." https://developer.mozilla.org/en-US/docs/Web/API/Window/setI...

Wah! I just skimmed through the Gecko bug that is linked from the above link. I recommend reading reading it through if you are interested in using these techniques on multiple browsers. It's a bit of a mess. I think there is some misunderstanding here of what the requirements are. I should be able to queue callbacks and have them execute using 100% of the CPU, but still yield to browser and IO events so that the bro…

Why do you want the CPU pegged at 100%? What benefit does that provide?

Edit: ok I read some background, assume you mean it should be unclamped?

Re: Writing a Non-Blocking JavaScript Quicksort

#30
post #29

Earlier quoted context omitted.

Wah! I just skimmed through the Gecko bug that is linked from the above link. I recommend reading reading it through if you are interested in using these techniques on multiple browsers. It's a bit of a mess. I think there is some misunderstanding here of what the requirements are. I should be able to queue callbacks and have them execute using 100% of the CPU, but still yield to browser and IO events so that the bro…

Why do you want the CPU pegged at 100%? What benefit does that provide? Edit: ok I read some background, assume you mean it should be unclamped?

To be honest, I don't really understand what "unclamped" means in this context.

My background is in embedded telephony. I've worked on many systems that are single threaded because you have real time constraints. You have to make sure that your bit of work finishes in a certain amount of time and then you yield to the next bit of work. You could imagine that it would be very strange if a telephone switch handled connecting one call, but before it could connect another call it would have to "rest" for 15 ms.

The Javascript event loop is really just the reactor pattern: http://en.wikipedia.org/wiki/Reactor_pattern

There are many, many useful ways to use the reactor pattern. In fact, I have often used it in preference of threads in GUI applications because it gives you very fine grained control over how much responsiveness you want in the UI. As long as you yield every X ms, it means that the user only has to wait X ms for a response to their actions. You can also prioritise work easily. With threads, you are at the mercy of the task scheduler and so you might have very uneven response times.

In the same way, I want to be able to program things that run continuously in JS but not have to give up the responsiveness in the browser (or other IO events). Like the original author, imagine that you are sorting thousands of records and it will take 3 seconds. You don't want to hang the browser for 3 seconds because maybe they want to click the "cancel" button. So you yield every 50 ms. But if you have something like a 15 ms wait time every time you yield, your 3 second task is going to take 4 seconds... for no reason at all.

So what I want is to be able to yield, have the system check for events and if there aren't any, resume as quickly as possible. Obviously there will be some overhead. But sticking a timer on it and saying, "I'm just going to take a brief nap" really sucks.

The problem I'm noticing is that some people think that there should be no delay at all. As soon as schedule a callback, it should run. This is not desirable at all. Again, the whole point is to yield to events and then come back. I just don't want to wait longer that it is necessary to do that.

To sum up, it's not that I want to use 100% CPU all the time, but if it is impossible to do, then it is broken. Similarly, if I am using 100% of the CPU and yielding every X ms, then then events should be handled every X ms (modulus the amount of time the event handler takes).

Post reply on HN