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
  • Today, I accidentally came across the website notes.andymatuschak.org which has a very interesting note-taking method. Clicking on a link opens a new tab next to it. Each time you click, it continues to open. Just like filing cabinets.

    This presentation style is not only easy to follow but also aligns with the flow of thought. It's a pity that I can't find the author of this open-source project. I wonder if there's anything similar out there 🤔

    » Read more
  • Instagram has just introduced a video editing app for content creators called Edits. This is a direct competition with popular apps on the market. However, it might take some time because looking at the features, they are still quite basic, just enough to use.

    By the way, I've been "playing" on IG for over 10 years now. Anyone with IG, please leave your account for me to check out 🥳.

    My IG is hoaitx_

    » Read more
  • I signed up for Cursor for the second month now and just realized I haven't used all 500 premium requests per month 😳

    Specifically, just over 100 🙏

    » 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

Me & the desire to "play with words"

Have you tried writing? And then failed or not satisfied? At 2coffee.dev we have had a hard time with writing. Don't be discouraged, because now we have a way to help you. Click to become a member now!

Have you tried writing? And then failed or not satisfied? At 2coffee.dev we have had a hard time with writing. Don't be discouraged, because now we have a way to help you. Click to become a member 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