You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
error[E0277]: the trait bound `image::buffer::ImageBuffer<image::color::Rgb<u8>, std::vec::Vec<u8>>: image::image::GenericImage` is not satisfied
--> src/main.rs:48:5
|
48 | draw_filled_circle_mut(&mut image, (500, 500), 400, white);| ^^^^^^^^^^^^^^^^^^^^^^ the trait `image::image::GenericImage` is not implemented for`image::buffer::ImageBuffer<image::color::Rgb<u8>, std::vec::Vec<u8>>`|
= note: required by `imageproc::drawing::conics::draw_filled_circle_mut`
背景
大家都说 Rust 比较擅长系统底层,我猜想图像处理还是很底层的。
至少比较好用的都是 C 语言实现的。
imagemagick
libpng ? 也是 C 实现的。
那我们是不是可以来测试一下 Rust 和 Go 在图像处理上的表现呢?首先从 decode 开始。
Rust decode 一个图片文件
耗时:
Decode in ~1.73 s
Rust 指定 Release 模式下运行 Decode 耗时 :
Decode in ~21 ms
还可以指定 opt-level3
Go decode 一个图片文件
耗时:695.732µs
当时我就震惊了!!!
注意看上面的代码
image.Decode(rd)
这里有 error 返回,但是这里测试代码没有捕获。其实它会报错:
代码修改为:
使用 png 解析就正常了:
执行耗时:
耗时: ~15.914074ms
Rust 和 Go 在对 png 图片进行 decode 时,两者的耗时差别并不大。
当我们将图片更换为 jpeg 后,他们的对比如下:
Rust(RELEASE模式下):
Decode in 3 ms
Go:
耗时: ~5.472894ms
对于 jpeg 的图片,Rust decode 要稍稍优于 Go 的 jpeg decode。
分析讨论过程
通过看 image 的源码发现 png 这个库 next frame 这个方法比较慢。
go版本一次性读整个图,png要一行一行的读,且每行都要一次内存拷贝
为了更高的抽象层级,有非常多细碎的内存拷贝
找到原因了:每行会创建一个Vec,一次Vec创建的时间在几十微秒左右,一个几百行的图片,主要会花在内存分配上
(准确说,单纯创建Vec不会发生堆内存分配,等价于一个栈上变量,代价可以忽略,但随后会对其写入,此时就会导致堆内存分配)
其实怎么存都有问题,抛开内存分配的问题,flatten到一维,行序,列序在处理的时候都对cache不友好
分析2:
主要不是内存分配的问题,其实在初始化的时候已经通过宏得到了图片大小,一次性分配好了。
主要是内存copy的问题,那里还注释了 TODO 待优化。
内存copy,还有下面那一行into转换,内存会重新分配吧,作者打算留给有缘人优化了。
Rust不保证代码的性能。
写不好rust是我不行,不是rust不行。
很多人误以为,用rust写了代码就性能好了
其实我的印象里,内存拷贝的成本应该比内存分配低?
不过至少可以确定,image 这个库的速度确实是慢😂
我还测试了一下 jpeg 的解码,发现速度也一样糟糕
没法复用,他api设计的时候就断了复用的念头了
关键是后面解码的时候remalloc
读Row是个公开api,返回的是字节序列引用
作者还是有考虑的,可能处理时候有点问题,还没细看
Rust 还是一个新手,所以源代码和实现逻辑还得仔细研究研究再来理解大家的讨论了。
其他
环境:
MacBookPro 2017
3.1 GHz Intel Core i7
16 GB 2133 MHz LPDDR3
rustc 1.36.0-nightly (33fe1131c 2019-04-20)
cargo 1.36.0-nightly (b6581d383 2019-04-16)
Go 1.12.4
Rust 画一个○
使用
image-rs/imageproc
在一个 1000x1000 的画板上画一个 500x500 的圆:Debug:
Output: draw in 1.22s
Release:
Output:draw in 20ms
注意:此样例代码,必须在
image-rs/imageproc/examples/drawing.rs
中运行。单独运行会报错:Go 画一个○
耗时:99ms ~ 108ms 左右。
简单对比 Rust 比 Go 快 5 倍。(这个还是非常值得期待的,但是两者图像不太一样,所以还需要修补修补)
以上代码:https://github.com/developer-learning/learning-rust/tree/master/practices/image
翻转我的头像文件(不生成文件)
使用
github.com/disintegration/imaging
库:使用 Rust 的 image crate 源代码:
执行10次,耗时:
非 release 模式下执行:
Cost in 550 ms
Rust decode && flip h 比 Go
github.com/disintegration/imaging
decode && flip h 要快 8 ms。Go :
Rust release 模式下:
Go decode && flip h && save 比 Rust decode && flip h && save 要快 8 ms。
Go decode && flip h && save cost:
Rust decode && flip h && save cost:
Cost in 81 ms
去掉 flip ,纯 image decode 然后再 save,则 Rust 比 Go 慢 10ms:
Go decode && save:
Rust decode && save:
Cost in 77 ms
一探究竟
上面的代码中 Go decode && flip h && save 比 Rust 快 8-10 ms,我们也已经知道差距是在 save。
所以我们研究一下 Go 和 Rust 的 save 部分代码。
Go 代码:
很明显 Go save 的时间比较小是因为
jpegQuality
默认是 95 ,所以指定jpegQuality
为 100:err = imaging.Save(img, "newavatar-origin-flip-h.jpg", imaging.JPEGQuality(100))
执行:Go 整体执行时间比 Rust 多 3-5ms。
缩放
Go:
Rust:
旋转
Go:
Rust:
参考资料
引用 wish:
我个人非常认同,语言好坏并不是一概而论的,有时候你得考虑更多方面,比方说:工程化、生态等。
The text was updated successfully, but these errors were encountered: