Phân biệt tác vụ I/O và tác vụ chuyên sâu CPU

Phân biệt tác vụ I/O và tác vụ chuyên sâu CPU

Tin ngắn hàng ngày dành cho bạn
  • Ui! Blog đang dính một lỗi bảo mật khá nghiêm trọng. Không biết có bạn nào phát hiện ra chưa. Dù chưa ảnh hưởng đến người dùng nhưng thông qua quá trình Eating your own dog food thì mới phát hiện ra á. Để sửa xong thì mình kể chi tiết.

    » Xem thêm
  • Ngủ dậy thấy bảng tin ngập tràn bài viết về việc Microsoft vừa viết lại trình biên dịch Typescript - tsc bằng Go, hiệu suất cho ra nhanh hơn gấp 10 lần so với cái hiện tại. Wow!

    Nhưng khi nhìn thấy tin này thì trong đầu nảy luôn ra câu hỏi "Tại sao không phải là Rust?". Bạn biết đấy, phong trào viết lại mọi thứ bằng Rust đang nóng hơn bao giờ hết, không ngoa khi nói rằng nó đang càn quét bảng xếp hạng của những công cụ kỳ cựu trước đó.

    Điều thú vị hơn nữa là viêc lựa chọn Go - mang lại hiệu suất tốt nhất cho đến thời điểm hiện tại - như họ nói. Thì rất nhiều người tỏ ra thất vọng vì tại sao không phải là C# 😆. Đấy khi bản nổi tiếng quá làm gì cũng bị xét nét từng tí một, nói chưa chắc ai cũng nghe 🥶

    Why Go? #411

    » Xem thêm
  • Mình đọc được bài viết này Migrating Off Oh-My-Zsh and other recent Yak Shavings - đại ý tác giả lâu nay dùng Oh-My-Zsh (OMZ) nhưng mà giờ đây cuộc chơi đã khác, có nhiều công cụ mạnh mẽ hơn ra đời cho nên anh nhận ra ồ, không cần đến OMZ nhiều như vậy.

    Mình cũng đã thử làm theo vì thấy trường hợp này khá giống với tình hình hiện tại. Thật ngạc nhiên khi khám phá thêm được điều mới mẻ. Hy vọng sẽ sớm lên một bài nói về cuộc di cư tiếp theo này với bạn đọc 😁

    » Xem thêm

Vấn đề

Là một lập trình viên Node.js đã bao giờ bạn nghe đến thế mạnh của Node.js là xử lý các tác vụ I/O và không đồng bộ, rằng Node.js không phải là lựa chọn tốt nhất cho các ứng dụng thiên về khả năng khai thác triệt để sức mạnh CPU? Vậy thì tác vụ I/O là gì và tại sao Node.js lại mạnh về I/O? Việc nói Node.js không thực sự tốt với các phép tính lớn là có đúng không. Bài viết ngày hôm nay tôi sẽ dành thời gian nói về vấn đề nêu trên.

Tác vụ I/O là gì?

I/O (Input/Output) đề cập đến sự tương tác của máy tính hay chương trình máy tính với ổ đĩa (disk) và mạng của hệ thống. Ví dụ các hoạt động I/O bao gồm đọc/ghi dữ liệu từ ổ đĩa, thực hiện các yêu cầu HTTP và tương tác với cơ sở dữ liệu. Chúng rất chậm so với việc truy cập bộ nhớ RAM hoặc các phép tính được thực hiện trên CPU.

V8 xử lý mã Javascript như thế nào

Chúng ta biết rằng Node.js sử dụng V8 của Chrome để thực thi mã Javascript có điều với sức mạnh của V8 cũng phải chào thua trước I/O bởi chúng không hoàn toàn phụ thuộc vào tốc độ xử lý của CPU như tốc độ đọc/ghi của ổ đĩa, tốc độ của mạng... Nếu I/O được xử lý trên V8 nó sẽ gây ra một cuộc tắc nghẽn nghiêm trọng vì mất quá nhiều thời gian, chính vì thế Node.js phải tìm ra một giải pháp để tận dụng được sức mạnh của V8 mà vẫn phải xử lý được I/O.

Giải pháp của Node là lựa chọn libuv cho xử lý I/O không đồng bộ. Đây là thư viện C đa nền tảng cung cấp hỗ trợ cho I/O không đồng bộ dựa trên các vòng lặp sự kiện (Event Loop).

V8 + libuv Node.js

