添加 Rust 和 Webassembly
Addition with Rust and Webassembly
给定 我想用以下函数对序列 1,2,3,.. 的前 n 项求和生锈
fn sum_sequence(x: u64) -> u64
{
let mut s: u64 = 0;
for n in 1..=x
{
s = s + n;
}
return s;
}
当我为x64架构编译它
cargo build --release
和 运行 它与 x=10000000000
结果是 13106511857580896768
- 很好。
但是 当我将这个函数编译为 Webassembly (WASM)
cargo build --target wasm32-unknown-unknown --release
和运行它的参数与以前相同,x=10000000000
,
wasmtime ./target/wasm32-unknown-unknown/release/sum_it.wasm --invoke sum_sequence 1000000000
然后结果是-5340232216128654848
.
我没想到 Rust 被编译为 x64 与 Rust 被编译为 WASM 相比在结果上有任何偏差。此外,从 WASM 文本文件(下方)中,我不明白为什么当我使用 WASM 运行 时我会得到负面结果。
为什么 WASM 显示不同的结果,我该怎么做才能更正 WASM 的计算?
(module
(type (;0;) (func (param i64) (result i64)))
(func $sum_sequence (type 0) (param i64) (result i64)
(local i64 i64 i32)
block ;; label = @1
local.get 0
i64.eqz
i32.eqz
br_if 0 (;@1;)
i64.const 0
return
end
i64.const 1
local.set 1
i64.const 0
local.set 2
block ;; label = @1
loop ;; label = @2
local.get 1
local.get 2
i64.add
local.set 2
local.get 1
local.get 1
local.get 0
i64.lt_u
local.tee 3
i64.extend_i32_u
i64.add
local.tee 1
local.get 0
i64.gt_u
br_if 1 (;@1;)
local.get 3
br_if 0 (;@2;)
end
end
local.get 2)
(table (;0;) 1 1 funcref)
(memory (;0;) 16)
(global (;0;) (mut i32) (i32.const 1048576))
(global (;1;) i32 (i32.const 1048576))
(global (;2;) i32 (i32.const 1048576))
(export "memory" (memory 0))
(export "sum" (func $sum))
(export "__data_end" (global 1))
(export "__heap_base" (global 2)))
这似乎是因为 wasm 不支持原生 u64
作为 type,只支持签名变体(特别是 i64
),这就是为什么它使用 i64
作为算术运算的类型。因为这会溢出一个 64 位整数(正确的输出是 n * (n+1) / 2
,或 50000000005000000000
,由于溢出,你得到一个负值,然后打印到控制台。这是由于wasm 中缺少类型支持。
仅供参考,Σ n=0 to N := (N * (N+1) / 2
,我从现在开始使用它,因为它的计算速度更快,并且适合我们的目的。
结果 50000000005000000000
在内存中需要约 65.4 位才能在内存中准确表示,这就是为什么您获得 x86_64 和 wasm 的包装行为,只是包装的类型不同.
使用 NumPy,我们可以清楚地确认这一点:
>>> import numpy as np
>>> a = np.uint64(10000000000)
>>> b = np.uint64(10000000001)
>>> (a >> np.uint64(1)) * b
13106511857580896768
>>> import numpy as np
>>> a = np.int64(10000000000)
>>> b = np.int64(10000000001)
>>> (a >> np.int64(1)) * b
-5340232216128654848
您得到的值是由于无符号和有符号(二进制补码)整数溢出造成的。 (注意:我使用右位移来模拟除以二,我可能也可以使用 //
运算符)。
编辑:此外,Herohtar 在评论中提出了一个很好的观点:如果 运行 在调试模式下,它显然会溢出,并因 'attempt to add with overflow'
.
而恐慌
给定 我想用以下函数对序列 1,2,3,.. 的前 n 项求和生锈
fn sum_sequence(x: u64) -> u64
{
let mut s: u64 = 0;
for n in 1..=x
{
s = s + n;
}
return s;
}
当我为x64架构编译它
cargo build --release
和 运行 它与 x=10000000000
结果是 13106511857580896768
- 很好。
但是 当我将这个函数编译为 Webassembly (WASM)
cargo build --target wasm32-unknown-unknown --release
和运行它的参数与以前相同,x=10000000000
,
wasmtime ./target/wasm32-unknown-unknown/release/sum_it.wasm --invoke sum_sequence 1000000000
然后结果是-5340232216128654848
.
我没想到 Rust 被编译为 x64 与 Rust 被编译为 WASM 相比在结果上有任何偏差。此外,从 WASM 文本文件(下方)中,我不明白为什么当我使用 WASM 运行 时我会得到负面结果。
为什么 WASM 显示不同的结果,我该怎么做才能更正 WASM 的计算?
(module
(type (;0;) (func (param i64) (result i64)))
(func $sum_sequence (type 0) (param i64) (result i64)
(local i64 i64 i32)
block ;; label = @1
local.get 0
i64.eqz
i32.eqz
br_if 0 (;@1;)
i64.const 0
return
end
i64.const 1
local.set 1
i64.const 0
local.set 2
block ;; label = @1
loop ;; label = @2
local.get 1
local.get 2
i64.add
local.set 2
local.get 1
local.get 1
local.get 0
i64.lt_u
local.tee 3
i64.extend_i32_u
i64.add
local.tee 1
local.get 0
i64.gt_u
br_if 1 (;@1;)
local.get 3
br_if 0 (;@2;)
end
end
local.get 2)
(table (;0;) 1 1 funcref)
(memory (;0;) 16)
(global (;0;) (mut i32) (i32.const 1048576))
(global (;1;) i32 (i32.const 1048576))
(global (;2;) i32 (i32.const 1048576))
(export "memory" (memory 0))
(export "sum" (func $sum))
(export "__data_end" (global 1))
(export "__heap_base" (global 2)))
这似乎是因为 wasm 不支持原生 u64
作为 type,只支持签名变体(特别是 i64
),这就是为什么它使用 i64
作为算术运算的类型。因为这会溢出一个 64 位整数(正确的输出是 n * (n+1) / 2
,或 50000000005000000000
,由于溢出,你得到一个负值,然后打印到控制台。这是由于wasm 中缺少类型支持。
仅供参考,Σ n=0 to N := (N * (N+1) / 2
,我从现在开始使用它,因为它的计算速度更快,并且适合我们的目的。
结果 50000000005000000000
在内存中需要约 65.4 位才能在内存中准确表示,这就是为什么您获得 x86_64 和 wasm 的包装行为,只是包装的类型不同.
使用 NumPy,我们可以清楚地确认这一点:
>>> import numpy as np
>>> a = np.uint64(10000000000)
>>> b = np.uint64(10000000001)
>>> (a >> np.uint64(1)) * b
13106511857580896768
>>> import numpy as np
>>> a = np.int64(10000000000)
>>> b = np.int64(10000000001)
>>> (a >> np.int64(1)) * b
-5340232216128654848
您得到的值是由于无符号和有符号(二进制补码)整数溢出造成的。 (注意:我使用右位移来模拟除以二,我可能也可以使用 //
运算符)。
编辑:此外,Herohtar 在评论中提出了一个很好的观点:如果 运行 在调试模式下,它显然会溢出,并因 'attempt to add with overflow'
.