"Go sẽ là ngôn ngữ máy chủ tương lai" - Tobias Lütke, Shopify

 Trong vài năm qua có một sự xuất hiện của ngôn ngữ lập trình mới: Go hay còn gọi là Golang. Không có gì khiến một lập trình viên phấn khích đứng ngồi không yên hơn một ngôn ngữ lập trình mới đúng không? Do đó, tôi đã bắt đầu học Go 4 tầm 5 tháng trước và bây giờ tôi sẽ nói cho bạn lý do tại sao bạn nên học ngôn ngữ lập trình mới này ngay hôm nay. 

Tôi hoàn toàn không dạy bạn cách viết câu "Hello World" trong bài viết này. Có nhiều bài báo online khác nói về nội dung này rồi. Tôi chỉ giải thích phần mềm - phần cứng máy tính và lý do chúng ta cần một ngôn ngữ mới như Go. Vì nếu như không có bất kỳ vấn đề nào thì chúng ta cũng đâu cần giải pháp, đúng không?

Các giới hạn phần cứng:

Định luật Moore đang không còn đúng. 

Nhân xử lý Pentinum 4 với tốc độ xung nhịp 3.0GHz đã được Intel giới thiệu trở lại năm 2004. Đến thời điểm hiện tại, con Mackbook Pro 2016 của tôi có tốc độ xung nhịp 2.9 GHz. Vậy gần một thập kỷ qua không có thay đổi gì nhiều trong sức mạnh của vi xử lý. Các bạn có thể xem so sánh về tăng khả năng xử lý theo thời gian ở biểu đồ sau:

Từ biểu đồ trên các bạn có thể thấy hiệu suất của một single-thread và tần số của vi xử lý vẫn ổn định không thay đổi nhiều trong gần một thập kỷ qua. Nếu bạn đang nghĩ việc thêm nhiều linh kiện bán dẫn là giải pháp thì bạn nhầm rồi. Đấy là do ở quy mô nhỏ hơn thì một số thuộc tính lượng tử bắt đầu xuất hiện (như tunneling) và bởi vì tốn nhiều chi phí hơn để đặt thêm nhiều linh kiện bán dẫn hơn, mà càng nhiều linh kiện bán dẫn thì ví của bạn sẽ càng xẹp lép thôi

Vì vậy, giải pháp cho vấn đề trên là: 

  • Các nhà sản xuất bắt đầu thêm nhiều nhân vào vi xử lý hơn nữa. Ngày nay chúng ta có các vi xử lý quad-core và octa-core. 
  • Giới thiệu hyper-threading
  • Thêm nhiều bộ nhớ sẵn (cache) vào vi  xử lý để tăng hiệu suất hoạt động.

Tuy nhiên các phương pháp trên đều có những hạn chế riêng của nó. Chúng ta không thể thêm quá nhiều cache vào vi xử lý để tăng hiệu suất hoạt động được vì cache có hạn chế về mặt vật lý: cache càng lớn thì càng chậm. Việc thêm càng nhiều nhân vào vi xử lý cũng có giá tương tự như vậy. Ngoài ra việc làm ấy không thể mở rộng ra đến vô hạn.

Nếu chúng ta không thể dựa vào các cải thiện phần cứng thì cách duy nhất là có nhiều phần mềm hiệu quả để tăng năng suất lên. Nhưng điểu đáng buồn là ngôn ngữ lập trình hiện đại lại không thực sự hiệu quả. 

"Các bộ vi xử lý hiện đại giống như những chiếc xe vui nhộn chạy bằng nitro, chúng vượt trội ở cuộc đua chặng ngắn.  Tuy nhiên các ngôn ngữ lập trình hiện đại lại giống như Monte Carlo với những con đường đầy ngã rẽ và gấp khúc" - David Ungar

Go có goroutines!! 

Như chúng ta đã thảo luận ở phía trên, các nhà sản xuất phần cứng đang thêm ngày càng nhiều nhân vào vi xử lý để tăng hiệu suất hoạt động. Tất cả các trung tâm dữ liệu đang chạy trên những bộ xử lý này và chúng ta nên trông đợi vào sự gia tăng số lượng nhân trong các năm tới. Thêm vào đó, các ứng dụng ngày nay sử dụng nhiều micro-service để duy trì kết nối cơ sở dữ liệu, xếp hàng tin nhắn và duy trì các bộ nhớ cache. Do đó phần mềm mà chúng ta phát triển và các ngôn ngữ lập trình có thể hỗ trợ đồng thời dễ dàng, chúng có thể mở rộng được với số lượng nhân tăng lên.

