LoginSignup
13
12

More than 5 years have passed since last update.

.NET4.6で末尾再帰するとしぬかもな話

Last updated at Posted at 2015-12-21

超絶マイナーネタ。現在進行形のお話。

C#で再帰する奴ー

.NETのx64JITには末尾再帰の最適化があります。
再帰してもコールスタックを消費しないすごいやつです。
これを全力で利用しまくったライブラリを書いてたんですが、そのときハマった話。

こんなコード

// ちょっとしたstruct
public struct AStruct
{
    public int Value;

    public AStruct(int x)
    {
        this.Value = x;
    }
}

// 末尾再帰する関数
private static AStruct RecA(AStruct a)
{
    //System.Console.WriteLine(new System.Diagnostics.StackTrace().FrameCount); // コールスタックのカウントだせるやつ
    return (a.Value <= 0) ? a : RecA(new AStruct(a.Value - 1));
}

// よびだすやつ
internal static void Main(string[] args)
{
    RecA(new AStruct(100000000));
}

.NETのtail-call optimizationを有効にするには、x64環境でReleaseビルドでコンパイルします。
実行すると、華麗にStackOverflowExceptionで死にます。んんん、だめじゃん。

// classにしてみた
public class BClass
{
    public int Value;

    public BClass(int x)
    {
        this.Value = x;
    }
}

// 末尾再帰する関数
private static BClass RecB(BClass b)
{
    //System.Console.WriteLine(new System.Diagnostics.StackTrace().FrameCount); // コールスタックのカウントだせるやつ
    return (b.Value <= 0) ? b : RecB(new BClass(b.Value - 1));
}

// よびだすやつ
internal static void Main(string[] args)
{
    RecB(new BClass(100000000));
}

これは正常に完了します。あれー???

RyuJITのバグ???

App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
    <runtime>
        <useLegacyJit enabled="1" />
    </runtime>
</configuration>

App.configuseLegacyJitを追加して値を1にするとRyuJITの使用を無効化できます。
すると上のコードはAもBも正常に動くようになります。
これは一体……?

まだ検証中なの

上のコードのそのまま動くやつをとりあえずここに置きました。
今はRyuJITの仕様とかissueとかを探ってるんですけど、英語が全く読めないので死んでます。
何か関連する情報があったらくださいおねがいします。

13
12
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
12