-4

当一个人决定使用惰性初始化时,他通常必须为此付出代价。

class Loafer
{
    private VeryExpensiveField field;
    private VeryExpensiveField LazyInitField()
    {
        field = new VeryExpensiveField();
        // I wanna here remove null check from accessor, but how?
        return field;
    }
    property Field { get { return field ?? LazyInitField(); } }
}

基本上,他必须每次检查他的支持字段是否具有 null/nil 值。如果他能摆脱这种做法呢?当你成功初始化字段后,你就可以摆脱这个检查了,对吧?

不幸的是,大多数生产语言不允许您在运行时修改它们的函数,特别是从函数体中添加或删除单个指令,尽管如果明智地使用它会有所帮助。但是,在 C# 中,您可以使用委托(最初我发现它们,后来意识到为什么本机语言具有函数指针)和事件机制来模仿这种行为,从而导致性能不足,因为空检查只是移动到较低级别,但是不会完全消失。一些语言,例如 LISP 和 Prolog,允许您轻松地修改它们的代码,但它们几乎不能被视为生产语言。

在 Delphi 和 C/C++ 等本地语言中,最好编写两个函数,安全和快速,通过指针调用它们并在初始化后将此指针切换到快速版本。您甚至可以允许编译器或 IDE 生成代码来执行此操作,而无需额外头痛。但是正如@hvd 提到的,这甚至会降低速度,因为 CPU 不会知道这些功能几乎相同,因此不会将它们预取到它的缓存中。

是的,我是个性能狂,在没有明确问题的情况下寻求性能,只是为了满足我的好奇心。有哪些常用方法来开发此类功能?

4

2 回答 2

2

实际上,当您将其开销与实际计算进行比较时,惰性工具包框架并不总是那么重要。

有很多方法。你可以使用Lazy,一个自我修改的 lambda 设置,一个布尔值或任何最适合你的工作流程的东西。

惰性评估工具包的开销仅在您进行一些重复计算时才需要考虑。

我的带有微基准的代码示例探讨了在循环中伴随的更昂贵操作的上下文中惰性计算的相对开销。

您可以看到,即使与相对芯片的有效负载操作一起使用,惰性工具包的开销也可以忽略不计。

void Main()
{
    // If the payload is small, laziness toolkit is not neglectible
    RunBenchmarks(i => i % 2 == 0, "Smaller payload");

    // Even this small string manupulation neglects overhead of laziness toolkit
    RunBenchmarks(i => i.ToString().Contains("5"), "Larger payload");
}

void RunBenchmarks(Func<int, bool> payload, string what)
{
    Console.WriteLine(what);
    var items = Enumerable.Range(0, 10000000).ToList();

    Func<Func<int, bool>> createPredicateWithBoolean = () =>
    {
        bool computed = false;
        return i => (computed || (computed = Compute())) && payload(i);
    };

    items.Count(createPredicateWithBoolean());
    var sw = Stopwatch.StartNew();
    Console.WriteLine(items.Count(createPredicateWithBoolean()));
    sw.Stop();
    Console.WriteLine("Elapsed using boolean: {0}", sw.ElapsedMilliseconds);

    Func<Func<int, bool>> createPredicate = () =>
    {
        Func<int, bool> current = i =>
        {
            var computed2 = Compute();
            current = j => computed2;
            return computed2;
        };
        return i => current(i) && payload(i);
    };

    items.Count(createPredicate());
    sw = Stopwatch.StartNew();
    Console.WriteLine(items.Count(createPredicate()));
    sw.Stop();
    Console.WriteLine("Elapsed using smart predicate: {0}", sw.ElapsedMilliseconds);
    Console.WriteLine();
}

bool Compute()
{
    return true; // not important for the exploration
}

输出:

Smaller payload
5000000
Elapsed using boolean: 161
5000000
Elapsed using smart predicate: 182

Larger payload
5217031
Elapsed using boolean: 1980
5217031
Elapsed using smart predicate: 1994
于 2015-09-29T08:38:07.253 回答
1

FWIW 在 Spring4D 的帮助下也可以在 Delphi 中完成:

var
  field: Lazy<VeryExpensiveField>;
begin
  field :=
    function: VeryExpensiveField
    begin
      Result := VeryExpensiveField.Create;
    end;
于 2015-09-29T08:09:06.187 回答