简体   繁体   English

F#编译器抛出OutOfMemoryException

[英]F# compiler throws OutOfMemoryException

The project I use contains a lot of classes inherited from single base class. 我使用的项目包含很多从单个基类继承的类。 In unit tests I need to compare received results by types and data. 在单元测试中,我需要按类型和数据比较接收结果。

When I use match comparison by types in the case when conditions list contains about enough many different conditions compiler throws OutOfMemoryException. 当条件列表包含足够多的不同条件时,我在类型中使用匹配比较时,编译器会抛出OutOfMemoryException。

For example, following F# code raises System.OutOfMemoryException (parameter error FS0193) during compilation (and compilation took about 30 seconds before exception was thrown) 例如,在编译期间跟随F#代码引发System.OutOfMemoryException (参数错误FS0193)(并且在抛出异常之前编译大约需要30秒)

type IBaseClass() = class end

type IChildClass1 () = inherit IBaseClass () 

type IChildClass2 () = inherit IBaseClass () 

type IChildClass3 () = inherit IBaseClass () 

type IChildClass4 () = inherit IBaseClass () 

type IChildClass5 () = inherit IBaseClass () 

type IChildClass6 () = inherit IBaseClass () 

type IChildClass7 () = inherit IBaseClass () 

type IChildClass8 () = inherit IBaseClass () 

type IChildClass9 () = inherit IBaseClass () 

type IChildClass10 () = inherit IBaseClass () 

type IChildClass11 () = inherit IBaseClass () 

type IChildClass12 () = inherit IBaseClass () 

type IChildClass13 () = inherit IBaseClass () 

type IChildClass14 () = inherit IBaseClass () 

type IChildClass15 () = inherit IBaseClass () 

type IChildClass16 () = inherit IBaseClass () 

type IChildClass17 () = inherit IBaseClass () 

type IChildClass18 () = inherit IBaseClass () 

type IChildClass19 () = inherit IBaseClass () 

type IChildClass20 () = inherit IBaseClass () 


let AreEqual (original: IBaseClass) (compareWith: IBaseClass) : bool =
    match (original, compareWith) with
    | (:? IChildClass1 as a), (:? IChildClass1 as b) -> a = b
    | (:? IChildClass2 as a), (:? IChildClass2 as b) -> a = b
    | (:? IChildClass3 as a), (:? IChildClass3 as b) -> a = b
    | (:? IChildClass4 as a), (:? IChildClass4 as b) -> a = b
    | (:? IChildClass5 as a), (:? IChildClass5 as b) -> a = b
    | (:? IChildClass6 as a), (:? IChildClass6 as b) -> a = b
    | (:? IChildClass7 as a), (:? IChildClass7 as b) -> a = b
    | (:? IChildClass8 as a), (:? IChildClass8 as b) -> a = b
    | (:? IChildClass9 as a), (:? IChildClass9 as b) -> a = b
    | (:? IChildClass10 as a), (:? IChildClass10 as b) -> a = b
    | (:? IChildClass11 as a), (:? IChildClass11 as b) -> a = b
    | (:? IChildClass12 as a), (:? IChildClass12 as b) -> a = b
    | (:? IChildClass13 as a), (:? IChildClass13 as b) -> a = b
    | (:? IChildClass14 as a), (:? IChildClass14 as b) -> a = b
    | (:? IChildClass15 as a), (:? IChildClass15 as b) -> a = b
    | (:? IChildClass16 as a), (:? IChildClass16 as b) -> a = b
    | (:? IChildClass17 as a), (:? IChildClass17 as b) -> a = b
    | (:? IChildClass18 as a), (:? IChildClass18 as b) -> a = b
    | (:? IChildClass19 as a), (:? IChildClass19 as b) -> a = b
    | (:? IChildClass20 as a), (:? IChildClass20 as b) -> a = b
    | _ -> false

For sure I can add IEquatable interface to my IBaseClass class that's will avoid usage of such match construction, or add int Kind member (or enum) to the IBaseClass interface and make match not by types, but by some int value. 我肯定可以将IEquatable接口添加到我的IBaseClass类中,这样可以避免使用这种匹配构造,或者将int类成员(或枚举)添加到IBaseClass接口,并且不是按类型匹配 ,而是通过某个int值进行匹配

Note, I've tried to compile the same project in MS VS 2010 and MSVS 11 Beta, and had the same compiler error 注意,我试图在MS VS 2010和MSVS 11 Beta中编译相同的项目,并且具有相同的编译器错误

