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 …)

Quy luật Moore & Kiến trúc đa lõi – Phần 1 (Phiên bản 1.0)

Lịch sử ra đời

Chỉ chưa đầy 20 năm sau ngày Shockley cùng với các đồng nghiệp của mình tại phòng thí nghiệm Bell Labs phát minh ra chất bán dẫn, và cũng chỉ chưa đến 5 năm kể từ lúc Bob Noyce và Jack Kilby phát minh ra mạch tích hợp, ngày 19 tháng Tư năm 1965, cách đây hơn 43 năm, trong số báo kỷ niệm lần thứ 35 của tạp chí Electronic, Gordon Moore đã đưa ra một dự đoán khá nổi tiếng:

… The complexity for minimum component costs has increased at a rate of roughly a factor of two per year (see graph on next page). Certainly over the short term this rate can be expected to continue, if not to increase. Over the longer term, the rate of increase is a bit more uncertain, although there is no reason to believe it will not remain nearly constant for at least 10 years. That means by 1975, the number of components per integrated circuit for minimum cost will be 65,000. I believe that such a large circuit can be built on a single wafer…

Gordon Moore, Cramming More Components Onto Integrated Circuits, Electronic Magazine, Vol.38, Num.8, April 19, 1965. (Bản PDF)

Nói một cách nôm na, Moore dự đoán là số lượng transistor trên một chip đơn sẽ tăng gấp đôi sau mỗi năm một. Đây chỉ là một dự đoán theo kiểu ngoại suy dựa trên các số liệu cụ thể thời bấy giờ, hoàn toàn không có chứng minh gì về mặt toán học, theo như lời Moore kể lại dưới đây.

I was given the chore of predicting what would happen in silicon components in the next 10 years for the 35th anniversary edition of “Electronic Magazine”. So I looked at what we were doing in integrated circuits at that time, and we made a few circuits and gotten up to 30 circuits on the most complex chips that were out there in the laboratory, we were working on with about 60, and I looked and said gee in fact from the days of the original planar transistor, which was 1959, we had about doubled every year the amount of components we could put on a chip. So I took that first few points, up to 60 components on a chip in 1965 and blindly extrapolated for about 10 years and said okay, in 1975 we’ll have about 60 thousand components on a chip.


Excerpts from A Conversation with Gordon Moore: Moore’s Law (Bản PDF)

Sau khi Moore điều chỉnh nhận định này lần thứ hai vào năm 1975, từ một năm thành 18 tháng, hay nói một cách dễ hiểu là số lượng transistor trên một chip sẽ tăng thêm khoảng 60% cho từng năm một, thực tế phát triển của ngành công nghiệp máy tính đã chứng minh nhận định của Moore là đúng, và đưa nó trở thành một huyền thoại.

Thật ra tên gọi “quy luật Moore” chỉ được đưa ra vào năm 1970 bởi một người bạn của Moore, là giáo sư Carver Mead ở Caltech (cũng là trường Moore đã từng học và nhận bằng Tiến sĩ Hóa Lý), một nhà tiên phong trong lĩnh vực VLSI.

Nên hiểu thế nào về những phát biểu của Moore

Sau khi viết bản đầu tiên của bài này, tôi nhận thấy là có rất nhiều cách diễn dịch quy luật Moore ở cả các tài liệu trong và ngoài nước. Điều này cũng là chuyện thường xảy ra với những dự đoán đã trở thành huyền thoại, khi chúng được kể đi kể lại nhiều lần, mỗi lần khác đi một chút xíu, dẫn đến kết cuộc là nhiều khi trở thành tam sao thất bản. Đối với quy luật Moore, điều này lại càng dễ xảy ra, vì ông đã không sử dụng các công thức toán học chính xác trong bài viết, mà chỉ dùng lời văn cộng với đồ thị và tranh minh họa để đưa người đọc đến với kết luận của mình.

Để hiểu rõ hơn về những gì Moore đã viết, và quan trọng hơn là những gì ông muốn ám chỉ, tôi đã cố gắng đọc lại bài viết ban đầu. Tôi tìm thấy là, phần hay được trích dẫn nhất khi nói về qui luật Moore, như đã được ghi ở trên, nằm trong phần bài ở cuối trang 2, với tựa đề là “Costs and curves”. Sau đây là toàn bộ nội dung bằng tiếng Anh của đoạn văn đó.

