4

I am experiencing some memory performance issues with my code and I am not sure which is the best way to pass parameters to my methods. I will give a short example about this.

I have for instance this class:

class Person
{
    public string Name { get; set; }
    public bool IsMale { get; set; }
    public string Address { get; set; }
    public DateTime DateOfBirth { get; set; }
}

and then I am using it like this:

    static void Main(string[] args)
    {
        Person person = new Person {
            Name = "John",
            Address = "Test address",
            DateOfBirth = DateTime.Now,
            IsMale = false };

        int age = CalculateAge(person);

        int age2 = CalculateAge(person.DateOfBirth);
    }

    private static int CalculateAge(Person person)
    {
        return (DateTime.Now - person.DateOfBirth).Days;
    }

    private static int CalculateAge(DateTime birthDate)
    {
        return (DateTime.Now - birthDate).Days;
    }

Now in my app I have lots of method calls where the entire object (an object with lots of properties not like this "Person" object from my example) was passed as parameter and I am thinking to improve the code by sending to the methods only the properties that they need, but currently I am not sure how this will improve memory performance.

How is it better regarding memory usage, to send the entire Peron object to my method like CalculateAge(person); or to send just the property that is used into the method like CalculateAge(person.DateOfBirth);?

First I thought that calls like this CalculateAge(person.DateOfBirth); (sending as parameters only the needed properties instead of the entire object) are using less memory but in the end after tests I noticed that app performs slower, and now I am not sure if these changes or others were slowing down my app.

Clock
  • 974
  • 3
  • 17
  • 35
  • 1
    How did you determine you were having memory issues due to parameter passing? Have you done a performance/memory analysis of your app? – AlG Jan 21 '16 at 19:04
  • Yes, I was using a StopWatch instance to count the running time, and was bigger... – Clock Jan 21 '16 at 19:08
  • 2
    time is money, not memory. – jgauffin Jan 21 '16 at 19:37
  • 2
    Timing your app using `StopWatch` is not an effective way to find performance issues. It _can_ be a decent way to compare algorithms but it requires MANY iterations to give menaingful results. Cosider looking at commercial profilers like [Ants](http://www.red-gate.com/products/dotnet-development/ants-performance-profiler/) or [dotTrace](http://www.jetbrains.com/profiler/) to get a more accurate understanding of possible performance issues. Passing parameters is very likely not one of your problems. – D Stanley Jan 21 '16 at 19:40
  • passing individual properties is going to be even worse if you think it's bad now. When you pass the object as a whole, you are sending a reference to it (basically c#'s version of a pointer) so one reference. If you send multiple properties instead, you're sending several references + some value copies (DateTime, bool). Any performance bottleneck you are experiencing must be coming from something else. – Mike Johnson Jan 24 '16 at 00:15

2 Answers2

4

There are three situations to consider here:

  1. Passing a class object vs. another class object
  2. Passing a struct value vs. a class object
  3. Passing multiple class/structs vs. a single class

In terms of memory only the first case is going to be an equal trade: it takes as much memory to pass a string Name as it takes to pass Person person.

Passing a struct, such as DateTime, may take more memory or less memory, depending on the size of the struct. For DateTime the size is (currently) eight bytes. For a smaller by-value object, e.g. a struct with a single short, you would need fewer bytes. For larger structs, e.g. DateTimeOffset, you would need more bytes.

Passing multiple objects or values in most cases is going to require more memory than passing a single class object. The only exception would be passing several very small structs.

Independently from considering the memory impact you should consider the logical structure of your API before deciding on the strategy.

Passing a Person to an API that computes the age creates a dependency between the age-computing API and the Person. Passing DOB separately, on the other hand, creates a dependency between Person and the caller of the API.

If you plan to use age-computing API exclusively with Person objects, then passing Person makes sense. Moreover, you should consider adding a computed property directly to the Person class,

