Efficiency: Func <T>, Vs instance T

I recently experimented using the Func<T> class, and still like it. I noticed, however, that more and more I am starting to use it instead of actually using an instance of T , so I wanted to ask; What is the overhead of using Func<T> vs T ? I know this is a somewhat general question, since T can be anything, so I think this question might need to be focused on What are the overhead of passing a function, not an instance of a simple object?

For the argument, suppose the following.

Our object layout T

 public class Person { private string _name = string.Empty; private int _age = 0; private bool _isMale = true; public Person(string name, int age, bool isMale) { this.Name = name; this.Age = age; this.IsMale = isMale; } public string Name { get { return this._name; } set { this._name = value; } } public int Age { get { return this._age; } set { this._age = value; } } public bool IsMale { get { return this._isMale; } set { this._isMale = value; } } } 

Now let's say that we have a pretty extension method on an IDictionary that selects a key value or a default value. Pseudocode can be described as follows:
Is the key found in the KeyValuePair collection Yes, return No, return the default

Option 1. Our extension method using instance T

 public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary source, TKey key, TValue @default) { if (source.ContainsKey(key)) { return source[key]; } return @default; } // usage var myValue = myDictionary.GetValueOrDefault("Richard", new Person()); 

Option 2. Our extension method using Func<T> ... mmm, pretty!

 public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary source, TKey key, Func<TValue> defaultSelector) { if (source.ContainsKey(key)) { return source[key]; } return defaultSelector(); } // usage var myValue = myDictionary.GetValueOrDefault("Richard", () => new Person("Richard", 25, true)); 

Comparison

Comparing the above options, it is clear that there are potential advantages of both. Option 1 is a little easier to read, however at the moment I like using Func<T> , and so option 2 seems to me ideal. I assume that I consider it as a lazily-created parameter that is executed only when necessary, therefore it saves on efficiency, but am I right?

+4
source share
4 answers

This is the code I used for the reference:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Linq.Expressions; namespace ConsoleApplication3 { using System.Collections; using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Xml.Linq; public class Program { public class Person { private string _name = string.Empty; private int _age = 0; private bool _isMale = true; public Person(string name, int age, bool isMale) { this.Name = name; this.Age = age; this.IsMale = isMale; } public string Name { get { return this._name; } set { this._name = value; } } public int Age { get { return this._age; } set { this._age = value; } } public bool IsMale { get { return this._isMale; } set { this._isMale = value; } } } private static void Main(string[] args) { var myDictionary = new Dictionary<string, Person>(); myDictionary.Add("notRichard", new Program.Person("Richard1", 26, true)); myDictionary.Add("notRichard1", new Program.Person("Richard2", 27, true)); myDictionary.Add("notRichard2", new Program.Person("Richard3", 28, true)); myDictionary.Add("notRichard3", new Program.Person("Richard4", 29, true)); // usage Stopwatch sw = new Stopwatch(); sw.Start(); for(int i = 0; i < 100000000; i++) { var myValue = myDictionary.GetValueOrDefault("Richard", new Program.Person("Richard", 25, true)); } sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 100000000; i++) { var myValue = myDictionary.GetValueOrDefault("Richard", ()=> new Program.Person("Richard", 25, true)); } sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); Console.ReadKey(); } } public static class Ex { public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, TValue @default) { if (source.ContainsKey(key)) { return source[key]; } return @default; } public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, Func<TValue> defaultSelector) { if (source.ContainsKey(key)) { return source[key]; } return defaultSelector(); } } } 

Calling each extensionsion method 100000000 times (without searching for a record, therefore, to run Func every time) gives the following result:

T - 10352 ms

Func<T> - 12268 ms

A call to each extensionsion method of 100000000 (and finding a record, therefore, not calling Func at all) gives the following result:

T - 15578 ms

Func<T> - 11072 ms

Therefore, the one that runs faster depends on how many instances you save and how expensive each instance is.

Optimizing the code by reusing the human instance by default gives 6809 ms for T and 7452 for Func<T> :

  Stopwatch sw = new Stopwatch(); var defaultPerson = new Program.Person("Richard", 25, true); sw.Start(); for(int i = 0; i < 100000000; i++) { var myValue = myDictionary.GetValueOrDefault("Richard", defaultPerson); } sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 100000000; i++) { var myValue = myDictionary.GetValueOrDefault("Richard", () => defaultPerson); } 

So, theoretically (if you take the instantiation from the equation), storing the jump in the call stack gives you a slight increase in performance, but in practice this gain is negligible.

+6
source

Passing a reference to a function and passing a reference to an object that I would expect to be very similar. If you make a million of these calls, you are likely to win by storing the link on the side and reusing the same value each time. But note that in the first case, the default object can be made each time:

 Person defaultPerson = new Person(); var myValue = myDictionary.GetValueOrDefault("Richard", defaultPerson); 

but in the second case, you will create a new Person each time you return the default value:

 Func<Person> defaultPersonFunc = () => new Person("Richard", 25, true); var myValue = myDictionary.GetValueOrDefault("Richard", defaultPersonFunc); 

You can get around this by changing to:

 Person defaultPerson = new Person("Richard", 25, true); Func<Person> defaultPersonFunc = () => defaultPerson; var myValue = myDictionary.GetValueOrDefault("Richard", defaultPersonFunc); 

but then I don’t see that you are typing anything using Func<Person>

+1
source

The delegates are great, but I'm not sure if this is a situation where you can use them.

Option 1 is inefficient because you instantiate the new object unconditionally:

 var myValue = myDictionary.GetValueOrDefault("Richard", new Person()); 

always creating a new Person.

Option 2 is inefficient because you are creating a new instance of Func . Your syntax is a shorthand for this:

 var myValue = myDictionary.GetValueOrDefault("Richard", new Func<Person>(()=> { Person("Richard", 25, true)); }); 

Perhaps the compiler can optimize one another, but ultimately this is the case of using the wrong tool to solve the problem. The solution is to use the built-in TryGetValue method and actually run the default code only if necessary:

 Person myValue; if (!myDictionary.TryGetValue("Richard", out myValue)) { myValue = new Person("Richard",25,true); } 

Running tests from another answer that I get when the target is not in the dictionary:

  • 7309 ms (choice 1)
  • 8705 ms (choice 2)
  • 5972 ms (TryGetValue)

If the target is in the dictionary (for example, you never need to use the default value):

  • 10026 ms (choice 2)
  • 7712 ms (choice 2)
  • 3491 ms (TryGetValue)

This is clearly much faster, no matter what. This makes sense - because you need to create an object regardless of the result every time you run a test for option 1 or 2. In the Func script, you need to create two objects when required by default, and one when it is not, so this is the worst when the default is always required.

On the other hand, just using TryGetValue and only conditionally executing the default code, you need to create an object (and only the one that you really need) when the default is required.

Sometimes the old fashioned way is the best :)

FWIW. I think a method like GetValueOrDefault is certainly useful, but maybe not when you need to explicitly define a default value. That is, I do not see a huge benefit in terms of the code style for using the delegate, and, of course, there is no performance benefit. But if you don’t need to determine the default face content, then why not just create an extension method like this:

 public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary source, TKey key) where TValue: new() { TValue value; if (!source.TryGetValue(key, out value)) { value = new TValue(); } return value; } 
0
source

The second method is inefficient because it requires the compiler to generate a closure (a combination of a delegate and an instance of the object) each time the function is called, regardless of whether the closure is really needed. If Person is expensive to create, unconditionally creating a closure will be worse than generating Person unconditionally.

An alternative approach would be for the lambda to be a static method that takes its parameters as a ref struct. I would like C # to provide some language support for this approach, because it can do a lot of what locks can do more efficiently. The code will look something like this:

  public delegate TResult TFuncByRef <TParam, TResult> (ref TParam);
 public static TValue GetValueOrDefault <TKey, TValue, TParam>
   (this IDictionary source, TKey key, 
    FuncByRef <TParam, TValue> defaultSelector, 
    ref TParam param)
 {
     ref TValue Result = default (TValue);
     if (! source.TryGetValue (key, ref Result))
         Result = defaultSelector (ref param);
     return Result;
 }

 struct CreatePersonParams {public string Name;  public int Age;  public bool IsMale};
 static Person CreatePersonByName (ref CreatePersonParams param)
 {
   return new Person (param.Name, param.Age, param.IsMale);
 }
 ... then to use it ...
 {
   ...
   CreatePersonParams newPersonParams;
   newPersonParams.Name = "Emily";
   newPersonParams.Age = 23;
   newPersonParams.IsMale = False;
   ...
   whatever = myDict.GetValueOrDefault (keyValue, CreatePersonByName, ref newPersonParams);
   ...
 }

Note that creating an instance of a structure as a local variable is cheaper than creating a new instance of the class (in essence, this is the same as creating a local variable for each of the fields of the structure). Note also that since CreatePersonByName is a static method, the system needs to create only one delegate throughout the entire program life cycle. Note that passing struct by ref is cheap, no matter how large the structures and accessing the fields of the structures passed by ref is as efficient as accessing the fields of the class.

Using this approach, the same GetValueOrDefault method can process procedures that require any combination of parameters, without requiring any closures. Unfortunately, since C # does not provide any language support for this type of code conversion (which actually could be simpler than converting lambdas to locks), it is necessary to explicitly define all types of structures that could be used with it. It was possible to define a family of structures TupleStruct<T1,T2,T3> etc., but it is still a little ugly.

0
source

Source: https://habr.com/ru/post/1414043/


All Articles