0

In a project I am working on we load various entities from the database, do some work then attempt to save the resulting entities.
If saving the result is not successful, we return a Result object containing any errors. If it is successful, we return Success = true & Messages = new string[0] and don't use it ever again in the rest of the method.

public class Result
{
    public bool Success {get; set;}
    public IEnumerable<string> Messages { get; set;}
}

public class Entity
{
    public IEnumerable<Things> Things {get; set;}
    public IEnumerable<MoreThings> MoreThings { get; set;}
    public IEnumerable<EvenMoreThings> YouGetWhereThisIsGoing {get; set;}
}

public async Task<Result> DoSomeWork(...)
{
    Entity x = await myService.LoadAsync(identifierParam).ConfigureAwait(false);

... do some work with x ...
    
    var saveResult = await myRepoService.SaveAsync(x.Things).ConfigureAwait(false);
    if (!saveResult.Success) return saveResult;

    saveResult = await myOtherRepo.SaveAsync(x.MoreThings).ConfigureAwait(false);
    if (!saveResult.Success) return saveResult;

    saveResult = await yetAnotherRepo.SaveAsync(x.YouGetWhereThisIsGoing).ConfigureAwait(false);
    if (!saveResult.Success) return saveResult;

... assuming all goes well ...

    return new Result { Success = true, Messages = new string[0] };
}

This has been extremely simplified, but in this example is reusing the saveResult actually accomplishing anything/more readable than
var thingSaveResult = ...SaveAsync(x.Things)
...
var moreThingsSaveResult = ...SaveAsync(x.MoreThings)
...etc.?

BakeryD
  • 29
  • 3
  • 4
    No, it's a false economy. From a performance/efficiency standpoint, it's probably a wash; the compiler will almost certainly optimize out the extra variables. And in the context you're using these variables, a little bit of extra memory or processing time isn't going to matter anyway, so clarity is king. – Robert Harvey Mar 29 '21 at 15:12
  • Does this answer your question? [Is micro-optimisation important when coding?](https://softwareengineering.stackexchange.com/questions/99445/is-micro-optimisation-important-when-coding) – gnat Mar 29 '21 at 15:14
  • 1
    Personally I would be more concerned about saving many things in a single method when apparently any of them can fail, leaving you with a partially updated database. – Rik D Mar 29 '21 at 18:47
  • Be careful of your scope when reusing variables but yes, you want to reuse objects as much as humanly possible it has a large impact (consider the overhead of GC compared to overhead of reusing an already allocated one). – Richard Bamford Apr 22 '21 at 10:00
  • From the title of the question, I get the sense that you misunderstand variables and object. Each successive call to `saveResult = await ...` is created a new object. It's the variable that's reused and reassigned. There's no fewer objects being allocated because you used the same `saveResult` variable several times, instead of using unique names for each method call. – Alexander Jun 08 '22 at 19:28

3 Answers3

7

This is not a correct way to think about performance. As a result, you'll make your code harder to follow and more error prone, while having no gains in performance.

The correct way consists of profiling the piece of code to determine the exact location of the bottleneck. Profiling is an essential and mandatory step: you shouldn't try to guess where is the slow part of the code, as you'll get it wrong most of the time (even if you're an experienced developer). Profiling, on the other hand, will show the exact location of the problem, telling, for instance, that the piece of code is wasting 95% of the time doing ToList of a given sequence.

Once you identified it, you think about possible ways to modify the code in order to remove the bottleneck. You formulate hypotheses, and then check them, using either profiling, or benchmarking. Having actual measures is crucial here: you can't claim you optimized something if you can't prove it by showing the actual data.

When formulating hypotheses about possible optimizations, one of the useful techniques is to go see the IL code produced by the compiler. A side effect is that by looking at the IL, you'll understand more not only about the optimizations performed by the compiler, but also about the language itself. Just for fun, do it in relation to your question. Create two pieces of code. Compile. Disassemble. Look at the IL. Noticed anything interesting?

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
  • 2
    Moreover, it's almost always better to get an application working properly and then consider optimizations (if needed.) You can spend a lot of time optimizing something only to find out it's doing the wrong thing. – JimmyJames Mar 29 '21 at 17:34
  • @JimmyJames *almost always*? What situations warrant "doing the wrong thing (allegedly) fast"? – Caleth Mar 30 '21 at 08:28
  • 2
    @Caleth There are always exceptions. The one thing that pops to mind is if the implementation is so slow that you can test it in a reasonable amount of time. You are often better off optimizing the unproven solution than waiting hours to find out if it's right. – JimmyJames Mar 30 '21 at 14:17
  • Jimmy, I would say the first step should be to write code that works _and_ is readable. It’s easier to make code correct if it is readable. And readable code tends to be faster because it doesn’t have unneeded complexity. (And if it turns out you _need_ to optimise, that’s easier if you start with readable code). – gnasher729 Jun 06 '22 at 18:17
0

I'll add one more point because the accepted answer is correct, but overlooking an important aspect of the question. With modern compilers/runtime environments creating a new local variable has very little cost on performance. If you tried to profile the two different codes you woul find very difficult to notice any difference. However an efficient use of the variables in your code helps also to reduce chances of a bug. I'll explain what I mean with an example. If you write:

var moreThingsSaveResult = await myOtherRepo.SaveAsync(x.MoreThings).ConfigureAwait(false);
if (!moreThingsSaveResult.Success) return saveResult;

Then someone tells you that you have to change the method to save moreThings in another DB. You copy two lines and you change three names, two in the first line, one in the second:

var moreThingsOtherDBSaveResult = await myThirdRepo.SaveAsync(x.MoreThings).ConfigureAwait(false);
if (!moreThingsOtherDBSaveResult.Success) return saveResult;

How many chances that you forget to change the name of the variable in the second line? Not many, but it does happen. Forgetting to change the name of a variable after a copy/paste of some lines of code is a major source of bugs. The more variables you have the higher the chances to make a mistake. So, even if it seems just a question about performance, actually it isn't.

FluidCode
  • 709
  • 3
  • 10
-4

The FIRST thing you do is write efficient code. There are some things you can assume:

  1. allocating objects on demand takes more time than creating them initially at the beginning. Weigh creating a bunch of global objects over security and efficiency.

  2. depending on the compiler some statements are more efficient than others. for example in C/C++ using if-then-else is more efficient than using switch-case but often more difficult to read and can contribute to errors in logic for complex statements. In some compilers for() loops are more efficient than while() loops.

  3. C# like Java creates Byte-Code which gets compiled with a JIT(Just in Time) compiler. If you build a UWP application there is a tool to compile the .NET IL into object-code i.e. native machine code.

  4. for time critical operations it's often better to build a class library in C/C++ with possible inline assembly since C# doesn't support inline assembly code.

So the question: Which is more efficient, reusing a variable or instantiating a new variable. The answer depends on how C# creates variables. A memory range/slot in the heap must be found followed by allocation and initialization when creating a new variable. Reusing a variable means reusing existing space and reinitializing that space. It seems like reusing a variable would be more efficient but the problem arises when we don't know how the compiler reuses space. Does it simply assign new characters to existing space or does it reallocate that space before assigning the new characters?

For the most part, declaring a variable on demand is fine. It only remains in the scope it was created and when it goes out of scope it's freed for garbage collection. Modern compilers are pretty good at memory management but there is still a cost imposed how ever small for constantly declaring, freeing, garbage collecting variables. Basically think about memory usage and garbage collection. If you can minimize memory management by declaring variables before a loop and then reusing them in the loop you will improve performance.

Scott
  • 1
  • 2
  • 3
    "The FIRST thing you do is write efficient code." No, the first thing you should do is to write clear code. – Philip Kendall Jun 06 '22 at 08:36
  • 4
    None of what you wrote matters in terms of performance when the application accesses a database. *Any* optimization you make on the grounds you listed is dwarfed by the external communication. – nvoigt Jun 06 '22 at 09:13