Gas
Gas là một đơn vị tính toán đo lường lượng khối lượng công việc hay tính toán cần thực hiện và chỉ có thể mua bằng ETH. Khi thực hiện giao dịch hay thực thi smart contract thì đều cần sử dụng một lượng gas nhất định.
Đối với smart contract, lượng gas tiêu thụ phụ thuộc vào độ phức tạp của phép tính. Mỗi phép tính, mà cụ thể là mỗi instruction, đều sử dụng một lượng gas nhất định. Lượng gas này sẽ tương ứng với lượng tài nguyên cần sử dụng để thực hiện instruction. Ví dụ: việc thay đổi dữ liệu lưu trên blockchain tiêu thụ nhiều gas hơn việc cộng hai số nguyên.
Gas Price
Gas price là lượng ETH mà người dùng sẽ trả cho một gas.
Bởi vì việc thực thi hàm cần sử dụng tiền thật, việc tối ưu gas là cực kỳ quan trọng trong lập trình Solidity.
Tip
Có thể tiết kiệm gas bằng cách sử dụng state modifier
view
cho các hàm nếu có thể. Tuy nhiên, nếu như một view function được gọi bởi một non-view function ở trong cùng một contract thì hàm đó vẫn tiêu thụ gas.
Why is Gas Neccessary?
Khi ta thực thi một hàm, tất cả các node ở trong mạng lưới cũng thực thi hàm đó nhằm verify output. Việc sử dụng gas mà chỉ có thể mua bằng ETH là để đảm bảo không có ai đó làm nghẽn đường mạng bằng các vòng lặp vô tận hay là các phép tính toán phức tạp. Nói một cách đơn giản, việc tính phí khi thực thi smart contract sẽ giúp giảm thiểu được các cuộc tấn công DDoS1.
Struct Packing
Tương tự với C++, thứ tự khai báo các thuộc tính của struct có thể ảnh hưởng đến kích thước thật của struct2. Ngoài ra ta cũng nên cân nhắc sử dụng các kiểu số nguyên không dấu nhỏ nhất có thể, chẳng hạn như uint32
hoặc uint16
3.
Việc giảm kích thước của struct sẽ giúp contract tiêu thụ ít gas hơn. Ví dụ minh họa:
struct NormalStruct {
uint a;
uint b;
uint c;
}
struct MiniMe {
uint32 a;
uint32 b;
uint c;
}
// `mini` will cost less gas than `normal` because of struct packing
NormalStruct normal = NormalStruct(10, 20, 30);
MiniMe mini = MiniMe(10, 20, 30);
Declaring Arrays in Memory
Việc ghi dữ liệu vào các biến storage
4 ở trong contract tiêu tốn rất nhiều gas. Do đó, ta nên sử dụng memory
nếu có thể, bởi vì như đã biết, các biến có data location là memory
sẽ giải phóng vùng nhớ sau khi kết thúc hàm.
Ví dụ sau giúp tạo ra một mảng các số chẵn:
function getEvens() pure external returns(uint[] memory) {
uint[] memory evens = new uint[](5);
uint counter = 0;
for (uint i = 1; i <= 10; i++) {
if (i % 2 == 0) {
evens[counter] = i;
counter++;
}
}
return evens;
}
Có thể thấy, cú pháp vòng lặp trong Solidity tương tự như C/C++.
Lưu ý
Các mảng có data location là
memory
cần phải được định sẵn số lượng phần tử (phải là mảng tĩnh).
Resources
Footnotes
-
tham khảo 10.1109/IWBOSE.2018.8327565 (Smart Contracts - Security Patterns in the Ethereum Ecosystem and Solidity) ↩
-
Điều này không đúng với các biến số nguyên không dấu bên ngoài struct vì Solidity luôn chừa 256 bits cho các biến thuộc kiểu
uint
. ↩ -
Xem thêm Solidity - Data Location. ↩