Luồng chính gặp các tác vụ I/O sẽ đẩy chúng xuống libuv, kết quả được đưa trở lại cho luồng chính thông qua Event Loop. Cứ như thế tạo nên một vòng lặp vô hạn mà luồng chính không bị chặn bởi I/O nữa.

Trong Node.js có khái niệm worker, chúng có nhiệm vụ chuyển các yêu cầu I/O từ luồng chính đến libuv và chờ nhận phải hồi, trong thời gian đó chúng không phải làm gì khác và có thể bị hệ điều hành huỷ lập lịch (de-scheduled) để cho một worker khác gửi yêu cầu. Do đó các tác vụ I/O được worker chuyển vào từ trước vẫn được xử lý ngay cả khi luồng liên kết không chạy.

Hệ điều hành đã tối ưu các công cụ quản lý tệp và cơ sở dữ liệu cũng được tối ưu hoá cao để xử lý đồng thời nhiều yêu cầu chờ xử lý. Ví dụ như sắp xếp lại thứ tự ưu tiên khi có đồng thời nhiều yêu cầu đọc/ghi dữ liệu vào một tệp.

Khi chạy một ứng dụng Node.js bạn sẽ có một số thread pools chuyên dùng để xử lý các yêu cầu I/O. Nhóm luồng này được tạo bởi libuv. Số lượng mặc định của nó là 4 nhưng có thể tăng lên tối đa 128 thông qua biến môi trường UV_THREADPOOL_SIZE.

cấu trúc libuv

Tác vụ chuyên sâu CPU

Là những công việc đòi hỏi nhiều về khả năng tính toán của CPU. Đó có thể là những phép tính phức tạp về mã hoá/giải mã, xử lý hình ảnh, xử lý video... Các worker cũng có thể chuyển những yêu cầu tính toán phức tạp này, lập lịch và xử lý chúng bên ngoài luồng chính. Nhưng chúng chỉ được xử lý khi worker được lập lịch trên một trong các lõi của CPU. Ví dụ nếu CPU của bạn có 4 lõi và bạn tạo ra 5 worker thì một trong những worker này không được tham gia xử lý, trong khi vẫn phải duy trì một nguồn tài nguyên cho nó (bộ nhớ & lập lịch) gây ra tình trạng lãng phí tài nguyên.

mô hình worker thread

Để hiểu rõ hơn về cách Node.js xử lý các task tính toán nặng thông qua Worker Threads tôi khuyên bạn nên đọc bài Worker threads là gì? Bạn đã biết khi nào thì sử dụng Worker threads trong node.js chưa?.

Có thể thấy nếu như luồng chính đưa các tác vụ I/O ra libuv, nó sẽ ngăn chặn được một cuộc tắc nghẽn nghiêm trọng. Trong khi đó, các tác vụ thiên về CPU suy cho cùng sẽ chiếm thời gian xử lý. Giải pháp là tạo ra các child process hoặc worker threads nhưng những giải pháp này còn phụ thuộc vào sức mạnh của phần cứng.

Tổng kết

Với mô hình kiến trúc của Node.js được thiết kế để tận dụng sức mạnh của V8 mà vẫn giải quyết được I/O thế nên Node.js hoàn toàn phù hợp với những bài toán thiên về I/O. Thế nhưng không phải là Node.js không xử lý được những bài toán nặng về tính toán. Bằng cách tạo ra những child process hoặc worker threads trên CPU đa lõi hoàn toàn có thể xử lý mà không chặn luồng chính.

Cao cấp
Hello

Tôi & khao khát "chơi chữ"

Bạn đã thử viết? Và rồi thất bại hoặc chưa ưng ý? Tại 2coffee.dev chúng tôi đã có quãng thời gian chật vật với công việc viết. Đừng nản chí, vì giờ đây chúng tôi đã có cách giúp bạn. Hãy bấm vào để trở thành hội viên ngay!

Bạn đã thử viết? Và rồi thất bại hoặc chưa ưng ý? Tại 2coffee.dev chúng tôi đã có quãng thời gian chật vật với công việc viết. Đừng nản chí, vì giờ đây chúng tôi đã có cách giúp bạn. Hãy bấm vào để trở thành hội viên ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (1)

Nội dung bình luận...
Avatar
Đăng Khoa2 năm trước
Hoá ra v8 để chạy mã js và nó một luồng đúng ko ạ
Trả lời
Avatar
Thành Đỗ2 năm trước
Đúng rồi bác