25次发布
新 0.8.1 | 2024年8月20日 |
---|---|
0.8.0 | 2024年7月9日 |
0.7.2 | 2024年4月6日 |
0.7.1 | 2024年3月20日 |
0.1.3 | 2022年7月7日 |
#506 in 神奇豆子
每月20,685次下载
95KB
2K SLoC
ic-wasm
一个针对在互联网计算机上运行的Wasm canister进行转换的库
可执行文件
要安装ic-wasm
可执行文件,请运行
$ cargo install ic-wasm
元数据
管理Wasm模块的元数据。
用法:ic-wasm <input.wasm> [-o <output.wasm>] metadata [name] [-d <text content> | -f <file content>] [-v <public|private>]
- 列出当前元数据部分
$ ic-wasm input.wasm metadata
- 列出特定元数据内容
$ ic-wasm input.wasm metadata candid:service
- 添加/覆盖私有元数据部分
注意:私有元数据部分的哈希值任何人都可以读取。如果部分包含低熵数据,攻击者可能可以暴力破解内容。
$ ic-wasm input.wasm -o output.wasm metadata new_section -d "hello, world"
- 从文件添加/覆盖公共元数据部分
$ ic-wasm input.wasm -o output.wasm metadata candid:service -f service.did -v public
信息
打印关于Wasm canister的信息
用法:ic-wasm <input.wasm> info
收缩
移除未使用的函数和调试信息。
注意:在收缩过程中,icp
元数据部分被保留。
用法: ic-wasm <input.wasm> -o <output.wasm> shrink
优化
从wasm-opt
调用wasm优化。
优化器提供了不同的优化级别可供选择。
性能级别(针对运行时优化)
- O4
- O3(默认设置:最佳循环使用率最小化)
- O2
- O1
- O0(无优化)
代码大小级别(针对二进制大小优化)
- Oz(最佳代码大小最小化)
- Os
推荐的设置(O3)将Motoko程序的计算周期使用率减少约10%,Rust程序减少约4%。两种语言的代码大小都减少了约16%。
注意:在优化过程中,icp
元数据部分被保留。
用法: ic-wasm <input.wasm> -o <output.wasm> optimize <level>
从wasm-opt
公开了两个额外的标志
--inline-functions-with-loops
--always-inline-max-function-size<FUNCTION_SIZE>
这些被用于大胆地内联函数,这在Motoko程序中很常见。由于新的成本模型,内联带有循环的函数可以带来很大的性能提升,但也会导致二进制大小大幅增加。由于二进制大小增加,我们可能无法在Wasm模块内的actor类中应用这种内联。
例如: ic-wasm <input.wasm> -o <output.wasm> optimize O3 --inline-functions-with-loops --always-inline-max-function-size 100
资源
限制资源使用,主要用于Motoko Playground
用法: ic-wasm <input.wasm> -o <output.wasm> resource --remove_cycles_transfer --limit_stable_memory_page 1024
仪器(实验性)
仪器可以记录canister方法以将执行跟踪输出到稳定内存。
用法: ic-wasm <input.wasm> -o <output.wasm> instrument --trace-only func1 --trace-only func2 --start-page 16 --page-limit 30
测量过的罐体具有以下附加端点
__get_cycles: () -> (int64) query
. 获取当前周期计数器。__get_profiling: (idx:int32) -> (vec { record { int32; int64 }}, opt int32) query
. 获取执行跟踪日志,从idx
0 开始。如果日志大于 2M,则返回前 2M 的跟踪内容,以及下一个idx
以获取下一个 2M 数据块。__toggle_tracing: () -> ()
. 禁用/启用执行跟踪的记录。__toggle_entry: () -> ()
. 禁用/启用为每个更新调用清除执行跟踪。icp:public name
元数据。用于将执行跟踪中的 func_id 映射到函数名。
当提供 --trace-only
标志时,计数器和跟踪日志仅在执行该函数期间发生,而不是跟踪整个更新调用。请注意,该函数本身必须是不可递归的。
与升级和稳定内存一起工作
默认情况下,执行跟踪存储在稳定内存的前几页(最多 32 页)中。如果没有用户端支持,我们无法对升级或访问稳定内存的代码进行性能分析。如果罐体可以在 canister_init
中预先分配稳定内存的固定区域,我们可以通过 --start-page
标志将此地址传递给 ic-wasm
,这样跟踪就会被写入此预先分配的空间,而不会破坏稳定内存的其他访问。
另一个可选标志 --page-limit
指定稳定内存中预先分配的页面数。默认情况下,它设置为 4096 页(256MB)。我们只存储最多 page-limit
页的跟踪,剩余的跟踪将被丢弃。
推荐通过 Motoko 中的 Region
库和 Rust 中的 ic-stable-structures
预先分配稳定内存。但开发者可以自由使用任何其他库,甚至原始的稳定内存系统 API 来预先分配空间,只要开发者可以保证预分配的空间不会被其他代码访问。
以下是在 Motoko 中预先分配稳定内存的代码示例(带有 --start-page 16
)
import Region "mo:base/Region";
actor {
stable let profiling = do {
let r = Region.new();
ignore Region.grow(r, 4096); // Increase the page number if you need larger log space
r;
};
...
}
以及在 Rust 中(带有 --start-page 1
)
use ic_stable_structures::{
memory_manager::{MemoryId, MemoryManager},
writer::Writer,
DefaultMemoryImpl, Memory,
};
thread_local! {
static MEMORY_MANAGER: RefCell<MemoryManager<DefaultMemoryImpl>> =
RefCell::new(MemoryManager::init(DefaultMemoryImpl::default()));
}
const PROFILING: MemoryId = MemoryId::new(0);
const UPGRADES: MemoryId = MemoryId::new(1);
#[ic_cdk::init]
fn init() {
let memory = MEMORY_MANAGER.with(|m| m.borrow().get(PROFILING));
memory.grow(4096); // Increase the page number if you need larger log space
...
}
#[ic_cdk::pre_upgrade]
fn pre_upgrade() {
let mut memory = MEMORY_MANAGER.with(|m| m.borrow().get(UPGRADES));
...
}
#[ic_cdk::post_upgrade]
fn post_upgrade() {
let memory = MEMORY_MANAGER.with(|m| m.borrow().get(UPGRADES));
...
}
当前限制
- 如果没有从用户代码中预先分配稳定内存,我们无法对升级或访问稳定内存的代码进行性能分析。如果您预先分配了大量的稳定内存页并指定了
page-limit
标志,则可以分析大于 256M 的跟踪。更大的跟踪可以通过__get_profiling
以流式传输方式检索。 - 由于预分配发生在
canister_init
中,我们无法对canister_init
进行性能分析。 - 如果存在心跳,则很难测量任何其他方法调用。也很难测量特定的心跳事件。
- 无法测量查询调用。
- 没有并发调用。
库
要将 ic-wasm
作为库使用,请将以下内容添加到您的 Cargo.toml
[dependencies.ic-wasm]
default-features = false
贡献
查看我们的CONTRIBUTING 以开始。
依赖项
~15–26MB
~419K SLoC