Mô hình lập trình CUDA – Phần 1: Tổng quan về CUDA (Phiên bản 1.0)

Bắt đầu từ hôm nay, tôi sẽ cố gắng viết một loạt bài về lập trình CUDA, công cụ gắn bó với công việc hiện tại của mình. Đây cũng là cách để tôi hệ thống hóa lại những kiến thức, kinh nghiệm mà mình đã thu thập được kể từ lúc làm quen với CUDA đến nay. Hy vọng loạt bài này sẽ khơi dậy niềm yêu thích thử nghiệm và ứng dụng lập trình song song trong cộng đồng những người đã, đang, và sẽ là lập trình viên chuyên nghiệp trong một ngày không xa.

Câu hỏi đầu tiên được đặt ra là liệu có đáng bỏ công bỏ sức để học và sử dụng CUDA hay không?

Để trả lời câu hỏi này, trước hết chúng ta cần nhìn nhận một hiện thực đang diễn ra là, các CPU đa lõi (multicore) và các GPU nhiều lõi (manycore) ngày càng có mặt nhiều hơn ở khắp nơi, từ máy tính ở cơ quan cho đến trường học, rồi cả máy tính ở nhà riêng của mọi người. Ở đây, khi đề cập đến từ “đa lõi“, chúng ta ám chỉ các CPU/GPU có từ 2 đến 8 nhân, còn nói “nhiều lõi” có nghĩa là từ 8 nhân trở lên. Vậy điểm chung của những hệ thống đa nhân – có từ 2 lõi trở lên này là gì? Đó chính là khả năng xử lý song song thật sự nhiều tác vụ, chứ không phải giả lập song song như cách trước đây vẫn được sử dụng trên Windows hay Linux thông qua môi trường đồng hành đa nhiệm. Nói cách khác, đó là các hệ thống xử lý song song thật sự.

Sự tràn ngập của các hệ thống song song này dẫn đến một yêu cầu hết sức cấp bách và đầy thách thức đối với cộng đồng phát triển phần mềm, đó là làm thế nào tận dụng được sức mạnh song song của chúng đối với những phần mềm ứng dụng đang và sẽ được phát triển trong một vài năm nữa. Mục tiêu chúng ta đặt ra ở đây là, với các CPU/GPU càng có nhiều nhân, phần mềm sẽ càng chạy nhanh hơn, theo kiểu đã đạt được trong thế giới đồ họa, khi các trò chơi hiện đại được chơi trên máy tính có card đồ họa mạnh hơn, sẽ chạy nhanh hơn, sống động hơn, lý thú hơn.

Đọc đến đây chắc chắn có bạn sẽ đặt lại câu hỏi cho tôi là, vậy chứ không phải từ trước đến giờ, muốn phần mềm trên máy chạy nhanh hơn, mình chỉ cần đầu tư phần cứng ngon lành là được hay sao?

Vấn đề ở chỗ là nếu trước đây vài năm, làm như vậy hoàn toàn không có vấn đề gì để càm ràm hết. Nhưng vài năm trở lại đây, do nhiều lý do kỹ thuật liên quan đến các giới hạn vật lý, các đại gia sản xuất CPU không còn có thể tăng tốc độ chip đơn ngày một cao hơn như trước, mà chuyển sang mô hình nhiều chip (nhân, lõi) với tốc độ vừa phải, dễ sản xuất, giá thành chấp nhận được. Như vậy, đa phần máy tính mới giờ đây có thể coi như có nhiều “CPU” nhỏ bên trong. Tuy nhiên, đa phần các phần mềm hiện có đều vẫn chỉ được thiết kế để chạy trên một CPU đơn lẻ, theo đúng mô hình máy Turing và một trong những cách cài đặt phổ biến của nó là kiến trúc xử lý tuần tự von Neumann. Điều này có nghĩa là người dùng trả tiền mua về một cái máy mới với nhiều cỗ máy con chạy bên trong, nhưng gần như chỉ có thể dùng được một cái mà thôi!!

Ở hội nghị MVP năm ngoái của Microsoft (MVP là chữ viết tắt của Most Valuable Professional, danh hiệu Microsoft giành tặng cho những người có kiến thức sâu sắc về các phần mềm chạy trên hệ điều hành Windows, kể cả bản thân Windows), với bài nói chuyện của mình, Bill Gate đã nêu lên một số điểm mấu chốt có liên quan đến lập trình song song trên các máy tính đa lõi. Tôi trích dẫn lại các ý đó dưới đây.

And the incredible thing is that even after 30 years, the opportunity for what software can do is far greater today than it’s ever been. After all, we have the growing momentum behind the Internet and the content and programming models that exist out there. We’ve got the continued innovation of the hardware industry. That’s often exemplified by the increase in transistors predicted by Moore’s Law. No end in sight in terms of the doubling of power of the processor; in fact, now instead of giving us higher clock speed, they’re giving us more processors. And that’s an interesting challenge because the ability to take multiple processors and use them in parallel has been a programming challenge going back many, many decades, so now it’s important that we actually solve that problem, and make it possible for developers of all types to take advantage of these multi-core devices.

If we look at a typical desktop machine, today it’s already got two processors, but if we look out even five years, it will be more like 16 or 32 processors, and even more at the level of the server.

The speed of the machines, as I said, is going to depend on this parallel programming. And so the operating system, as it has over the years, will take on higher level tasks. And so that as applications are calling the operating system, the sophistication of doing this parallel programming will be handled in the operating system itself. And so we’ll take the graphics layer and move it up to a much higher level of API. We’ll even take things like some of the physics capabilities and make those available in a standard runtime in the operating system, high level visualization, 3-D capabilities, those things built-in.

We’ll even have capabilities around database, not just your classic disk type database, but also an in-memory database where you’re manipulating XML-type data structures in a very rich way. And so often the things that applications had to do themselves, they’ll be able to turn over and let us do in a very parallel fashion using rich runtime libraries that we create.

Bây giờ thì chắc các bạn đồng ý với tôi là cần phải học lập trình song song rồi chứ.

Rõ ràng lập trình song song không phải là chuyện dễ. Tuy nhiên, như các bạn sẽ thấy qua loạt bài này, lập trình song song với CUDA rất dễ học, với một số ít các khái niệm trừu tượng dễ hiểu. Chính vì vậy mà chỉ sau khi NVIDIA cho ra đời CUDA từ năm 2007 đến nay, lập trình viên trên khắp thế giới đã nhanh chóng phát triển các ứng dụng song song trong rất nhiều lĩnh vực khác nhau, từ điện toán hóa học đến sắp xếp, tìm kiếm, rồi mô phỏng các mô hình vật lý, hay chẩn đoán y khoa, thăm dò dầu khí, v.v… Những ứng dụng này có một đặc thù gọi là “có thể mở rộng quy mô thực thi một cách trong suốt” (tiếng Anh gọi là “scalable transparently“), theo nghĩa sẽ phát huy hiệu năng một cách phù hợp trên các GPU từ 8 lõi (thấp nhất) đến hàng trăm lõi, mà không cần phải viết lại chương trình gì cả (hay nói cách khác là trong suốt với số lượng lõi có trong GPU trên môi trường thực thi cụ thể). Điều này thực hiện được nhờ sự phối hợp nhịp nhàng của 2 yếu tố phần cứng và phần mềm trong công nghệ CUDA, đó là:

  • Mô hình lập trình song song CUDA
  • Kiến trúc phần cứng Tesla

Theo kinh nghiệm của riêng tôi, phần khó nuốt nhất trong việc tự học lập trình CUDA có 3 điểm then chốt sau đây:

  • Thay đổi tư duy thuật toán từ kiểu tuần tự bước một lâu nay ta vẫn dùng, sang kiểu song song.
  • Hiểu được cách mô hình hóa bài toán theo kiểu CUDA.
  • Hiểu được kiến trúc phần cứng Tesla và mối liên hệ của nó với việc cài đặt bài toán kiểu CUDA.

Điểm thứ nhất đòi hỏi phải có thời gian và sẽ được tích lũy dần qua kinh nghiệm thực tế, không thể đạt được trong một thời gian ngắn. Do vậy, điểm thứ hai và thứ ba cần được ưu tiên nhiều hơn. Tuy nhiên, đối với dân làm phần mềm thì nghe nói đến kiến trúc phần cứng là lùng bùng lỗ tai rồi, nên có lẻ nó sẽ là con đường đau khổ nhất cần vượt qua.

Tôi sẽ không trình bày kiểu lý thuyết về mô hình lập trình CUDA và kiến trúc phần cứng Tesla. Bạn nào thích kiểu trình bày hàn lâm như vậy, xin mời đọc các bài báo tôi đã có giới thiệu. Cách tiếp cận trong giảng giải về lập trình CUDA của tôi ở đây là đi từ các ví dụ hết sức cụ thể mà bất kỳ người nào đã có kinh nghiệm lập trình C/Pascal/Java đều có thể hiểu được. Dọc đường đi, khi nào cần thiết tôi sẽ giảng giải chi tiết các khía cạnh phần mềm và phần cứng có liên quan, như vậy tôi nghĩ mọi người sẽ dễ nắm bắt và tiến hành thực hành được ngay.

Trong phần 2 tôi sẽ trình bày tiếp về một khung lập trình kiểu mẫu cơ bản và hữu hiệu với CUDA, qua bài toán “Cộng 2 mảng A và B” một cách song song. Các bạn thử nghĩ xem nếu chúng ta có nhiều “CPU” con trong máy, thì về mặt ý tưởng nên cài đặt phép cộng này song song như thế nào?

(Tiếp tục cập nhật tối nay …)