The Difference Between I/O Tasks and CPU-Intensive Tasks

The Difference Between I/O Tasks and CPU-Intensive Tasks

Daily short news for you
  • Previously, there was a mention of openai/codex - a type of agent that runs conveniently in the Terminal from OpenAI, especially since it is open source and they have now added support for other providers instead of just using the chatgpt model as before.

    Recently, Anthropic also introduced Claude Code which is quite similar to Codex, except it is not open source and you are required to use their API. Since I don't have money to experiment, I've only heard that people in the programming community praise it a lot, and it might even be better than Cursor. On the flip side, there's the risk of burning a hole in your wallet at any moment 😨

    » Read more
  • For a long time, I have been thinking about how to increase brand presence, as well as users for the blog. After much contemplation, it seems the only way is to share on social media or hope they seek it out, until...

    Wearing this shirt means no more worries about traffic jams, the more crowded it gets, the more fun it is because hundreds of eyes are watching 🤓

    (It really works, you know 🤭)

    » Read more
  • A cycle of developing many projects is quite interesting. Summarized in 3 steps: See something complex -> Simplify it -> Add features until it becomes complex again... -> Back to a new loop.

    Why is that? Let me give you 2 examples to illustrate.

    Markdown was created with the aim of producing a plain text format that is "easy to write, easy to read, and easy to convert into something like HTML." At that time, no one had the patience to sit and write while also adding formatting for how the text displayed on the web. Yet now, people are "stuffing" or creating variations based on markdown to add so many new formats that… they can’t even remember all the syntax.

    React is also an example. Since the time of PHP, there has been a desire to create something that clearly separates the user interface from the core logic processing of applications into two distinct parts for better readability and writing. The result is that UI/UX libraries have developed very robustly, providing excellent user interaction, while the application logic resides on a separate server. The duo of Front-end and Back-end emerged from this, with the indispensable REST API waiter. Yet now, React doesn’t look much different from PHP, leading to Vue, Svelte... all converging back to a single point.

    However, the loop is not bad; on the contrary, this loop is more about evolution than "regression." Sometimes, it creates something good from something old, and people rely on that goodness to continue the loop. In other words, it’s about distilling the essence little by little 😁

    » Read more

The Issue

As a Node.js developer, have you ever heard of its strength in handling I/O and asynchronous tasks? That Node.js is not the best choice for applications that heavily utilize CPU power? So what is an I/O task and why is Node.js strong in I/O? Is it true that Node.js is not really good at large calculations? Let's explore together in today's article!

What is an I/O Task?

I/O (Input/Output) refers to the interaction of a computer or computer program with the system's disk and network. Examples of I/O activities include reading/writing data from disk, making HTTP requests, and interacting with databases. These are much slower compared to accessing RAM or performing calculations on the CPU.

How V8 handles JavaScript code

We know that Node.js uses Chrome's V8 to execute JavaScript code, but the power of V8 must also concede to I/O since they do not entirely depend on CPU speed. Activities such as reading/writing to disk, accessing random access memory (RAM), and network speed... if processed on V8 will cause severe congestion due to excessive waiting time. Therefore, Node.js must find a way to leverage V8's power while still handling I/O.

Node's solution is to use libuv to handle asynchronous I/O. This is a cross-platform C library that supports asynchronous I/O based on the Event Loop.

V8 + libuv Node.js

Speaking of the processing process. The main thread, when encountering I/O tasks, offloads them to libuv; once processed, the results are returned to the main thread through the Event Loop. Thus, the main thread does not have to wait for any I/O but only processes the results of the I/O.

Node.js has the concept of workers, which are tasked with transferring I/O requests from the main thread to libuv. During this time, they do not have to do anything else and can be de-scheduled by the operating system to allow another worker to send a request. Therefore, I/O tasks transferred earlier by workers are still processed even when the event loop is not running.

The operating system has optimized file management tools, and databases are also highly optimized to handle multiple pending requests concurrently. For example, prioritizing requests when there are multiple simultaneous read/write requests to a file.

When running a Node.js application, you will have a number of thread pools dedicated to handling I/O requests. This thread group is created by libuv. The default number is 4 but can be increased up to 128 through the environment variable UV_THREADPOOL_SIZE.

libuv structure

CPU-Bound Tasks

These are jobs that require high computational capability from the CPU. They can include complex calculations for encoding/decoding, image processing, video processing... Workers can also transfer these complex computational requests, schedule them, and handle them outside the main thread. However, they can only be processed when a worker is scheduled on one of the CPU cores. For example, if your CPU has 4 cores and you create 5 workers, one of those workers will not be processed while still maintaining a resource allocation for it (memory & scheduling), leading to resource waste.

worker thread model

To better understand how Node.js handles heavy computational tasks through Worker Threads, I recommend reading the article What are Worker Threads? Do you know when to use Worker Threads in Node.js?.

It can be seen that while the main thread offloads I/O tasks to libuv, it prevents severe congestion. In contrast, CPU-bound tasks ultimately consume processing time. The solution is to create child processes or worker threads, but how many to create depends on hardware capabilities.

Conclusion

The architecture model of Node.js is designed to leverage the power of V8 while still addressing I/O tasks. Node.js is perfectly suitable for I/O-centric problems. However, it does not mean that Node.js cannot handle problems centered around CPU power. To prevent blocking the main thread, create child processes or worker threads to handle them in a separate thread.

Premium
Hello

The secret stack of Blog

As a developer, are you curious about the technology secrets or the technical debts of this blog? All secrets will be revealed in the article below. What are you waiting for, click now!

As a developer, are you curious about the technology secrets or the technical debts of this blog? All secrets will be revealed in the article below. What are you waiting for, click now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.

Comments (1)

Leave a comment...
Avatar
Đăng Khoa2 years ago

Hoá ra v8 để chạy mã js và nó một luồng đúng ko ạ

Reply
Avatar
Thành Đỗ2 years ago

Đúng rồi bác