延迟初始化后删除空检查

时间:2015-09-29 06:46:23

标签: c# c++ delphi lazy-initialization self-modifying

当决定使用延迟初始化时,他通常需要付费。

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(); } }
}

基本上,他必须每次检查他的支持字段是否为零/零值。如果他可以摆脱这种做法怎么办?成功初始化字段后,您可以取消此检查,对吗?

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

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

是的,我是表现疯子,在没有明显问题的情况下寻求表现,只是为了满足我的好奇心。开发此类功能有哪些常用方法?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

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

var
  field: Lazy<VeryExpensiveField>;
begin
  field :=
    function: VeryExpensiveField
    begin
      Result := VeryExpensiveField.Create;
    end;
相关问题