I have the following program which construct a local Func from two static methods. But strangely, when I profile the program, it allocated close to a million Func objects. Why invoking Func object is also creating Func instances?
public static class Utils { public static bool ComparerFunc(long thisTicks, long thatTicks) { return thisTicks < thatTicks; } public static int Foo(Guid[] guids, Func<long, long, bool> comparerFunc) { bool a = comparerFunc(1, 2); return 0; } } class Program { static void Main(string[] args) { Func<Guid[], int> func = x => Utils.Foo(x, Utils.ComparerFunc); var guids = new Guid[10]; for (int i = 0; i < 1000000; i++) { int a = func(guids); } } }
In allocation, function memory is divided into program memory and data memory. Program memory consists of the memory used for main and functions. Data memory consists of permanent definitions, such as global data and constants, local declarations, and dynamic data memory.
Memory allocation is the process of setting aside sections of memory in a program to be used to store variables, and instances of structures and classes. There are two basic types of memory allocation: When you declare a variable or an instance of a structure or class.
Locals 101: When a function is called, memory is allocated for all of its locals. In other words, when the flow of control hits the starting { for the function, all of its locals are allocated memory.
One per each calling.
The exact memory allocation behavior is a function of the specific platform's 1 calling conventions. For example, on x86-64, space will not be allocated on the stack for the function argument; instead, it will be passed via a register ( %rcx on Windows, %rdi on *nix).
The memory is allocated during compile time. Dynamic Memory Allocation: Memory allocation done at the time of execution (run time) is known as dynamic memory allocation. Functions calloc () and malloc () support allocating dynamic memory.
Reasons and Advantage of allocating memory dynamically: 1 When we do not know how much amount of memory would be needed for the program beforehand. 2 When we want data structures without any upper limit of memory space. 3 When you want to use your memory space more efficiently. ... More items...
In this memory allocation scheme, execution is slower than static memory allocation. In this memory is allocated at compile time. In this memory is allocated at run time. In this allocated memory remains from start to end of the program. In this allocated memory can be released at any time during the program.
You're using a method group conversion to create the Func<long, long, bool>
used for the comparerFunc
parameter. Unfortunately, the C# 5 specification currently requires that to create a new delegate instance each time it's run. From the C# 5 specification section 6.6, describing the run-time evaluation of a method group conversion:
A new instance of the delegate type D is allocated. If there is not enough memory available to allocate the new instance, a System.OutOfMemoryException is thrown and no further steps are executed.
The section for anonymous function conversions (6.5.1) includes this:
Conversions of semantically identical anonymous functions with the same (possibly empty) set of captured outer variable instances to the same delegate types are permitted (but not required) to return the same delegate instance.
... but there's nothing similar for method group conversions.
That means this code is permitted to be optimized to use a single delegate instance for each of the delegates involved - and Roslyn does.
Func<Guid[], int> func = x => Utils.Foo(x, (a, b) => Utils.ComparerFunc(a, b));
Another option would be to allocate the Func<long, long, bool>
once and store it in a local variable. That local variable would need to be captured by the lambda expression, which prevents the Func<Guid[], int>
from being cached - meaning that if you executed Main
many times, you'd create two new delegates on each call, whereas the earlier solution would cache as far as is reasonable. The code is simpler though:
Func<long, long, bool> comparer = Utils.ComparerFunc; Func<Guid[], int> func = x => Utils.Foo(x, comparer); var guids = new Guid[10]; for (int i = 0; i < 1000000; i++) { int a = func(guids); }
All of this makes me sad, and in the latest edition of the ECMA C# standard, the compiler will be permitted to cache the result of method group conversions. I don't know when/whether it will do so though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With