2
[<Struct;CustomComparison>]
type Node<'a>(expr:'a, symbol:int) = 
    member x.Expression = expr
    member x.Symbol = symbol
    override x.ToString() = sprintf "%A" expr
    override x.GetHashCode() = symbol
    override x.Equals(y) = 
        match y with 
        | :? Node<'a> as y -> symbol = y.Symbol
        | _ -> failwith "Invalid equality for Node."

    interface IComparable with
        member x.CompareTo(y) = 
            match y with
            | :? Node<'a> as y -> compare symbol y.Symbol
            | _ -> failwith "Invalid comparison for Node."

The above does not work and the error messages are confusing to me. I am using the Node class to hold other nodes in a AST and am wondering whether turning it into a struct would speed up the compiler I am working on.

The type does typecheck without the IComparable interface, so I am asking specifically about comparison.

Marko Grdinić
  • 3,798
  • 3
  • 18
  • 21
  • 2
    "the error messages are confusing to me" - if you provided the error messages, we'd be in a better position to explain them to you. – Jon Skeet Aug 12 '17 at 12:54
  • `Script1.fsx(4,6): error FS0377: This type uses an invalid mix of the attributes 'NoEquality', 'ReferenceEquality', 'StructuralEquality', 'NoComparison' and 'StructuralComparison'` This is confusing for the reason that I am not using any of the aforementioned attributes as can be seen. – Marko Grdinić Aug 12 '17 at 12:55
  • 3
    Right. You should include that in the question then. (But your `Equals` method should probably just return `false` for non-nodes, rather than failing.) – Jon Skeet Aug 12 '17 at 12:57
  • 1
    I think I've solved it just now. Once I add `CustomEqualityAttribute` it typechecks. – Marko Grdinić Aug 12 '17 at 12:58
  • 2
    Goodo. It might be worth reporting that on github as an odd error message though. – Jon Skeet Aug 12 '17 at 13:03
  • Surprisingly, using structs makes the compiler 20-30% slower. I'd hoped it would not be the case. I've read that .NET is not good with structs and that seems to be true here. It does not bode well for my language. – Marko Grdinić Aug 12 '17 at 13:03
  • https://github.com/Microsoft/visualfsharp/issues/3429 – Marko Grdinić Aug 12 '17 at 13:12
  • @MarkoGrdinic Compiler or the execution of the compiled program? If you can make a reproduction of the issue (by profiling for instance) someone might be able to help you. – Just another metaprogrammer Aug 12 '17 at 13:50
  • 1
    Structs are not a "go faster" button. They are good for some applications and bad for others. You need to measure where the time goes before deciding to use structs. – Fyodor Soikin Aug 12 '17 at 14:34
  • @FuleSnabel The compiler. But inside the language apart from what is needed for .NET interop and for union types, I am doing the majority of allocations on the stack and passing them as arguments. The language is functional in nature and because I am aiming to make it compatible with its GPU backend the functions are stack allocated. – Marko Grdinić Aug 12 '17 at 15:08
  • @FyodorSoikin If I did by hand what turning the `Node` class into a struct should have done, it would have cut the size of the uncompiled AST by half and presumably improved the performance of the compiler. I'd very much like to understand what goes into the performance of .NET structs better since I am compiling tuples to structs rather than using reference types like F# does. There was a [benchmark here](https://stackoverflow.com/a/45276657/4364027) and it seems structs make things worse when put inside lists and such. What is the hidden boxing and unboxing Roman mentioned? – Marko Grdinić Aug 12 '17 at 15:14
  • 1
    @MarkoGrdinic I looked into the benchmark in some depth (machine code level) and to me the difference between list / array comes from that for each over list uses enumerators. When it comes to LINQ/enumerator I find that that there are extra calls to Jit Writebarriers (they have been haunting me before) for structs that I can't explain right now. I am working on some kind of blog entry on this as I found it interesting. This is on 4.6.2 with RyuJit. Older versions might have different issues. – Just another metaprogrammer Aug 14 '17 at 09:50
  • @FuleSnabel I'd be interested in reading about that when you are done so would you mind linking me your blog either here or on your profile? Just recently, I've posted a [question here](https://stackoverflow.com/questions/45662291/why-does-deep-usage-of-the-stack-cause-superlinear-time-behavior-for-a-simple-in/45663511) regarding stack memory usage that might catch your interest. I am guessing the answer will have something to do with the GC walking the stack, but as I've never heard about write barriers I am sure I need to do more studying. – Marko Grdinić Aug 14 '17 at 11:46

0 Answers0