Reduced cost is one of the big attractions of integrated electronics, and the cost advantage continues to increase as the technology evolves toward the production of larger and larger circuit functions on a single semiconductor substrate. For simple circuits, the cost per component is nearly inversely proportional to the number of components, the result of the equivalent piece of semiconductor in the equivalent package containing more components. But as components are added, decreased yields more than compensate for the increased complexity, tending to raise the cost per component. Thus there is a minimum cost at any given time in the evolution of the technology. At present, it is reached when 50 components are used per circuit. But the minimum is rising rapidly while the entire cost curve is falling (see graph below). If we look ahead five years, a plot of costs suggests that the minimum cost per component might be expected in circuits with about 1,000 components per circuit (providing such circuit functions can be produced in moderate quantities.) In 1970, the manufacturing cost per component can be expected to be only a tenth of the present cost.

The complexity for minimum component costs has increased at a rate of roughly a factor of two per year (see graph on next page). Certainly over the short term this rate can be expected to continue, if not to increase. Over the longer term, the rate of increase is a bit more uncertain, although there is no reason to believe it will not remain nearly constant for at least 10 years. That means by 1975, the number of components per integrated circuit for minimum cost will be 65,000.

I believe that such a large circuit can be built on a single wafer.

Phần đầu của đoạn văn trên đây cho chúng ta thấy là không phải bây giờ người ta mới quan tâm đến chuyện hạ giá thành sản phẩm trong ngành điện tử tích hợp. Từ suốt hơn 40 năm về trước cho đến ngày nay, việc làm giảm phí tổn sản xuất đã được thực hiện theo hướng ngày càng có nhiều mạch điện tử được đặt trên cùng một thiết bị bán dẫn đơn lẻ. Điều này là khả thi do, theo Moore, chi phí ứng với mỗi thành phần (điện tử) tỷ lệ nghịch với số lượng các thành phần đặt trên cùng một thiết bị. Kiểu tương quan tỷ lệ nghịch này có rất nhiều trong các ngành sản xuất, chứ không phải chỉ trong ngành điện tử, theo kiểu khi sản xuất, nếu một mặt hàng được làm ra với một khối lượng lớn, thì rất nhiều khả năng là giá thành cho mặt hàng đó sẽ giảm đi rất nhiều. Nói nôm na là cái gì khan hiếm thì tự nhiên sẽ trở nên đắt đỏ.

Phần khó hiểu trong đoạn văn đối với tôi là thế nào là “minimum cost” và “the comlexity for minimum component costs”.

Sau khi loay hoay tìm kiếm trên net, tôi biết được là trong ngành điện tử bán dẫn, nhất là đối với dân thiết kế chip như Intel, “complexity” có nghĩa là “số lượng các transistor trên mỗi chip”, còn “component cost” hàm ý “chi phí cho mỗi transistor”. Vậy phần tô màu xanh dương ở trên có thể tạm dịch sang tiếng Việt ở mức độ dễ hiểu hơn theo ý tôi là:

Số lượng các transistor trên mỗi chip cho phép dẫn đến chi phí thấp nhất ứng với từng transistor đã và đang tăng lên với mức độ gấp đôi sau từng năm một.

Nghe có vẻ dễ hiểu hơn nhiều rồi phải không các bạn. Tuy nhiên tại sao “số lượng transistor” mà nhà sản xuất có thể đặt lên từng chip lại liên quan đến “chi phí thất nhất” ứng với từng transistor, và từ đó rõ ràng ảnh hưởng đến giá thành của từng con chip trên thị trường? Rõ ràng là nếu lập luận theo kiểu tôi nói ở trên, càng đặt nhiều transistor lên một chip thì giá thành của từng transistor càng rẻ chứ có sao đâu mà phải quan tâm đến “chi phí thấp nhất”. Để trả lời cho câu hỏi này, chúng ta cần phải nhìn lại các công đoạn của quá trình thiết kế và chế tạo chip trong ngành bán dẫn.

Tôi sẽ tiếp tục chủ đề này ở phần 2 của bài.