【问题标题】:F# NativePtr.stackalloc Unexpected Stack OverflowF# NativePtr.stackalloc 意外堆栈溢出
【发布时间】:2016-05-30 01:06:09
【问题描述】:

仍在进行我的 F# 性能测试并尝试使基于堆栈的数组正常工作。有关更多背景信息,请参见此处:f# NativePtr.stackalloc in Struct Constructor

据我了解,每个函数调用都应该在堆栈中获得自己的帧。然后在返回时通过将堆栈指针移回来释放此内存。然而,下面会导致堆栈溢出错误 - 不确定为什么在函数内部执行 stackalloc。

有趣的是,这只发生在发布模式,而不是调试模式。

我相信 dotnet 中的标准堆栈大小是 1MB,我还没有调整我的。我希望分配 8192 个整数(32768 字节)不会破坏堆栈。

#nowarn "9"

module File1 =

    open Microsoft.FSharp.NativeInterop
    open System
    open System.Diagnostics    

    let test () =
        let stackAlloc x =
            let mutable ints:nativeptr<int> = NativePtr.stackalloc x
            ()

        let size = 8192            
        let reps = 10000
        let clock = Stopwatch()
        clock.Start()
        for i = 1 to reps do            
            stackAlloc size
        let elapsed = clock.Elapsed.TotalMilliseconds
        let description = "NativePtr.stackalloc"
        Console.WriteLine("{0} ({1} ints, {2} reps): {3:#,##0.####}ms", description, size, reps, elapsed)

    [<EntryPoint>]
    let main argv = 
        printfn "%A" argv
        test ()
        Console.ReadKey() |> ignore
        0

更新 按照 Fyodor Soikin 的建议使用 ILSpy 进行反编译后,我们可以看到在优化期间发生了内联。有点酷,有点吓人!

using Microsoft.FSharp.Core;
using System;
using System.Diagnostics;
using System.IO;

[CompilationMapping(SourceConstructFlags.Module)]
public static class File1
{
    public unsafe static void test()
    {
        Stopwatch clock = new Stopwatch();
        clock.Start();
        for (int i = 1; i < 10001; i++)
        {
            IntPtr intPtr = stackalloc byte[8192 * sizeof(int)];
        }
        double elapsed = clock.Elapsed.TotalMilliseconds;
        Console.WriteLine("{0} ({1} ints, {2} reps): {3:#,##0.####}ms", "NativePtr.stackalloc", 8192, 10000, elapsed);
    }

    [EntryPoint]
    public static int main(string[] argv)
    {
        PrintfFormat<FSharpFunc<string[], Unit>, TextWriter, Unit, Unit> format = new PrintfFormat<FSharpFunc<string[], Unit>, TextWriter, Unit, Unit, string[]>("%A");
        PrintfModule.PrintFormatLineToTextWriter<FSharpFunc<string[], Unit>>(Console.Out, format).Invoke(argv);
        File1.File1.test();
        ConsoleKeyInfo consoleKeyInfo = Console.ReadKey();
        return 0;
    }
}

除此之外,您可能会感兴趣以下内容:

http://www.hanselman.com/blog/ReleaseISNOTDebug64bitOptimizationsAndCMethodInliningInReleaseBuildCallStacks.aspx

也可以使用属性调整优化:

https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.methodimploptions(v=vs.110).aspx?cs-save-lang=1&cs-lang=fsharp#code-snippet-1

【问题讨论】:

  • 这里有点奇怪 - 它是在第 256 次分配上导致问题。
  • 奇怪,猜猜堆栈不是 1mb。

标签: f# stack unmanaged-memory


【解决方案1】:

如果您的stackAlloc 函数被内联,就会发生这种情况,从而导致stackalloc 在test 的框架内发生。这也解释了为什么它只会在 Release 中发生:内联是一种优化,在 Debug 中执行的力度要小于 Release。

为了确认这一点,我会尝试使用 ILSpy 查看您生成的代码。

为什么首先需要使用堆栈分配的数组?这看起来与 Donald Knuth 警告我们的事情一模一样。 :-)

【讨论】:

  • 说真的,虽然我正在尝试编写非常低延迟的套接字代码。我正在对各种语言、编程范式、数据结构和内存管理技术进行基准测试,以了解它们的性能水平如何比较。到目前为止,F# 站得很好,尽管我不得不放弃一些惯用的编程风格——不要使用序列进行迭代。
  • 非常感谢您的建议,使用ILSpy的反编译版本显示已发生内联。 Stack Overflow 很棒,你也很棒!
猜你喜欢
  • 1970-01-01
  • 2019-05-18
  • 2011-11-24
  • 2015-12-01
  • 1970-01-01
  • 2013-09-24
  • 2021-08-09
  • 2015-03-14
  • 2010-10-28
相关资源
最近更新 更多