< 返回版块

hzqd 发表于 2021-12-16 18:26

大家好,我写了两个y组合子y1y2

其中,y2 相比 y1 是把 Copy trait 变为了 引用

按理来说,引用避免了内存复制,应该更加高效;但现实却恰恰相反,引用慢了接近 4 倍,且生成了数倍复杂的 ASM

下面是测速代码:

use std::{fmt::Display, time::{Duration, Instant}};

fn main() {
    measure_time(|| fib1(50)).as_secs().echo(); // 耗时:37 秒
    measure_time(|| fib2(50)).as_secs().echo(); // 耗时:139 秒,慢了接近 4 倍
}

fn fib1(n: usize) -> usize {
    n.y1(|f, n| match n {
        0 => 0,
        1 => 1,
        n => f(n - 1) + f(n - 2),
    })
}

fn fib2(n: usize) -> usize {
    n.y2(|f, n| match n {
        0 => 0,
        1 => 1,
        n => f(n - 1) + f(n - 2),
    })
}

trait Ext: Sized {
    fn y1<R>(self, f: impl Copy + Fn(&dyn Fn(Self) -> R, Self) -> R) -> R {
        // The Y Combinator
        fn y<T, R>(f: impl Copy + Fn(&dyn Fn(T) -> R, T) -> R) -> impl Fn(T) -> R {
            move |a| f(&y(f), a)
        }
        // Chainable
        y(f)(self)
    }

    fn y2<R>(self, f: impl Fn(&dyn Fn(Self) -> R, Self) -> R) -> R {
        // The Y Combinator
        fn y<T, R>(f: &dyn Fn(&dyn Fn(T) -> R, T) -> R) -> impl Fn(T) -> R + '_ {
            move |a| f(&y(f), a)
        }
        // Chainable
        y(&f)(self)
    }

    fn tap<R>(self, f: impl FnOnce(&Self) -> R) -> Self {
        f(&self);
        self
    }

    fn echo(self) -> Self where Self: Display {
        self.tap(|s| println!("{}", s))
    }
}

impl<T> Ext for T {}

fn measure_time<R>(f: impl FnOnce() -> R) -> Duration {
    Instant::now().tap(|_| f()).elapsed()
}

为什么不需要 Copyy2 耗时更长,且 ASM 更复杂?

评论区

写评论
作者 hzqd 2021-12-17 23:14

我不认同此观点。 “尾递归优化”是把“递归”优化成“循环”,而斐波那契数列循环到50根本要不了这么长时间。 甚至,循环到一万,都溢出了,仍不到1秒。

--
👇
Easonzero: 个人觉得, 首先你的lambda是一个纯函数, 因此当使用模板的时候, 相当于移动一个栈上的usize, 所以理论上并不会慢于引用的方式.

至于y2比y1慢, 我有个猜测是, 因为y2这个递归传递的是引用, 所以f drop的时机是y2递归树的根节点退出的时候, 那么编译器就无法做尾递归优化, 导致当递归较深时, y2性能大幅度下降.

Easonzero 2021-12-17 15:53

个人觉得, 首先你的lambda是一个纯函数, 因此当使用模板的时候, 相当于移动一个栈上的usize, 所以理论上并不会慢于引用的方式.

至于y2比y1慢, 我有个猜测是, 因为y2这个递归传递的是引用, 所以f drop的时机是y2递归树的根节点退出的时候, 那么编译器就无法做尾递归优化, 导致当递归较深时, y2性能大幅度下降.

shaitao 2021-12-17 14:45

同问

1 共 3 条评论, 1 页