Top 10 Best Practices cho tối ưu hóa Gas trong Hợp đồng thông minh Ethereum

Trung cấp1/3/2025, 11:43:43 AM
Bài viết này khám phá vấn đề phí Gas trên Ethereum mainnet và phương pháp tối ưu hóa. Nó tập trung vào cơ chế Gas của EVM, các khái niệm cốt lõi để tối ưu hóa chi phí Gas và các phương pháp hay nhất để phát triển hợp đồng thông minh. Điều này bao gồm giảm sử dụng bộ nhớ, đóng gói biến, tối ưu hóa kiểu dữ liệu và sử dụng biến có kích thước cố định.

Bằng cách tuân thủ những phương pháp này, các nhà phát triển có thể giảm tiêu thụ Gas trong các hợp đồng thông minh, giảm chi phí giao dịch và tạo ra các ứng dụng hiệu quả và thân thiện với người dùng hơn.

Phí gas trên Ethereum mainnet luôn là một vấn đề lớn, đặc biệt trong các giai đoạn tắc nghẽn mạng. Trong thời điểm cao điểm, người dùng thường phải trả mức phí giao dịch cực kỳ cao. Do đó, tối ưu hóa chi phí Gas trong giai đoạn phát triển hợp đồng thông minh là rất quan trọng. Tối ưu hóa Gas không chỉ giúp giảm chi phí giao dịch một cách hiệu quả mà còn cải thiện hiệu suất giao dịch, mang lại cho người dùng trải nghiệm blockchain tiết kiệm và hiệu quả hơn.

Bài viết này sẽ trình bày cơ chế phí Gas của Máy Ảo Ethereum (EVM), các khái niệm cốt lõi liên quan đến tối ưu hóa phí Gas, và các thực tiễn tốt nhất để tối ưu hóa phí Gas khi phát triển các hợp đồng thông minh. Hi vọng rằng nội dung này sẽ truyền cảm hứng và hỗ trợ các nhà phát triển, đồng thời giúp người dùng thông thường hiểu rõ hơn về cách thức hoạt động của hệ thống phí Gas EVM, từ đó cùng nhau đối mặt với những thách thức trong hệ sinh thái blockchain.

Tổng quan về cơ chế phí Gas của EVM

Trong các mạng tương thích với EVM, “Gas” đề cập đến đơn vị được sử dụng để đo lường sức mạnh tính toán cần thiết để thực hiện các hoạt động cụ thể.

Sơ đồ dưới đây minh họa cấu trúc của EVM. Trong sơ đồ, việc tiêu thụ Gas được chia thành ba phần: thực thi hoạt động, gọi tin nhắn ngoại vi, và đọc/ghi bộ nhớ và lưu trữ.

Nguồn: Trang web chính thức của Ethereum[1]

Kể từ khi kích hoạt EIP-1559 (London Hard Fork), phí Gas được tính bằng công thức sau:

Phí gas = số đơn vị gas sử dụng * (phí cơ sở + phí ưu tiên)

Phí cơ bản được đốt cháy, trong khi phí ưu tiên đóng vai trò khuyến khích người xác thực đưa giao dịch vào blockchain. Đặt phí ưu tiên cao hơn khi gửi giao dịch làm tăng khả năng giao dịch được đưa vào khối tiếp theo. Điều này tương tự như một “mẹo” được trả bởi người dùng cho các trình xác thực.

1. Hiểu về Tối ưu Gas trong EVM

Khi biên soạn một hợp đồng thông minh với Solidity, hợp đồng được chuyển đổi thành một loạt các “mã hoạt động,” hoặc mã opcode.

Mỗi mã opcode (như tạo hợp đồng, thực hiện cuộc gọi tin nhắn, truy cập lưu trữ tài khoản, và thực hiện các hoạt động trên máy ảo) đều có chi phí tiêu thụ Gas kèm theo, được ghi chép trong Ethereum Yellow Paper[2].

Sau nhiều sửa đổi EIP, chi phí Gas của một số mã opcode đã được điều chỉnh, có thể khác với các giá trị trong Yellow Paper. Để biết thông tin chi tiết về chi phí opcode mới nhất, vui lòng tham khảo nguồn này [3].

2. Khái niệm cơ bản về tối ưu hóa gas

Khái niệm cốt lõi của việc tối ưu hóa Gas là ưu tiên các hoạt động tiết kiệm chi phí trên blockchain EVM và tránh các hoạt động gây ra chi phí Gas cao.

Trong EVM, các hoạt động sau đây có chi phí tương đối thấp:

  • Đọc và ghi biến nhớ
  • [ ] Đọc các biến hằng và không thay đổi
  • [ ] Đọc và viết các biến cục bộ
  • [ ] Đọc các biến calldata, chẳng hạn như mảng calldata và cấu trúc calldata
  • [ ] Cuộc gọi hàm nội bộ

Các hoạt động có chi phí cao bao gồm:

  • [ ] Đọc và ghi các biến trạng thái được lưu trữ trong lưu trữ hợp đồng
  • [ ] Cuộc gọi hàm bên ngoài
  • [ ] Hoạt động vòng lặp

Tối Ưu Hóa Phí Gas EVM Thực Hành Tốt Nhất

Dựa trên các khái niệm cơ bản trên, chúng tôi đã biên soạn một danh sách các thực tiễn tối ưu hóa phí Gas cho cộng đồng nhà phát triển. Bằng cách tuân thủ các thực tiễn này, các nhà phát triển có thể giảm tiêu thụ Gas của hợp đồng thông minh, giảm chi phí giao dịch và tạo ra các ứng dụng hiệu quả và thân thiện với người dùng hơn.

1. Giảm thiểu việc sử dụng bộ nhớ

Trong Solidity, Lưu trữ là một tài nguyên hạn chế và Tiêu thụ Gas của nó cao hơn đáng kể so với Bộ nhớ. Mỗi khi một hợp đồng thông minh đọc từ hoặc ghi vào lưu trữ, nó phải chịu một chi phí Gas cao.

Theo định nghĩa trong Ethereum Yellow Paper, chi phí của các hoạt động lưu trữ cao hơn 100 lần so với hoạt động bộ nhớ. Ví dụ, các mã opcode như sload và sstore tốn ít nhất 100 đơn vị Gas trong trường hợp tốt nhất, trong khi các hoạt động bộ nhớ như mload và mstore chỉ tiêu thụ 3 đơn vị Gas.

Các phương pháp để giới hạn việc sử dụng lưu trữ bao gồm:

  • [ ] Lưu trữ dữ liệu không cố định trong bộ nhớ
  • [ ] Giảm số lần sửa đổi lưu trữ: Bằng cách lưu kết quả trung gian trong bộ nhớ, và chỉ gán kết quả cuối cùng cho biến lưu trữ sau khi tất cả các phép tính được hoàn thành.

2. Đóng gói biến số

Số lượng khe lưu trữ được sử dụng trong một hợp đồng thông minh và cách mà các nhà phát triển biểu diễn dữ liệu có thể ảnh hưởng đáng kể đến việc tiêu thụ Gas.

Bộ biên dịch Solidity đóng gói các biến lưu trữ liên tiếp trong quá trình biên dịch, sử dụng các khe lưu trữ 32 byte như đơn vị cơ bản để lưu trữ biến. Đóng gói biến đề cập đến việc sắp xếp các biến theo cách cho phép nhiều biến vừa với một khe lưu trữ duy nhất.

Bên trái là một cài đặt ít hiệu quả hơn, tiêu thụ 3 khe lưu trữ; bên phải là một cài đặt hiệu quả hơn.

Bằng cách thực hiện điều chỉnh này, các nhà phát triển có thể tiết kiệm 20.000 đơn vị Gas (vì lưu trữ một khe lưu trữ không được sử dụng tốn 20.000 Gas), nhưng hiện chỉ cần hai khe lưu trữ.

Vì mỗi khe lưu trữ tiêu thụ Gas, việc đóng gói biến tối ưu hóa việc sử dụng Gas bằng cách giảm số lượng khe lưu trữ yêu cầu.

3. Tối ưu hóa kiểu dữ liệu

Một biến có thể được biểu diễn bằng các loại dữ liệu khác nhau, nhưng chi phí thực hiện thao tác khác nhau tùy thuộc vào loại dữ liệu. Lựa chọn loại dữ liệu phù hợp giúp tối ưu hóa việc sử dụng Gas.

Ví dụ, trong Solidity, số nguyên có thể được chia thành các kích thước khác nhau: uint8, uint16, uint32, v.v. Khi EVM hoạt động theo đơn vị 256 bit, việc sử dụng uint8 có nghĩa là EVM phải chuyển đổi nó thành uint256 trước, và việc chuyển đổi này sẽ tạo thêm chi phí Gas.

Chúng ta có thể so sánh Chi phí Gas của uint8 và uint256 bằng cách sử dụng mã trong sơ đồ. Hàm UseUint() tiêu thụ 120.382 đơn vị Gas, trong khi hàm UseUInt8() tiêu thụ 166.111 đơn vị Gas.

Một mình, việc sử dụng uint256 rẻ hơn uint8. Tuy nhiên, nếu chúng ta áp dụng tối ưu hóa đóng gói biến được đề xuất trước đó, điều đó làm nên sự khác biệt. Nếu các nhà phát triển có thể đóng gói bốn biến uint8 vào một khe cắm lưu trữ duy nhất, tổng chi phí duyệt qua chúng sẽ thấp hơn việc sử dụng bốn biến uint256. Trong trường hợp này, hợp đồng thông minh có thể đọc và ghi khe cắm lưu trữ một lần và tải tất cả bốn biến uint8 vào bộ nhớ/lưu trữ trong một hoạt động duy nhất.

4. Sử dụng Biến Kích thước Cố định Thay vì Biến Động

Nếu dữ liệu có thể bị ràng buộc trong 32 byte, nên sử dụng loại dữ liệu bytes32 thay vì bytes hoặc chuỗi. Thông thường, biến có kích thước cố định tiêu tốn ít Gas hơn các biến có kích thước biến đổi. Nếu độ dài byte có thể bị giới hạn, hãy cố gắng chọn độ dài nhỏ nhất từ bytes1 đến bytes32.

5. Phân biệt Mapping và Array

Trong Solidity, danh sách dữ liệu có thể được biểu diễn bằng hai loại dữ liệu: Mảng và Ánh xạ, mỗi loại có cú pháp và cấu trúc riêng biệt.

Trường hợp chung, bản đồ ánh xạ hiệu quả hơn và tiết kiệm chi phí hơn so với mảng, trong khi mảng có thể được lặp lại và hỗ trợ đóng gói kiểu dữ liệu. Do đó, nên ưu tiên sử dụng bản đồ ánh xạ khi quản lý danh sách dữ liệu, trừ khi yêu cầu lặp lại hoặc tiêu thụ Gas có thể được tối ưu hóa thông qua đóng gói kiểu dữ liệu.