On the other hand, if you plan to use age-computing API with other objects (animals, buildings, etc.) you would be better off passing DOB.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • How can i determine the size of e.g. DateTime? The `sizeof` and the `Marshal.SizeOf` do not work here.. – Bashn Jan 21 '16 at 19:32
  • @Bashn [`sizeof` should work in unsafe context](http://stackoverflow.com/a/21881941/335858). – Sergey Kalinichenko Jan 21 '16 at 19:36
  • Isn't somehting like sending the parameters through reference or value influencing this memory performance ? So when they are passed through value, copies are created, so this is not the best way when sending the entire object. So in my case I should consider that they are already send through reference... and my strategy of sending just the needed parameters instead of the entire onject is a good aproach when sending them through value. – Clock Jan 21 '16 at 19:42
  • 2
    @user5631730 `struct`s are passed by value (`DateTime` is a `struct`; `int`, `double`, `Guid`, `byte`, `Nullable` are all `struct`s as well, to name a few). Some of them could be pretty large - bigger than an object reference. You can pass `struct`s by reference if you make your method receive a `ref DateTime` instead of just `DateTime`, but you should be careful with that, because `out` and `ref` semantic is reserved for situations when you want to write back a new `struct`. Passing a `class` object, on the other hand, costs you the same no matter how many properties the object has. – Sergey Kalinichenko Jan 21 '16 at 19:49
  • Thank you @dasblinkenlight for explanations ! – Clock Jan 21 '16 at 19:52
1

Basically, if I'm not completely wrong, this should not matter. All that is passed to the method is a pointer to your object, which is 8 byte on a 64-bit machine (see here).

It should only make a difference if you can reduce the number of objects you pass (n times 8 byte), but also this should never be a problem in a "standard application".

EDIT: As @Bashn pointet out, DateTime is a struct and is therefor completely copied to the stack on method call. Yet it does not matter in this case, because DateTime is 8 bytes big. Depending on the size of the given struct, this may impose more or less overhead on method calling.

Still, passing paramters should never lead to a memory problem. How did you find out your memory issue?

Community
  • 1
  • 1
Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
  • And re. that last paragraph: I guess fewer parameters would also be more likely to be able to be passed in registers rather than via the stack? – CompuChip Jan 21 '16 at 19:06
  • @CompuChip: Possible, I'm not really that into CIL. – Markus Weninger Jan 21 '16 at 19:07
  • 1
    Since `DateTime` is a struct it gets copied on the stack before invoking the method (if i am not mistaking :p) but interesting question +1 – Bashn Jan 21 '16 at 19:07
  • Oh yeah, you a totally right, didn't realize that [DateTime is a struct](https://msdn.microsoft.com/en-us/library/system.datetime(v=vs.110).aspx). Have to update my answer. +1 – Markus Weninger Jan 21 '16 at 19:10
  • I just count the app running time using a StopWatch and noticed that more time is used... – Clock Jan 21 '16 at 19:11
  • And this object that is pass as parameter quite have lots of properties, structs, lists, other objects, the one from my example is just a little one :), i am trying to find a "general" recommended approach for this parameter passing – Clock Jan 21 '16 at 19:15
  • That's not a sophisticated benchmarking method. You have to include VM warmup phases, multiple runs, and memory measurements. First of all, run-time != clock-time != cpu-time. Secondly, a memory problem should mostly occur in the heap, where the longer GC runtime introduces an overhead, not copies to the stack on method calls. General approach? Just pass the object, it is nothing else than a pointer to the heap and is lousy 8 bytes big. – Markus Weninger Jan 21 '16 at 19:15
  • Nevertheless its get completely copied. I prefer just passing the needed data (at least providing an overload with the least needed parameters as @user5631730 already did) PS: and passing a struct to a method can never lead to a memory issue – Bashn Jan 21 '16 at 19:16
  • That deponds on the "level" of the method. If it is a top-level method, used from the outside of the class, i prefer to have all values capsulated. Within the same class, I also often pass just the needed parameters. – Markus Weninger Jan 21 '16 at 19:17
  • I was hopping that passing only the needed parameters will improve app running time, it was somewhere where copies of the parameters where send to the method instead of the pointer to the object... Something like sending parameters through reference or by value.... but this seems to be more a C++ discussion, here it seems that they are already send in the good way :) (by reference) – Clock Jan 21 '16 at 19:29