11 ms·
Windows 3.1 All Over Again
- annon23 9y agoyup... I think browser need to come with some type of bytecode or vm that supports concurrency so we can finally have a true app platform.
- andrewguenther 9y agoDon't you already have this with WebWorkers? This isn't a limitation of the environment per se as much as it is a limitation of the DOM.
- annon23 9y agowe should develop software using different languages as well, web workers are part of the solution
- pmontra 9y agoThe DOM wants to be accessed sequentially by a single thread, then we ended up to say single threading is good because we don't have alternatives and because we're using the same frontend technology for backend jobs (v8.) It's a kind of Stockholm syndrome. Sequential access to the DOM can be ok because we are the only user of the browser. Single processing is not so ok on the backend because there could be thousands of users there. We scale Ruby, Python and Node with multiple processes (I'm doing it.) I'm also developing an Elixir application using Phoenix. The approach is similar to writing Rails or Django code (I never sent a single message in all the application) with the convenience of not having to manage sidekiq or celery for background jobs (they're in the language) and autoscaling.
- rixed 9y agoBrowser need to come with a bytecode that would run into the local container on the local operating system running on the VM on another operating system running on top of real hardware, so we can finally have a true app platform.
- icebraining 9y agoWhat's your point, exactly? Shipping bytecode is essentially the leanest way you can achieve platform-independent code execution, no?
- rixed 9y agoMy point is that we could ship bytecode long before browser were even invented, therefore I doubt that the explosion of complexity is caused by us trying to solve that problem. This is easy to test: we just have to wait until we can ship byte code through the browser, and see how long it takes for another layer of abstraction missing some important feature to pop up on top of it.
- EdSharkey 9y agoAssuming you're not being snarky, hold on to your pants ... As a baseline, you have Asm.js[1] + Web Workers[2] today in most browser places (actually, all places, if you creatively polyfill.) For newer-fangled browsers only, you have Web Assembly[3] + Web Workers, which takes in-browser performance to a whole 'nother level. Now, the Web Assembly spec isn't stopping at replacing Asm.js. There's going to be support added for SIMD and vectors and whatever cool stuff newer processors can do. The big deal will be once Web Assembly gets the ability to do syscall-like thunks to DOM API and the other myriad JavaScript API. At that point, the browser's a full OS platform capable of hosting most applications. This new thing will then be able to go beyond any previous application platform in terms of reach and ultimately, capability. Just forget about the term "browser" when the thunks appear. This will be a new "Platform 1.0" that will span all modern operating systems and devices. Node.JS will implement a compatible "Platform 1.0" subset for server-side platforming where client and server will be able to share binary libraries. This is coming soon. If you've got some great idea for a next generation online app, it's time to start working hard and have it target "Platform 1.0"! [1] http://asmjs.org/ http://asmjs.org/ [2] https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers... [3] http://webassembly.org/ http://webassembly.org/
- solarengineer 9y agoJava Applets - run bytecode in a VM (called the JVM) in the browser, and use multi-threading supported by the JVM.
- chaostheory 9y ago> Concurrency is hard. If you want concurrency you have two choices - processes or threads, take your pick. Sometimes there's a 3rd choice: actors. imo it's much easier to manage than threads
- syrrim 9y agoActors the same as what the OP is describing as processes I believe.
- lobster_johnson 9y agoIn Erlang, actors are called "processes".
- noncoml 9y agoCan we stop with the erlang/Elixir is slow? For most web services it is as fast as go. For my last side project I was evaluating go and erlang. Just to test out how faster go is, I quickly coded a simple HTTP request in both language(I am not an expert in either). The server would get a request with large body, cache it to a file on the disk, then load it in 128k chunks, get the hash of the chunk and save both the digest and the 128k data to a database. To my surprise, erlang was consistently slightly faster for each request. Happy to share the code snips of both if anyone want to audit/scrutinize. Edit: Code for both: https://pastebin.com/aBQWqkG3 https://pastebin.com/aBQWqkG3 On my laptop a single request(1.2MB) takes around 230ms for Erlang and 280ms for Go. Not a scientific test, but it gives an idea.
- lobster_johnson 9y agoYour Go code uses "defer" in a for loop, which could be a performance bottleneck. The deferred function call doesn't run until the saveToDb function returns. A simple fix would be to reuse the rows variable and put the defer outside the loop. I believe it might, depending on the database driver, also cause another issue. Since you're not closing the rows right away, and not even consuming the rows, this may force the driver to mark the internal connection as busy until the defer runs, meaning the next database call would have to open a new connection. Try closing the rows as soon as you can. If you don't expect any results, you can also use Exec instead. Then there's nothing to close.
- noncoml 9y agoI keep the habit of deferring immediately so I don’t forget later if the fucnction gets long. But I see what you mean, on this case, inside the loop, is really bad idea. Will try with Exec and without the defer and see if it makes a noticeable difference. The driver is the MySQL one for what’s worth it. Postgres was a bit slower.
- noncoml 9y agoJust to update that you where right. Without the `defer` now golang is as fast as erlang. Exec vs Query didn't make a difference. Yet another update: things get really interesting when I run wrk. I don’t want to spoil it for you. I can let you try it ;)
- andrewguenther 9y ago> “what else am I supposed to do when I’m waiting for a result from the database”? I can’t do anything else until I have that result. I need the result to make give to a template to give to the renderer, etc. I dunno, maybe you could serve one of your 100 other concurrent requests? > You can do something useful in the meanwhile and get the result later and then immediately... > It has nothing to do with hogging or stealing resources. It has everything to do with this. If you're blocking on something waiting for a result while you could do something useful in the meantime, that is the definition of hogging resources.
- christophilus 9y agoHis point is that blocking while waiting should not be an expensive thing. In preemptive systems like BEAM, it's not. Blocking a process is not an issue. In Node/Windows 3.1, it is a big deal, as it means starving other work.
- pas 9y agoAwait works the same way as BEAM, no? In theory their performance and behavior should be the same. BEAM and other N:M green thread systems being maybe faster if they can efficiently use multiple threads. But since they use message passing anyway (needed for isolation), they are again almost equivalent to running multiple Node/reactive request handlers. N:M is probably useful for desktop apps. But for that single thread is (should be) always enough, so same behavior.
- kabes 9y agoWell, as the article points out, await is not pre-emptive, so it doesn't have the same fairness guarantees as BEAM. But IMO this argument is mostly theoretical as most web applications deal with simple request/response kind of things and have few long running tasks. Although a non pre-emptive system might be easier to DOS as soon as you find one input that takes a long time to process.
- 9y ago
- Lerc 9y agoI tend to think of it as a pre-emption issue more than a concurrency or parralellism issue. Endless loops cause hangs. Being able to pre-empt things would solve a lot of the problem. 10 print "mike is cool" 20 goto 10 simply doesn't have a good analogue in javascript. Even in Web Workers a loop like that causes the worker to be unresponsive to events. The Shared Memory addition alows a rudimentary form of this by allowing a flag to make an aware program exit. It wouldn't be too hard to have a BASIC to worker compiler that compiled the above code to while (true) { print("mike is cool"); if (shared_global_flag === true) break; } There are a few javascript livecoding websites out there that update the code as you type. There is a problem with this. Broken (as half written code tends to be) javascript doesn't just fail to work, it breaks other things for example var x=1; while (x<10) { output(x); x+=1; } Typing this into a livecoding environment breaks because the while loop goes on forever until you type the code to increment x. It breaks everything because the while loop blocks you from being able to type the code that would get you out of the situation. I don't see the wisdom of disaallowing something due to the specter of race conditions when its absence seems to cause a much easier to cause debilitating problem.
- greggman 9y agoapparently codepen runs the code through a parser and inserts exit checks in all detectsble loops. https://blog.codepen.io/2016/06/08/can-adjust-infinite-loop-protection-timing/ https://blog.codepen.io/2016/06/08/can-adjust-infinite-loop-...
- greggman 9y agoas for why no races I think because it's basically impossible (or way too non performant) to write a JavaScript engine that can handle multiple threads deleting properties on objects. You'd have to lock on every delete.
- Lerc 9y agoThat's the nature of JIT though, non-performant but working is ok for the bits that might cause trouble. JavaScript should just make fast the things it can. I'd be ok for a light pre-emtion that would happen between javascript statements. Instead of genuain preemtion have the Javascript engine include break checks. A check of a flag and conditional branch (usually not taken) is not a huge expence. The cost can be especially ameliorated if you do the check only on statements that jump to earlier statements. That fixes the problem of long running code stopping other things. It doen't need system level threads or parallelism, just a js engine doing spot checks.
- chmod775 9y agoCan we stop repeating the meme that Node.JS is not multi-threaded? True, your JS code all runs in a single thread, but all the heavy lifting is behind asynchronous interfaces, delegating the work to a threadpool in the background. What a Node.JS application really is, is a supervisor thread (your JS code) giving work for a bunch of worker threads. Doesn't sound very single threaded to me.
- Mithaldu 9y agoCan you start a thread with a JS loop in node so you can have two loops running at the same time doing CPU-heavy tasks? If not, then calling it multi-threaded is misleading to a programmer who needs such things, and instead you'll need to find a more accurate word.
- chmod775 9y agoThis will repeatedly run 100000 iterations of PKBDF2 in 10 threads: process.env.UV_THREADPOOL_SIZE = 10; function startOne() { const start = Date.now() require('crypto').pbkdf2(String(start), 'secretSalt', 100000, 512, 'sha512', (error, result) => { console.log(`${result.length}b took ${Date.now() - start}ms`); startOne(); }); } for (let i = 0; i < 10; i++) startOne(); You can use htop or another tool of your choice to confirm this is the case.
- Mithaldu 9y agoAre the threads started by way of external process, thread started within a C library, or started with an actual Node.js call i can write directly in node.js code and also use to run literally arbitrary node.js code?
- chmod775 9y agoIf I'm going to assume that by "node.js" code, you actually mean JavaScript code running within the V8 engine as a part of the whole that is Node.JS, then: The answer is no, you cannot do that using pure JS and without using (external) stuff like WebWorkers. If you are talking more generally about anything we can run within Node.JS, is using Node.JS user-facing APIs and works without doing any modifications to Node.JS, the answer is yes. You can easily achieve that with node's native module support, and I would encourage you to do so over committing the folly of doing anything CPU intensive in JS. That is if you manage to find any CPU intensive task that isn't already handled by node built-ins or some library out there. Edit: Now you may say: "Ahah! You can't use threads using [limited usage of node]. Also threads must be usable for arbitrary workloads in order to consider a thing multi-threaded. Node.js is single-threaded!" I just can't argue with that. It's just a matter of opinions now about what makes a thing/environment single-threaded, and what makes it multi-threaded. To me it's simple: [x] Thing uses multiple threads to perform work.
- spc476 9y agoThere are other concepts, like coroutines. At work, I use Lua coroutines to handle network requests. Basically, any function that can block instead starts the IO request, then yields the current coroutine. Yeah, the management code was a bit of a pain to write, but you can write (using the BASIC example from the article and not Lua, but the idea is similar): 10 A = 1 20 A = A + 1 30 PRINT "HELLO " + A 40 PRINT "YES" 50 GOTO 20 where `PRINT` will do a non-blocking write, if there's anything left over, set the IO descriptor to trigger when ready for writing, and yield(). It's in the poll loop (select(), poll(), epoll(), kevent(), whatever the API is) to schedule the proper coroutine. Yes, this requires making sure the code doesn't do anything CPU intensive, but for the application I have running, that is the case. But for me, using Lua coroutines means the main business logic is still sequential, even in an event driving environment. Also, because you call a function to preempt execution, you don't need to save the entire CPU state. For C on the x86-64 bit, this is just six registers: https://github.com/spc476/C-Coroutines/blob/314607cf058352bee922a4c0d1cfc0a37646d250/coroutine_yield-x86-64.asm#L85 https://github.com/spc476/C-Coroutines/blob/314607cf058352be...
- Animats 9y agoThe real "Windows 3.1 all over again" experience is mixing functions which block with ones that are "async". If nothing blocks for long, as with Javascript, that's fine. If everything is threaded, and threads block, that's fine too. What works badly is mixing the two. If you block something that's doing cooperative multitasking, the whole system stalls. This is still a big problem inside browsers. Python has this problem with "async". If you make a blocking call, you stall out the "async" stuff. Go gets around this by having a "fiber" or "green thread" mechanism underneath. If you block on a lock, that doesn't tie up a resource needed to run other goroutines. This is why retrofitting async to a language has problems.
- vog 9y agoNote that Python has greenlet and gevent, though. It works by monkeypatching all blocking calls in the standard library (and common external libraries such as database client libs like psycopg2).
- Animats 9y agoThat's not a standard part of the new "async" feature. It's a leftover from the Stackless spinoff of Python.
- qualitytime 9y ago" 10 A = 1 20 A = A + 1 30 PRINT_NON_BLOCKING "HELLO" + "A", when done callback { LINE 40 } 40 PRINT "YES" 50 GOTO 20 A common quiz for Javascript is to ask: which is printed first? “YES” or “HELLO3”? " Is that a mistake with "HELLO3"? Should it not be HELLO2? Or am I stupid?
- vog 9y agoPlease note that your formatting is totally screwed, making your fine comment almost unreadable. See also the "help" link next to the edit box: https://news.ycombinator.com/formatdoc https://news.ycombinator.com/formatdoc
- BillinghamJ 9y agoAuthor seems to slightly miss the point of Node. It’s designed for IO-bound work, and essentially nothing else. If you have a long-running synchronous algorithm, you should not be running it in Node, or alternatively you could dispatch to another process/C lib and have it run in a true thread and asynchronously wait for the result.
- jcelerier 9y ago> Author seems to slightly miss the point of Node. It’s designed for IO-bound work, and essentially nothing else. No remotely relevant app is only ever IO-bound. There are always some long lists to sort, some graphs to walk, some intricate canvas renderings to do.
- romanovcode 9y ago> long lists to sort, some graphs to walk, some intricate canvas renderings to do This is probably one of the worst usecases for node.js
- golergka 9y ago> No remotely relevant app is only ever IO-bound. A lot of microservices that these apps could be split to certainly are.
- ubernostrum 9y agoNo remotely relevant app Define "relevant".
- zbentley 9y agoI'm not sure about that assertion. I work on a giant [Health|Fin|Defense]Tech monolith which has been around forever, has do to everything for everyone, and has been worked on by hundreds or thousands of developers with radically different skill levels. It connects to many databases, external services, etc., and does some immensely complex data munging just to render what you'd think are simple pages (since the inflexibility of the backing model and limited space to make denormalizations of really big data mean everyone has to do super complex aggregations in the app server, across data sources X, Y, and Z, all to show the user a 10 row table). In short, it's huge, ugly, and computationally expensive. I was asked to quickly research the benefits of switching its platform (a single-threaded scripting language) to NodeJS (I wasn't told to research anything other than NodeJS, despite my objections). I figured the savings would be minimal, since all our application servers are constantly running out of CPU (page loads crazy expensive, see above) or mem (aggregations crazy expensive, see above). So I broke down what the app was doing on some representative servers, working both from the coarse level (dtrace/system resource usage) to the fine level (flame graphs of calls/wait time/yield events within the application runtime itself). I didn't profile the batch processing services; they were RPC'd to via the renderers, and used more appropriate languages/patterns for huge-data manipulations. As far as my profiling was concerned, they were functionally databases. The result? 88% on average time spent waiting on IO or blocking non-block-file system calls. P90 was 99% blocking. That went totally against my assumptions. Sure, our webservers were overloaded with non-IO load, but if we were to switch to non-blocking IO and buy more webservers, we'd have gotten a massive performance increase without having to change the fundamental architecture of our webapp. That was when I started seriously considering the benefits of reactive-style programming in a single thread, a la NodeJS; it hits a nice balance between "programmers that aren't necessarily super skilled having to engage with a full/real concurrency system" and "do everything blocking one per process". There are tons of downsides, of course. Switching to nonblocking IO after spending so long in a blocking world would require both massive technical expenditure, and would probably also require reorganizing the capacity planning of all the other services/databases the app servers talked to, since they'd be fielding a lot more requests. Basically, the blocking nature of the render loops was an informal rate limiter on database queries. Parallelizing the render loops via processes gave much more direct control of changes in resource utilization, which is nice for proactive scaling. Additionally, node/callback style is still harder to learn (even with async/await sugar) than plain 'ol top-to-bottom sequential code. All that said, we'd be rewriting code in a new platform that looked different, but the code could still do the same things, per render, in the same order, which is a huge benefit. A platform that hides preemption/concurrency while allowing people to program in the sequential style (e.g. Erlang) might have been a better fit, but . . . we were already using one of the best M:N resource schedulers in the world, the Linux process scheduler, to multiplex concurrent sequential processes that were just . . . linux processes. At the end of the day, I gained a lot of respect for the power and balance struck by single-thread/event-loop-driven reactive runtimes like Node. Edits: grammr.
- ComodoHacker 9y ago>In Elixir (thanks to Erlang) yo have true pre-emptive multi-tasking >An Erlang process is not a system process. An Erlang process is a lightweight process. But here’s a key difference: there is no way in Erlang for processes to share memory I'm confused. Is there real concurrency in Erlang? If yes, what Erlang processes use under the hood: system processes or threads? If threads, why they aren't sharing memory?
- devbug 9y agoMapped to system threads. Separate heaps to simplify garbage collection (and improve performance.)
- ramchip 9y agoThe VM has a set of threads called schedulers, all running in parallel. Each scheduler has in turn a set of Erlang processes (possibly thousands) to run. Every process has a pointer to its own separate chunk of heap where it allocates its stuff. The language has no concept of pointer, so there is no way to create a reference to memory owned by another process. You can send a message to another process, but internally the VM will copy the message to that process’ heap.
- norswap 9y agoWith all due respect, "the reasons he heard" are bogus. The only reasons there is no preemptive multithreading in Node is just that the V8 interpreter is under GIL.
- gizzlon 9y agoThis feels like Elixir propaganda =/ Starts off nicely with a good premise, but instead of digging into the interesting details it glosses over them and starts to preach Elixir. I don't have any stake in this, I use neither js or Elixir, but this article is too shallow to be really useful.
- ENGNR 9y agoYeah he stakes a bit of a stab at scala play, which as far as I can tell in the latest release uses the same concurrency mode (actors) as Erlang anyway
- AndrewCHM 9y ago"I’ve heard a few reasons:" ... that you've considered, then cherry picked two of them, so you can confirm your views? To properly reason why the approach is wrong, shouldn't you consider all significant reasons, including backwards compatibility as probably the biggest one? "New language that learns from the mistakes of languages before it" would generally be better than "language and runtime that is keeping compatibility with a programming language that was heavily rushed just to fill a feature point for a web browser" One might as well state that the grass is green and sky is blue, no?
- baybal2 9y agoThe author seem to loose the point of FP, as is the case with many web programmers Async != FP Back in my high school days, FP was almost strictly about usage of pure functions above anything else. The late comers to the party, the React guys, then bunched FP with async stuff, and function polymorphism. That was wholly apocryphal to the original FP idea. I can still cite my high school teacher citing Niklaus Wirth: "If a function does not interact with external data in a any way other than reading them from its inputs, the whole program can be represented as a spreadsheet" The original advantages were states as: 1. a program written in this manner is very easy to optimize. 2. an interpreter or a compiler executing such program is easier to program, and is less error prone 3. for as long as a kind of middleware sits in between the "spreadsheet function," and the data, it is possible to write a continuous execution log with ease, as well as to do tricks with getters/setters to start execution from arbitrary points in code. All of that is stuff from times when Oberon and Modula were "hip" (seventies)
- pjmlp 9y agoOberon was designed in 1986 and it was hip up to around 1998. :)
- z3t4 9y agoWith nodeJS using fs.readFile is basically the same as scaling across multiple machines. NodeJS kinda force you to learn how to manage concurrency, eg what people call callback/promise/future hell.
- le-mark 9y agoThis articles is a fantastic, real world overview of cooperative and preemptive multitasking. Note that coroutines and fibers are essentially equivalent because they can be implemented, each with the other[1]. Unfortunately this thread (up to now) has been simply language tribal-ism. Helps if everyone is on the same page with respect to technical terms before that all starts. [1] https://stackoverflow.com/a/3325985/2561675 https://stackoverflow.com/a/3325985/2561675