6. Sử dụng calldata thay vì bộ nhớ

Biến được khai báo trong tham số của hàm có thể được lưu trữ trong calldata hoặc memory. Sự khác biệt chính là memory có thể được sửa đổi bởi hàm, trong khi calldata là không thể thay đổi.

Hãy ghi nhớ nguyên tắc này: nếu các tham số của hàm chỉ đọc, hãy ưu tiên sử dụng calldata thay vì memory. Điều này tránh các thao tác sao chép không cần thiết từ calldata của hàm vào bộ nhớ.

Ví dụ 1: Sử dụng bộ nhớ

Khi sử dụng từ khóa memory, các giá trị của mảng được sao chép từ encoded calldata vào bộ nhớ trong quá trình giải mã ABI. Chi phí thực thi của khối mã này là 3.694 đơn vị gas.

Ví dụ 2: Sử dụng calldata

Khi đọc các giá trị trực tiếp từ calldata, hoạt động bộ nhớ trung gian sẽ bị bỏ qua. Tối ưu hóa này giảm chi phí thực thi chỉ còn 2.413 đơn vị Gas, dẫn đến cải tiến hiệu suất Gas 35%.

7. Sử dụng từ khóa Constant/Immutable khi có thể

Các biến Constant/Immutable không được lưu trữ trong bộ nhớ lưu trữ của hợp đồng. Các biến này được tính toán vào thời gian biên dịch và được lưu trữ trong mã bytecode của hợp đồng. Do đó, chi phí truy cập của chúng thấp hơn nhiều so với biến lưu trữ. Đề xuất sử dụng các từ khóa Constant hoặc Immutable khi có thể.

8. Sử dụng Unchecked Khi không cần quan tâm đến Overflow/Underflow

Khi các nhà phát triển có thể chắc chắn rằng các phép toán số học sẽ không gây ra tràn hoặc thiếu, họ có thể sử dụng từ khóa unchecked được giới thiệu trong Solidity v0.8.0 để tránh kiểm tra tràn hoặc thiếu không cần thiết, do đó tiết kiệm chi phí Gas.

Trong sơ đồ dưới đây, điều kiện bị ràng buộc điều kiện i

Ngoài ra, các phiên bản trình biên dịch từ 0.8.0 trở lên không còn yêu cầu sử dụng thư viện SafeMath nữa, vì trình biên dịch bây giờ đã bao gồm sẵn bảo vệ tràn và tràn dưới.

9. Tối ưu hóa bộ điều chỉnh

Mã của các chỉnh sửa được nhúng vào các chức năng mà chúng sửa đổi. Mỗi khi một chỉnh sửa được sử dụng, mã của nó được nhân bản, điều này làm tăng kích thước bytecode và làm tăng tiêu thụ Gas. Đây là một cách để tối ưu hóa chi phí Gas của các chỉnh sửa:

Trước khi tối ưu hóa:

Sau khi tối ưu hóa:

Trong ví dụ này, bằng cách tái cấu trúc logic thành một hàm nội bộ _checkOwner(), có thể tái sử dụng trong modifier, kích thước bytecode được giảm và chi phí Gas được giảm.

10. Tối ưu hóa Ngắn mạch

Đối với toán tử || (hoặc) và && (và), các phép toán logic được đánh giá với việc rút gọn, có nghĩa là nếu điều kiện đầu tiên đủ để xác định kết quả của biểu thức logic, điều kiện thứ hai sẽ không được đánh giá.

Để tối ưu hóa tiêu thụ Gas, các điều kiện có chi phí tính toán thấp nên được đặt trước, để có thể bỏ qua các phép tính có thể tốn kém.

Khuyến nghị chung

1. Xóa mã không sử dụng

Nếu trong hợp đồng có các chức năng hoặc biến không được sử dụng, được khuyến nghị xóa chúng. Đây là cách trực tiếp nhất để giảm chi phí triển khai hợp đồng và giữ kích thước hợp đồng nhỏ.

Dưới đây là một số gợi ý thực tế:

Sử dụng thuật toán hiệu quả nhất cho các phép tính. Nếu hợp đồng sử dụng trực tiếp kết quả của một số phép tính cụ thể, các phép tính dư thừa nên được loại bỏ. Về cơ bản, bất kỳ phép tính không sử dụng nào nên bị xóa bỏ. Trong Ethereum, các nhà phát triển có thể nhận phần thưởng Gas bằng cách giải phóng không gian lưu trữ. Nếu một biến không còn cần thiết nữa, nó nên được xóa bằng cách sử dụng từ khóa delete hoặc thiết lập lại giá trị mặc định của nó.

Tối ưu vòng lặp: Tránh các hoạt động vòng lặp chi phí cao, cố gắng kết hợp các vòng lặp và di chuyển các phép tính lặp lại ra khỏi phần thân vòng lặp.

2. Sử dụng Hợp đồng được biên soạn trước

Các hợp đồng được biên soạn trước cung cấp các chức năng thư viện phức tạp như mật mã và các hoạt động băm. Vì mã không được thực thi trên EVM mà chạy cục bộ trên nút khách hàng, nên cần ít Gas hơn. Sử dụng các hợp đồng được biên soạn trước có thể tiết kiệm Gas bằng cách giảm công việc tính toán cần thiết để thực thi hợp đồng thông minh.

Các ví dụ về các hợp đồng được biên dịch trước bao gồm thuật toán chữ ký số đường cong Elliptic (ECDSA) và thuật toán băm SHA2-256. Bằng cách sử dụng những hợp đồng này trong các hợp đồng thông minh, các nhà phát triển có thể giảm thiểu chi phí Gas và cải thiện hiệu quả của ứng dụng.

Để xem danh sách đầy đủ các hợp đồng được biên soạn được hỗ trợ bởi mạng Ethereum, vui lòng tham khảo liên kết này [4].

3. Sử dụng Lắp ráp Trực tiếp

Inline assembly cho phép nhà phát triển viết mã nguồn cấp thấp nhưng hiệu quả có thể được thực thi trực tiếp bởi EVM, mà không cần sử dụng các opcode Solidity đắt tiền. Inline assembly cũng cho phép kiểm soát chính xác hơn về việc sử dụng bộ nhớ và lưu trữ, từ đó giảm tối đa chi phí Gas. Ngoài ra, inline assembly có thể thực hiện một số hoạt động phức tạp mà khó triển khai chỉ bằng Solidity một mình, mang lại linh hoạt hơn cho việc tối ưu hóa tiêu thụ Gas.

Đây là một ví dụ về việc sử dụng hợp đồng thông minh nội tuyến để tiết kiệm gas:

Như đã thấy trong ví dụ trên, trường hợp thứ hai sử dụng lắp ráp nội tuyến có hiệu suất Gas cao hơn so với trường hợp tiêu chuẩn.

Tuy nhiên, sử dụng lắp ráp nội tuyến cũng có thể gây ra rủi ro và dễ mắc lỗi. Do đó, nó nên được sử dụng cẩn thận và chỉ được khuyến khích cho những nhà phát triển có kinh nghiệm.

4. Sử dụng Giải pháp Layer 2

Các giải pháp Layer 2 có thể giảm lượng dữ liệu cần phải lưu trữ và tính toán trên mainnet Ethereum.

Các giải pháp Lớp 2 như rollups, sidechains và kênh trạng thái giảm tải xử lý giao dịch khỏi chuỗi Ethereum chính, cho phép các giao dịch nhanh hơn và rẻ hơn.

Bằng cách gói gọn một số lượng lớn giao dịch cùng nhau, những giải pháp này giảm số lượng giao dịch trên chuỗi, từ đó làm giảm phí Gas. Sử dụng các giải pháp Layer 2 cũng tăng cường khả năng mở rộng của Ethereum, cho phép nhiều người dùng và ứng dụng tham gia vào mạng mà không gây tắc nghẽn do quá tải.

5. Sử dụng Công cụ và Thư viện Tối ưu hóa

Có một số công cụ tối ưu hóa có sẵn, như bộ tối ưu hóa solc, bộ tối ưu hóa xây dựng Truffle và trình biên dịch Solidity của Remix.

Những công cụ này có thể giúp giảm kích thước bytecode, loại bỏ mã không sử dụng và giảm số lượng thao tác cần thiết để thực thi hợp đồng thông minh. Kết hợp với các thư viện tối ưu hóa Gas khác như “solmate”, các nhà phát triển có thể giảm được chi phí Gas và nâng cao hiệu suất của hợp đồng thông minh.

Kết luận

Tối ưu hóa tiêu thụ Gas là một bước quan trọng đối với các nhà phát triển, không chỉ giảm thiểu chi phí giao dịch mà còn cải thiện hiệu quả của các hợp đồng thông minh trên các mạng tương thích với EVM. Bằng cách ưu tiên các hoạt động tiết kiệm chi phí, giảm sử dụng bộ nhớ lưu trữ, sử dụng lắp ráp trực tiếp và tuân thủ các phương pháp tốt nhất khác được đề cập trong bài viết này, các nhà phát triển có thể giảm tiêu thụ Gas của hợp đồng một cách hiệu quả.

Tuy nhiên, cần lưu ý rằng trong quá trình tối ưu hóa, nhà phát triển phải thận trọng để tránh tạo ra các lỗ hổng bảo mật. Trong quá trình tối ưu hóa mã và giảm tiêu thụ Gas, tính bảo mật bẩm sinh của hợp đồng thông minh không bao giờ được đánh đổi.

[1]https://ethereum.org/vi/developers/docs/gas/
[2]https://ethereum.github.io/yellowpaper/paper.pdf
[3]https://www.evm.codes/
[4] https://www.evm.codes/precompiled

Miễn trừ trách nhiệm:

  1. Bài viết này được tái bản từ [PANewslab]. Bản quyền thuộc về tác giả gốc [ CertiK]. Nếu bạn có bất kỳ ý kiến nào về việc tái in, vui lòng liên hệ Gate Learnđội ngũ, đội ngũ sẽ xử lý nó càng sớm càng tốt theo các thủ tục liên quan.
  2. Tuyên bố từ chối trách nhiệm: Các quan điểm và ý kiến được thể hiện trong bài viết này chỉ đại diện cho quan điểm cá nhân của tác giả và không thành lập bất kỳ lời khuyên đầu tư nào.
  3. Các phiên bản ngôn ngữ khác của bài viết được dịch bởi nhóm Learn của gate. Trừ khi có quy định khác, bài viết dịch có thể không được sao chép, phân phối hoặc đạo văn.

Top 10 Best Practices cho tối ưu hóa Gas trong Hợp đồng thông minh Ethereum

Trung cấp1/3/2025, 11:43:43 AM
Bài viết này khám phá vấn đề phí Gas trên Ethereum mainnet và phương pháp tối ưu hóa. Nó tập trung vào cơ chế Gas của EVM, các khái niệm cốt lõi để tối ưu hóa chi phí Gas và các phương pháp hay nhất để phát triển hợp đồng thông minh. Điều này bao gồm giảm sử dụng bộ nhớ, đóng gói biến, tối ưu hóa kiểu dữ liệu và sử dụng biến có kích thước cố định.

Bằng cách tuân thủ những phương pháp này, các nhà phát triển có thể giảm tiêu thụ Gas trong các hợp đồng thông minh, giảm chi phí giao dịch và tạo ra các ứng dụng hiệu quả và thân thiện với người dùng hơn.

Phí gas trên Ethereum mainnet luôn là một vấn đề lớn, đặc biệt trong các giai đoạn tắc nghẽn mạng. Trong thời điểm cao điểm, người dùng thường phải trả mức phí giao dịch cực kỳ cao. Do đó, tối ưu hóa chi phí Gas trong giai đoạn phát triển hợp đồng thông minh là rất quan trọng. Tối ưu hóa Gas không chỉ giúp giảm chi phí giao dịch một cách hiệu quả mà còn cải thiện hiệu suất giao dịch, mang lại cho người dùng trải nghiệm blockchain tiết kiệm và hiệu quả hơn.

Bài viết này sẽ trình bày cơ chế phí Gas của Máy Ảo Ethereum (EVM), các khái niệm cốt lõi liên quan đến tối ưu hóa phí Gas, và các thực tiễn tốt nhất để tối ưu hóa phí Gas khi phát triển các hợp đồng thông minh. Hi vọng rằng nội dung này sẽ truyền cảm hứng và hỗ trợ các nhà phát triển, đồng thời giúp người dùng thông thường hiểu rõ hơn về cách thức hoạt động của hệ thống phí Gas EVM, từ đó cùng nhau đối mặt với những thách thức trong hệ sinh thái blockchain.

Tổng quan về cơ chế phí Gas của EVM

Trong các mạng tương thích với EVM, “Gas” đề cập đến đơn vị được sử dụng để đo lường sức mạnh tính toán cần thiết để thực hiện các hoạt động cụ thể.

Sơ đồ dưới đây minh họa cấu trúc của EVM. Trong sơ đồ, việc tiêu thụ Gas được chia thành ba phần: thực thi hoạt động, gọi tin nhắn ngoại vi, và đọc/ghi bộ nhớ và lưu trữ.

Nguồn: Trang web chính thức của Ethereum[1]

Kể từ khi kích hoạt EIP-1559 (London Hard Fork), phí Gas được tính bằng công thức sau:

Phí gas = số đơn vị gas sử dụng * (phí cơ sở + phí ưu tiên)

Phí cơ bản được đốt cháy, trong khi phí ưu tiên đóng vai trò khuyến khích người xác thực đưa giao dịch vào blockchain. Đặt phí ưu tiên cao hơn khi gửi giao dịch làm tăng khả năng giao dịch được đưa vào khối tiếp theo. Điều này tương tự như một “mẹo” được trả bởi người dùng cho các trình xác thực.

1. Hiểu về Tối ưu Gas trong EVM

Khi biên soạn một hợp đồng thông minh với Solidity, hợp đồng được chuyển đổi thành một loạt các “mã hoạt động,” hoặc mã opcode.

Mỗi mã opcode (như tạo hợp đồng, thực hiện cuộc gọi tin nhắn, truy cập lưu trữ tài khoản, và thực hiện các hoạt động trên máy ảo) đều có chi phí tiêu thụ Gas kèm theo, được ghi chép trong Ethereum Yellow Paper[2].

Sau nhiều sửa đổi EIP, chi phí Gas của một số mã opcode đã được điều chỉnh, có thể khác với các giá trị trong Yellow Paper. Để biết thông tin chi tiết về chi phí opcode mới nhất, vui lòng tham khảo nguồn này [3].

2. Khái niệm cơ bản về tối ưu hóa gas

Khái niệm cốt lõi của việc tối ưu hóa Gas là ưu tiên các hoạt động tiết kiệm chi phí trên blockchain EVM và tránh các hoạt động gây ra chi phí Gas cao.

Trong EVM, các hoạt động sau đây có chi phí tương đối thấp:

  • Đọc và ghi biến nhớ
  • [ ] Đọc các biến hằng và không thay đổi
  • [ ] Đọc và viết các biến cục bộ
  • [ ] Đọc các biến calldata, chẳng hạn như mảng calldata và cấu trúc calldata
  • [ ] Cuộc gọi hàm nội bộ

Các hoạt động có chi phí cao bao gồm:

  • [ ] Đọc và ghi các biến trạng thái được lưu trữ trong lưu trữ hợp đồng
  • [ ] Cuộc gọi hàm bên ngoài
  • [ ] Hoạt động vòng lặp

Tối Ưu Hóa Phí Gas EVM Thực Hành Tốt Nhất

Dựa trên các khái niệm cơ bản trên, chúng tôi đã biên soạn một danh sách các thực tiễn tối ưu hóa phí Gas cho cộng đồng nhà phát triển. Bằng cách tuân thủ các thực tiễn này, các nhà phát triển có thể giảm tiêu thụ Gas của hợp đồng thông minh, giảm chi phí giao dịch và tạo ra các ứng dụng hiệu quả và thân thiện với người dùng hơn.

1. Giảm thiểu việc sử dụng bộ nhớ

Trong Solidity, Lưu trữ là một tài nguyên hạn chế và Tiêu thụ Gas của nó cao hơn đáng kể so với Bộ nhớ. Mỗi khi một hợp đồng thông minh đọc từ hoặc ghi vào lưu trữ, nó phải chịu một chi phí Gas cao.

Theo định nghĩa trong Ethereum Yellow Paper, chi phí của các hoạt động lưu trữ cao hơn 100 lần so với hoạt động bộ nhớ. Ví dụ, các mã opcode như sload và sstore tốn ít nhất 100 đơn vị Gas trong trường hợp tốt nhất, trong khi các hoạt động bộ nhớ như mload và mstore chỉ tiêu thụ 3 đơn vị Gas.

Các phương pháp để giới hạn việc sử dụng lưu trữ bao gồm:

  • [ ] Lưu trữ dữ liệu không cố định trong bộ nhớ
  • [ ] Giảm số lần sửa đổi lưu trữ: Bằng cách lưu kết quả trung gian trong bộ nhớ, và chỉ gán kết quả cuối cùng cho biến lưu trữ sau khi tất cả các phép tính được hoàn thành.

2. Đóng gói biến số

Số lượng khe lưu trữ được sử dụng trong một hợp đồng thông minh và cách mà các nhà phát triển biểu diễn dữ liệu có thể ảnh hưởng đáng kể đến việc tiêu thụ Gas.

Bộ biên dịch Solidity đóng gói các biến lưu trữ liên tiếp trong quá trình biên dịch, sử dụng các khe lưu trữ 32 byte như đơn vị cơ bản để lưu trữ biến. Đóng gói biến đề cập đến việc sắp xếp các biến theo cách cho phép nhiều biến vừa với một khe lưu trữ duy nhất.

Bên trái là một cài đặt ít hiệu quả hơn, tiêu thụ 3 khe lưu trữ; bên phải là một cài đặt hiệu quả hơn.

Bằng cách thực hiện điều chỉnh này, các nhà phát triển có thể tiết kiệm 20.000 đơn vị Gas (vì lưu trữ một khe lưu trữ không được sử dụng tốn 20.000 Gas), nhưng hiện chỉ cần hai khe lưu trữ.

Vì mỗi khe lưu trữ tiêu thụ Gas, việc đóng gói biến tối ưu hóa việc sử dụng Gas bằng cách giảm số lượng khe lưu trữ yêu cầu.

3. Tối ưu hóa kiểu dữ liệu

Một biến có thể được biểu diễn bằng các loại dữ liệu khác nhau, nhưng chi phí thực hiện thao tác khác nhau tùy thuộc vào loại dữ liệu. Lựa chọn loại dữ liệu phù hợp giúp tối ưu hóa việc sử dụng Gas.

Ví dụ, trong Solidity, số nguyên có thể được chia thành các kích thước khác nhau: uint8, uint16, uint32, v.v. Khi EVM hoạt động theo đơn vị 256 bit, việc sử dụng uint8 có nghĩa là EVM phải chuyển đổi nó thành uint256 trước, và việc chuyển đổi này sẽ tạo thêm chi phí Gas.

Chúng ta có thể so sánh Chi phí Gas của uint8 và uint256 bằng cách sử dụng mã trong sơ đồ. Hàm UseUint() tiêu thụ 120.382 đơn vị Gas, trong khi hàm UseUInt8() tiêu thụ 166.111 đơn vị Gas.

Một mình, việc sử dụng uint256 rẻ hơn uint8. Tuy nhiên, nếu chúng ta áp dụng tối ưu hóa đóng gói biến được đề xuất trước đó, điều đó làm nên sự khác biệt. Nếu các nhà phát triển có thể đóng gói bốn biến uint8 vào một khe cắm lưu trữ duy nhất, tổng chi phí duyệt qua chúng sẽ thấp hơn việc sử dụng bốn biến uint256. Trong trường hợp này, hợp đồng thông minh có thể đọc và ghi khe cắm lưu trữ một lần và tải tất cả bốn biến uint8 vào bộ nhớ/lưu trữ trong một hoạt động duy nhất.

4. Sử dụng Biến Kích thước Cố định Thay vì Biến Động

Nếu dữ liệu có thể bị ràng buộc trong 32 byte, nên sử dụng loại dữ liệu bytes32 thay vì bytes hoặc chuỗi. Thông thường, biến có kích thước cố định tiêu tốn ít Gas hơn các biến có kích thước biến đổi. Nếu độ dài byte có thể bị giới hạn, hãy cố gắng chọn độ dài nhỏ nhất từ bytes1 đến bytes32.

5. Phân biệt Mapping và Array

Trong Solidity, danh sách dữ liệu có thể được biểu diễn bằng hai loại dữ liệu: Mảng và Ánh xạ, mỗi loại có cú pháp và cấu trúc riêng biệt.

Trường hợp chung, bản đồ ánh xạ hiệu quả hơn và tiết kiệm chi phí hơn so với mảng, trong khi mảng có thể được lặp lại và hỗ trợ đóng gói kiểu dữ liệu. Do đó, nên ưu tiên sử dụng bản đồ ánh xạ khi quản lý danh sách dữ liệu, trừ khi yêu cầu lặp lại hoặc tiêu thụ Gas có thể được tối ưu hóa thông qua đóng gói kiểu dữ liệu.

6. Sử dụng calldata thay vì bộ nhớ

Biến được khai báo trong tham số của hàm có thể được lưu trữ trong calldata hoặc memory. Sự khác biệt chính là memory có thể được sửa đổi bởi hàm, trong khi calldata là không thể thay đổi.

Hãy ghi nhớ nguyên tắc này: nếu các tham số của hàm chỉ đọc, hãy ưu tiên sử dụng calldata thay vì memory. Điều này tránh các thao tác sao chép không cần thiết từ calldata của hàm vào bộ nhớ.

Ví dụ 1: Sử dụng bộ nhớ

Khi sử dụng từ khóa memory, các giá trị của mảng được sao chép từ encoded calldata vào bộ nhớ trong quá trình giải mã ABI. Chi phí thực thi của khối mã này là 3.694 đơn vị gas.

Ví dụ 2: Sử dụng calldata

Khi đọc các giá trị trực tiếp từ calldata, hoạt động bộ nhớ trung gian sẽ bị bỏ qua. Tối ưu hóa này giảm chi phí thực thi chỉ còn 2.413 đơn vị Gas, dẫn đến cải tiến hiệu suất Gas 35%.

7. Sử dụng từ khóa Constant/Immutable khi có thể

Các biến Constant/Immutable không được lưu trữ trong bộ nhớ lưu trữ của hợp đồng. Các biến này được tính toán vào thời gian biên dịch và được lưu trữ trong mã bytecode của hợp đồng. Do đó, chi phí truy cập của chúng thấp hơn nhiều so với biến lưu trữ. Đề xuất sử dụng các từ khóa Constant hoặc Immutable khi có thể.

8. Sử dụng Unchecked Khi không cần quan tâm đến Overflow/Underflow

Khi các nhà phát triển có thể chắc chắn rằng các phép toán số học sẽ không gây ra tràn hoặc thiếu, họ có thể sử dụng từ khóa unchecked được giới thiệu trong Solidity v0.8.0 để tránh kiểm tra tràn hoặc thiếu không cần thiết, do đó tiết kiệm chi phí Gas.

Trong sơ đồ dưới đây, điều kiện bị ràng buộc điều kiện i

Ngoài ra, các phiên bản trình biên dịch từ 0.8.0 trở lên không còn yêu cầu sử dụng thư viện SafeMath nữa, vì trình biên dịch bây giờ đã bao gồm sẵn bảo vệ tràn và tràn dưới.

9. Tối ưu hóa bộ điều chỉnh

Mã của các chỉnh sửa được nhúng vào các chức năng mà chúng sửa đổi. Mỗi khi một chỉnh sửa được sử dụng, mã của nó được nhân bản, điều này làm tăng kích thước bytecode và làm tăng tiêu thụ Gas. Đây là một cách để tối ưu hóa chi phí Gas của các chỉnh sửa:

Trước khi tối ưu hóa:

Sau khi tối ưu hóa:

Trong ví dụ này, bằng cách tái cấu trúc logic thành một hàm nội bộ _checkOwner(), có thể tái sử dụng trong modifier, kích thước bytecode được giảm và chi phí Gas được giảm.

10. Tối ưu hóa Ngắn mạch

Đối với toán tử || (hoặc) và && (và), các phép toán logic được đánh giá với việc rút gọn, có nghĩa là nếu điều kiện đầu tiên đủ để xác định kết quả của biểu thức logic, điều kiện thứ hai sẽ không được đánh giá.

Để tối ưu hóa tiêu thụ Gas, các điều kiện có chi phí tính toán thấp nên được đặt trước, để có thể bỏ qua các phép tính có thể tốn kém.

Khuyến nghị chung

1. Xóa mã không sử dụng

Nếu trong hợp đồng có các chức năng hoặc biến không được sử dụng, được khuyến nghị xóa chúng. Đây là cách trực tiếp nhất để giảm chi phí triển khai hợp đồng và giữ kích thước hợp đồng nhỏ.

Dưới đây là một số gợi ý thực tế:

Sử dụng thuật toán hiệu quả nhất cho các phép tính. Nếu hợp đồng sử dụng trực tiếp kết quả của một số phép tính cụ thể, các phép tính dư thừa nên được loại bỏ. Về cơ bản, bất kỳ phép tính không sử dụng nào nên bị xóa bỏ. Trong Ethereum, các nhà phát triển có thể nhận phần thưởng Gas bằng cách giải phóng không gian lưu trữ. Nếu một biến không còn cần thiết nữa, nó nên được xóa bằng cách sử dụng từ khóa delete hoặc thiết lập lại giá trị mặc định của nó.

Tối ưu vòng lặp: Tránh các hoạt động vòng lặp chi phí cao, cố gắng kết hợp các vòng lặp và di chuyển các phép tính lặp lại ra khỏi phần thân vòng lặp.

2. Sử dụng Hợp đồng được biên soạn trước

Các hợp đồng được biên soạn trước cung cấp các chức năng thư viện phức tạp như mật mã và các hoạt động băm. Vì mã không được thực thi trên EVM mà chạy cục bộ trên nút khách hàng, nên cần ít Gas hơn. Sử dụng các hợp đồng được biên soạn trước có thể tiết kiệm Gas bằng cách giảm công việc tính toán cần thiết để thực thi hợp đồng thông minh.