Tuy nhiên, hầu hết các ngôn ngữ lập trình hiện đại (như Java, Python, v...v...) đều từ môi trường single-thread của thập niên 90. Hầu hết chúng đều hỗ trợ đa luồng nhưng vấn đề thực sự lại xảy ra với việc thực hiện đồng thời, threading-locking, race condition và deadlock. Những điều này làm cho việc tạo một ứng dụng đa luồng trên các ngôn ngữ đó trở nên khó khăn. 

Ví dụ, việc tạo luồng mới trong Java khá tốn bộ nhớ. Vì mỗi luồng ngốn khoảng 1 MB heap bộ nhớ và cuối cùng nếu bạn bắt đầu tạo hàng ngàn thread , chúng sẽ gây áp lực rất lớn lên heap và sẽ bị tắt do hết bộ nhớ. Ngoài ra, nếu bạn muốn kết nối giữa hai hoặc nhiều thread thì cũng rất khó. 

Nói cách khác, Go được ra mắt năm 2009 khi các bộ xử lý đa luồng đã có sẵn. Đó là lý do tại sao Go được xây dựng với tập trung về tính đồng thời. Thay vì các luồng thì Go lại có goroutines. Chúng tiêu thụ gần 2KB bộ nhớ từ heap nên bạn có thể tạo hàng triệu goroutines bất cứ lúc nào. 
 

Cách Goroutines hoạt động

Các lợi ích khác:

  • Goroutines có các segmented stack có thể mở rộng. Điều này có nghĩa là chúng sẽ chỉ dùng nhiều bộ nhớ hơn khi cần thiết. 
  • Goroutines có thời gian khởi động nhanh hơn các thread.
  • Goroutines đi kèm với các nguyên hàm tích hợp để giao tiếp an toàn giữa chúng (các kênh).
  • Goroutines cho phép bạn tránh phải dùng đến khóa mutex khi chia sẻ cấu trúc dữ liệu
  • Ngoài ra, các con goroutines và hệ điều hành không có ánh xạ 1: 1. Một goroutine đơn có thể chạy trên nhiều luồng. Goroutines được ghép thành một số lượng nhỏ các luồng hệ điều hành.

 Tất cả các điểm trên làm cho Go trở nên rất mạnh mẽ trong việc xử lý các tác vụ đồng thời không thua kém Java, C và C ++ trong khi vẫn giữ được mã nguồn thực thi đồng thời đẹp như Erlang.

Go chạy trực tiếp trên nền tảng phần cứng

Một trong những ưu điểm đáng kể nhất của C, C++ so với những ngôn nhữ cấp cao hiện đại khác như Java/Python là hiệu suất. Bởi vì C/C++ được biên dịch và không cần trình thông dịch.

Vi xử lý hiểu được mã nhị phân. Nhìn chung, khi bạn xây dựng một ứng dụng sử dụng Java hay các ngôn ngữ JVM-based khi biên dịch dự án, nó sẽ biên dịch mã nguồn thành byte-code cho JVM hoặc các máy ảo khác chạy trên nền hệ điều hành. Trong khi thực thi, máy ảo sẽ thông dịch những mã bytecodes đó sang mã nhị phân để vi xử lý có thể hiểu được.

Mặc khác, C/C++ không thực thi trên máy ảo vì thế đã giảm đi được một bước trong quá trình thực thi và gia tăng hiệu suất đáng kể. Nó biên dịch trực tiếp mã nguồn thành mã nhị phân.

Nhưng việc giải phóng và cấp phát biến cho những ngôn ngữ như C/C++ thật sự là mệt mỏi. Trong khi hầu hết những ngôn ngữ lập trình xử lý việc cấp phát đối tượng và giải phóng sử dụng thuật toán Garbage Collector hoặc Reference Counting.

Go mang lại sự hài hòa tốt nhất cho cả 2 thế giới. Cũng giống như ngôn ngữ bậc thấp như C/C++, Go là ngôn ngữ biên dịch. Điều đó có nghĩa là hiệu suất ngang với ngôn ngữ bậc thấp. Nó cũng sử dụng garbage collection để cấp phát và giải phóng đối tượng. Vì vậy, không còn cần đến malloc() hay free() nữa. Quá ngầu đúng không?

Mã nguồn được viết bằng Go rất dễ bảo trì

Go không có cú pháp lập trình điên rồ như các ngôn ngữ khác có. Nó có cú pháp rất gọn gàng và sạch sẽ.

Các nhà thiết kế của Go tại google đã nghĩ đến điều này khi họ tạo ra ngôn ngữ. Vì google có code-base rất lớn và hàng ngàn nhà phát triển đang làm việc trên cùng một code-base đó, nên mã nguồn phải dễ hiểu đối với các nhà phát triển khác và một đoạn mã sẽ ít ảnh hưởng nhất tới một đoạn mã khác. Điều đó sẽ làm cho mã nguồn dễ dàng duy trì và dễ sửa đổi.

Go cố tình bỏ đi nhiều tính năng của các ngôn ngữ OOP (hướng đối tượng) hiện đại.

  • Không có Class. Mỗi thứ chỉ được chia thành các packages. Go chỉ có structs thay vì các classes.
  • Không hỗ trợ thừa kế (inheritance). Điều đó sẽ làm cho mã nguồn dễ dàng sửa đổi. Trong các ngôn ngữ khác như Java / Python, nếu lớp ABC kế thừa lớp XYZ và bạn thực hiện một số thay đổi trong lớp XYZ, thì điều đó có thể tạo ra một số tác dụng phụ trong các lớp khác kế thừa XYZ. Bằng cách loại bỏ tính kế thừa, Go cũng giúp bạn dễ hiểu mã nguồn hơn (vì không có super class nào khi đọc mã nguồn).
  • Không có constructors.
  • Không có annotations.
  • Không có generic.
  • Không có exceptions.

Những thay đổi ở trên làm cho Go rất khác biệt với các ngôn ngữ khác và nó làm cho lập trình trong Go cũng khác với các ngôn ngữ khác. Bạn có thể không thích một số điểm ở trên... Nhưng về mặt tích cực, nó sẽ làm cho mã nguồn của bạn sạch sẽ hơn và thêm rõ ràng hơn.

Biểu đồ trên chỉ ra rằng Go cũng hiệu quả như C/C++, trong khi vẫn giữ được cú pháp lập trình đơn giản như Ruby, Python và một số ngôn ngữ khác. Đó là mối quan hệ win-win cho cả loài người và vi xử lý.

Không giống như những ngôn ngữ khác như Swift, cú pháp của Go rất ổn định. Nó vẫn duy trì và không thay đổi kể từ khi release phiên bản 1.0 vào năm 2012. Điều đó giúp cho nó có thể tương thích ngược.

Go được Google phát triển

Tôi biết đây không phải là một lợi thế kỹ thuật trực tiếp, tuy nhiên Go được Google thiết kế và hỗ trợ. Google có một trong những cơ sở hạ tầng đám mây lớn nhất trên thế giới và nó được mở rộng quy mô rất lớn. Google thiết kế Go để giải quyết các vấn đề về hỗ trợ khả năng mở rộng hiệu quả. Đó là những vấn đề tương tự bạn sẽ gặp phải trong khi tạo máy chủ của riêng bạn.

Thêm vào đó Go cũng được sử dụng bởi một số công ty lớn như Adobe, BBC, IBM, Intel và thậm chí là Medium (https://github.com/golang/go/wiki/GoUsers)

Tổng kết:

  • Mặc dù Go rất khác với các ngôn ngữ hướng đối tượng khác, nhưng nó vẫn là một con quái thú. Go cung cấp cho bạn hiệu suất cao như C/C++, xử lý đồng thời siêu hiệu quả như Java và dễ code như Python / Perl.
  • Nếu bạn không có kế hoạch học Go, tôi vẫn sẽ nói giới hạn phần cứng sẽ gây áp lực cho chúng ta và các nhà phát triển phần mềm để viết mã nguồn siêu hiệu quả. Nhà phát triển cần hiểu rõ về phần cứng và tối ưu hóa chương trình của họ cho phù hợp. Phần mềm được tối ưu hóa có thể chạy trên phần cứng rẻ hơn và chậm hơn (như thiết bị IOT) và tác động tổng thể tốt hơn đến trải nghiệm người dùng cuối.