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
  • I just found a quite interesting website talking about the memorable milestones in the history of the global Internet: Internet Artifacts

    Just from 1977 - when the Internet was still in the lab - look how much the Internet has developed now 🫣

    » Read more
  • Just thinking that a server "hiding" behind Cloudflare is safe, but that’s not necessarily true; nothing is absolutely safe in this Internet world. I invite you to read the article CloudFlair: Bypassing Cloudflare using Internet-wide scan data to see how the author discovered the IP address of the server that used Cloudflare.

    It's quite impressive, really; no matter what, there will always be those who strive for security and, conversely, those who specialize in exploiting vulnerabilities and... blogging 🤓

    » Read more
  • A new logo, of course, and I have to thank Threads a lot for the inspiration drawn from it.

    It's still a coffee bean, but with a stylized, simple yet modern, smooth design, symbolizing my development journey and that of the readers, let's call it "smooth as silk" 🎉

    » 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

5 profound lessons

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? 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