Các ví dụ về các hợp đồng được biên dịch trước bao gồm thuật toán chữ ký số đường cong Elliptic (ECDSA) và thuật toán băm SHA2-256. Bằng cách sử dụng những hợp đồng này trong các hợp đồng thông minh, các nhà phát triển có thể giảm thiểu chi phí Gas và cải thiện hiệu quả của ứng dụng.

Để xem danh sách đầy đủ các hợp đồng được biên soạn được hỗ trợ bởi mạng Ethereum, vui lòng tham khảo liên kết này [4].

3. Sử dụng Lắp ráp Trực tiếp

Inline assembly cho phép nhà phát triển viết mã nguồn cấp thấp nhưng hiệu quả có thể được thực thi trực tiếp bởi EVM, mà không cần sử dụng các opcode Solidity đắt tiền. Inline assembly cũng cho phép kiểm soát chính xác hơn về việc sử dụng bộ nhớ và lưu trữ, từ đó giảm tối đa chi phí Gas. Ngoài ra, inline assembly có thể thực hiện một số hoạt động phức tạp mà khó triển khai chỉ bằng Solidity một mình, mang lại linh hoạt hơn cho việc tối ưu hóa tiêu thụ Gas.

Đây là một ví dụ về việc sử dụng hợp đồng thông minh nội tuyến để tiết kiệm gas:

Như đã thấy trong ví dụ trên, trường hợp thứ hai sử dụng lắp ráp nội tuyến có hiệu suất Gas cao hơn so với trường hợp tiêu chuẩn.

Tuy nhiên, sử dụng lắp ráp nội tuyến cũng có thể gây ra rủi ro và dễ mắc lỗi. Do đó, nó nên được sử dụng cẩn thận và chỉ được khuyến khích cho những nhà phát triển có kinh nghiệm.

4. Sử dụng Giải pháp Layer 2

Các giải pháp Layer 2 có thể giảm lượng dữ liệu cần phải lưu trữ và tính toán trên mainnet Ethereum.

Các giải pháp Lớp 2 như rollups, sidechains và kênh trạng thái giảm tải xử lý giao dịch khỏi chuỗi Ethereum chính, cho phép các giao dịch nhanh hơn và rẻ hơn.

Bằng cách gói gọn một số lượng lớn giao dịch cùng nhau, những giải pháp này giảm số lượng giao dịch trên chuỗi, từ đó làm giảm phí Gas. Sử dụng các giải pháp Layer 2 cũng tăng cường khả năng mở rộng của Ethereum, cho phép nhiều người dùng và ứng dụng tham gia vào mạng mà không gây tắc nghẽn do quá tải.

5. Sử dụng Công cụ và Thư viện Tối ưu hóa

Có một số công cụ tối ưu hóa có sẵn, như bộ tối ưu hóa solc, bộ tối ưu hóa xây dựng Truffle và trình biên dịch Solidity của Remix.

Những công cụ này có thể giúp giảm kích thước bytecode, loại bỏ mã không sử dụng và giảm số lượng thao tác cần thiết để thực thi hợp đồng thông minh. Kết hợp với các thư viện tối ưu hóa Gas khác như “solmate”, các nhà phát triển có thể giảm được chi phí Gas và nâng cao hiệu suất của hợp đồng thông minh.

Kết luận

Tối ưu hóa tiêu thụ Gas là một bước quan trọng đối với các nhà phát triển, không chỉ giảm thiểu chi phí giao dịch mà còn cải thiện hiệu quả của các hợp đồng thông minh trên các mạng tương thích với EVM. Bằng cách ưu tiên các hoạt động tiết kiệm chi phí, giảm sử dụng bộ nhớ lưu trữ, sử dụng lắp ráp trực tiếp và tuân thủ các phương pháp tốt nhất khác được đề cập trong bài viết này, các nhà phát triển có thể giảm tiêu thụ Gas của hợp đồng một cách hiệu quả.

Tuy nhiên, cần lưu ý rằng trong quá trình tối ưu hóa, nhà phát triển phải thận trọng để tránh tạo ra các lỗ hổng bảo mật. Trong quá trình tối ưu hóa mã và giảm tiêu thụ Gas, tính bảo mật bẩm sinh của hợp đồng thông minh không bao giờ được đánh đổi.

[1]https://ethereum.org/vi/developers/docs/gas/
[2]https://ethereum.github.io/yellowpaper/paper.pdf
[3]https://www.evm.codes/
[4] https://www.evm.codes/precompiled

Miễn trừ trách nhiệm:

  1. Bài viết này được tái bản từ [PANewslab]. Bản quyền thuộc về tác giả gốc [ CertiK]. Nếu bạn có bất kỳ ý kiến nào về việc tái in, vui lòng liên hệ Gate Learnđội ngũ, đội ngũ sẽ xử lý nó càng sớm càng tốt theo các thủ tục liên quan.
  2. Tuyên bố từ chối trách nhiệm: Các quan điểm và ý kiến được thể hiện trong bài viết này chỉ đại diện cho quan điểm cá nhân của tác giả và không thành lập bất kỳ lời khuyên đầu tư nào.
  3. Các phiên bản ngôn ngữ khác của bài viết được dịch bởi nhóm Learn của gate. Trừ khi có quy định khác, bài viết dịch có thể không được sao chép, phân phối hoặc đạo văn.
Bắt đầu giao dịch
Đăng ký và giao dịch để nhận phần thưởng USDTEST trị giá
$100
$5500