0

A quick screenshot with the point of interest:

enter image description here

There are 2 questions here.

This happens in a tight loop. The 12.8% code is this:

{
    this with Side = side; PositionPrice = position'; StopLossPrice = sl'; TakeProfitPrice = tp'; Volume = this.Volume + this.Quantity * position'
}

This object is passed around a lot and has 23 fields so it's not tiny. It looks like immutability is great for stable code, but it's horrible for performance.

Since this recursive loop is run in parallel, I need to store it's context variables in an object.

I am looking for a general idea of what makes sense, not something specific to that code because I have a few tight loops with a bunch of math which I need to profile as well. I am sure I'll find the same pattern in several places.

The flaw here is that I store both the context for the calculations and its variables in a singe type that gets passed in the loop. As the variable fields get updated, the whole object has to be recreated.

What would make sense here (in general for this type of situations)?

  • make the fields that can change mutable. In this case, that means keeping the type as is (23 fields) and make some fields mutable (only 5 fields get regularly changed)
  • move the mutable fields to their own type to have a general context object and one holding all the variables. In this case, that means having a context with (23 - 5 fields) and a separate 5 fields type
  • make the mutable fields variables and move them out of the type. In this case, these 5 fields would be passed as variables in the recursive loop?

and for the second question:

enter image description here

I have no idea what the 10.0% line with get_Tag is. I have nothing called 'Tag' in the code, so I assume that's a dotnet internal thing. I have a type called Side and there is a field with the same name used in the loop, but what is the 'Tag' part?

Thomas
  • 10,933
  • 14
  • 65
  • 136
  • While you're puzzling over that kind of stuff, can you just [*do this?*](https://stackoverflow.com/a/4299378/23771) – Mike Dunlavey Aug 30 '21 at 11:39
  • I've done both sampling and tracing and the culprit remains the same: it's the recreation of the immutable (state + variable) object in a tight loop. – Thomas Aug 30 '21 at 13:04
  • Few reactions: 1) 12.8% is not a big deal, it's a little more than 1/8, so even if you could reduce it to zero, the speedup is marginal. 2) Whenever I want to go after object-construction costs, I ask if it's possible to save used objects and re-use then, because if you have to hit the memory manager, it costs hundreds to thousands of instructions. 3) Unless this is a little toy program, I suspect something far bigger is going on, that these statistics are not showing you, but if you study 10 random-time stack samples you will see. Real problems are good at hiding from profilers. – Mike Dunlavey Aug 31 '21 at 14:44
  • @MikeDunlavey this code is used in a loop that runs continuously doing calculations on data being updated with a stream. It's part of a Monte Carlo analysis, so ANY improvement in speed gives more data points and I looked at this 12.8% block since it's the largest one. It will eventually be rewritten in C once the algorithm has been fine tuned though, since dotnet is not a good fit for this, but it's ideal while in development. – Thomas Sep 01 '21 at 10:55
  • If performance actually matters, I would go directly to C (or C++), and then tune the daylights out of it with [*this.*](https://stackoverflow.com/a/378024/23771) That will truly maximize performance. Anything else is just wasting your time. – Mike Dunlavey Sep 03 '21 at 00:10

2 Answers2

1

What I would suggest is not to modify your existing immutable type at all. Instead, create a new type with mutable fields that is only used within your tight loop. If the type leaves that loop, convert it back to your immutable type (assuming you don't need a copy to go through the rest of your program with every iteration).

tranquillity
  • 1,619
  • 1
  • 5
  • 12
0

get_Tag in this case is likely the auto-generated get-only property on a discriminated union, it's just how the F# compiler represents this sort of type in CLR. The property can most easily be seen when looking at F# code from C#, here's a great page on F# decompiled: https://fsharpforfunandprofit.com/posts/fsharp-decompiled/#unions

For the performance issues I can only offer some suggestions:

  • If you can constrain the context object to your code only, then try making a mutable version and see which effect it has.
  • You mention that the context object is quite large, is it possible to split it up?
Andreas Ågren
  • 3,879
  • 24
  • 33