Question: Why the compiler's OutOfMemoryException happens in my case (is it known compiler bug or other limitation), how should I reorganize my match condition to avoid it? 问题:为什么编译器的OutOfMemoryException在我的情况下发生(是已知的编译器错误还是其他限制),我应该如何重新组织匹配条件以避免它?

Update When I put classes into discriminated unions and use similar match comparison Fsc.exe compiles the project without exception 更新当我将类放入有区别的联合并使用类似的匹配比较时,Fsc.exe会毫无例外地编译项目

type AllClasses = 
    | ChildClass1 of IChildClass1 | ChildClass2 of IChildClass2 | ChildClass3 of IChildClass3 | ChildClass4 of IChildClass4 | ChildClass5 of IChildClass5 | ChildClass6 of IChildClass6
    | ChildClass7 of IChildClass7 | ChildClass8 of IChildClass8 | ChildClass9 of IChildClass9 | ChildClass10 of IChildClass10 | ChildClass11 of IChildClass11 | ChildClass12 of IChildClass12
    | ChildClass13 of IChildClass13 | ChildClass14 of IChildClass14 | ChildClass15 of IChildClass15 | ChildClass16 of IChildClass16 | ChildClass17 of IChildClass17 | ChildClass18 of IChildClass18 
    | ChildClass19 of IChildClass19 | ChildClass20 of IChildClass20

let AreEqual2 (original: AllClasses) (compareWith: AllClasses) : bool =
    match (original, compareWith) with
    | ChildClass1(a), ChildClass1(b) -> a = b
    | ChildClass2(a), ChildClass2(b) -> a = b
    | ChildClass3(a), ChildClass3(b) -> a = b
    | ChildClass4(a), ChildClass4(b) -> a = b
    | ChildClass5(a), ChildClass5(b) -> a = b
    | ChildClass6(a), ChildClass6(b) -> a = b
    | ChildClass7(a), ChildClass7(b) -> a = b
    | ChildClass8(a), ChildClass8(b) -> a = b
    | ChildClass9(a), ChildClass9(b) -> a = b
    | ChildClass10(a), ChildClass10(b) -> a = b
    | ChildClass11(a), ChildClass11(b) -> a = b
    | ChildClass12(a), ChildClass12(b) -> a = b
    | ChildClass13(a), ChildClass13(b) -> a = b
    | ChildClass14(a), ChildClass14(b) -> a = b
    | ChildClass15(a), ChildClass15(b) -> a = b
    | ChildClass16(a), ChildClass16(b) -> a = b
    | ChildClass17(a), ChildClass17(b) -> a = b
    | ChildClass18(a), ChildClass18(b) -> a = b
    | ChildClass19(a), ChildClass19(b) -> a = b
    | ChildClass20(a), ChildClass20(b) -> a = b
    | _ -> false

Thanks 谢谢

This is caused by how the F# compiler compiles pattern matching on tuples in this case. 这是由F#编译器在这种情况下如何编译元组上的模式匹配引起的。 I'm not entirely sure when exactly you run in this particular problem and when the compiler uses other approach, but here is an explanation why it fails in this case... 我不完全确定你在这个特定问题上运行的时间以及编译器何时使用其他方法,但这里有一个解释为什么它在这种情况下失败...

If you write a pattern matching like the one in your example, the compiler essentially generates a decision tree that tests the first pattern for original ( :? IChildClass1 ) and then generates two branches. 如果您编写的模式与示例中的模式匹配,则编译器实际上会生成一个决策树,用于测试original的第一个模式( :? IChildClass1 ),然后生成两个分支。 The first branch checks whether compareWith is also IChildClass1 . 第一个分支检查compareWith是否也是IChildClass1 If yes, it runs the first case. 如果是,则运行第一个案例。 The rest of the pattern matching is then duplicated in both of the branches, so you get something like (you can check this by looking at the compiled code for smaller number of cases using ILSpy ): 其余的模式匹配然后在两个分支中重复 ,所以你得到类似的东西(你可以通过查看使用ILSpy的较少数量的案例的编译代码来检查这一点 ):

if (original is IChildClass1)
  if (compareWith is IChildClass1)
    case #1
  if (original is IChildClass2)
    if (compareWith is IChildClass2)
      case #2
    if (original is IChildClass3)
      (...)
else
  if (original is IChildClass2)
    if (compareWith is IChildClass2)
      case #2
    if (original is IChildClass3)
      (...)

This means that the size of the generated code is exponentially proportional to the number of cases in this pattern matching. 这意味着生成的代码的大小与此模式匹配中的个案数量成指数比例。 For 20 of cases, the compiler tries to create 2^20 branches, which (unsurprisingly) fails. 对于20种情况,编译器尝试创建2 ^ 20个分支,这(不出所料)失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM