Tips For Optimizing C#/.Net Programs

what optimizations can I do to run C# program faster?

The time comes stop guessing and find where the problem(s) actually are.
Use EQATEC free preformance analyzer to figure out your bottlenecks and fix them.

NGEN is usefull to boost the startup time of the application , but it's definitely not a golden key for that problem. It's most probabble that you will fix it profiling your app.

What about runtime optimization:

  • checkout DB accesses (if any), optimize your queries and minimize the data retrived to the amount you really need

  • look on disk access operations

  • look on CPU consuption. After profiling yoi can use Process Explorer to check CPU and Memory consuption from your application behavioural point of view

  • after profiling identify unnecessary or heavy iteration you made (if any), and make use of dictionary (just example) for O(1) access

... and more...

Like a literature for reading on performance can suggest definitely the monster blog of one of the greatest performance specialists in IT industry: Rico Mariani's Performance Tidbits

Hope this helps.

C# - What are Some High Performance Best Practices/Tips for ADO.NET

One error I see repeated over and over again:

Instantiating and setting up everything (DbConnection, DbCommand, DbParameters) inside a method which is called repeatedly in a tight loop.

Most of the time you can refactor those local variables as class members instantiating them only once and keeping only the updates to DbParameters inside the method.


UPDATED to include sample code asked for in the comments

Disclaimer:
This is a quick assembled sample for the sole intent of demonstrating the point about moving repetitive stuff out of the loop.
Other better practices aren't necessarily implemented.


public static void Show() {
List people = new List();

//Everything is done inside the loop
PersonDal personDal = new PersonDal();
foreach (Person person in people) {
personDal.Insert(person);
}

//Things are setup once outside the loop.
using (DbConnection connection = SqlClientFactory.Instance.CreateConnection()) {
// setup the connection
BetterPersonDal betterPersonDal = new BetterPersonDal(connection);
connection.Open();
foreach (Person person in people) {
betterPersonDal.Insert(person);
}
}
}
}

class Person {
public int Id { get; set; }
public string Name { get; set; }
}

On this first implementation, every thing is set up every time the method is called:


class PersonDal {
public int Insert(Person person) {
DbProviderFactory factory = SqlClientFactory.Instance;

using (DbConnection connection = factory.CreateConnection()) {
connection.Open();

connection.ConnectionString = "Whatever";

using (DbCommand command = connection.CreateCommand()) {
command.CommandText = "Whatever";
command.CommandType = CommandType.StoredProcedure;

DbParameter id = command.CreateParameter();
id.ParameterName = "@Id";
id.DbType = DbType.Int32;
id.Value = person.Id;

DbParameter name = command.CreateParameter();
name.ParameterName = "@Name";
name.DbType = DbType.String;
name.Size = 50;
name.Value = person.Name;

command.Parameters.AddRange(new DbParameter[] { id, name });

return (int)command.ExecuteScalar();
}
}
}
}

Now we move the setup to the objects construction leaving it out of the loop:


class BetterPersonDal {
private DbProviderFactory factory;
private DbConnection connection;
private DbCommand command;
private DbParameter id;
private DbParameter name;

public BetterPersonDal(DbConnection connection) {
this.command = connection.CreateCommand();
this.command.CommandText = "Whatever";
this.command.CommandType = CommandType.StoredProcedure;

this.id = command.CreateParameter();
this.id.ParameterName = "@Id";
this.id.DbType = DbType.Int32;

this.name = command.CreateParameter();
this.name.ParameterName = "@Name";
this.name.DbType = DbType.String;
this.name.Size = 50;

this.command.Parameters.AddRange(new DbParameter[] { id, name });
}

public int Insert(Person person) {
this.id.Value = person.Id;
this.name.Value = person.Name;

return (int)command.ExecuteScalar();
}
}

.NET 2.0 IL Optimization for a while loop

Let's take a slightly simpler example (from this issue):

using System;
public abstract class C
{
public abstract void WithAction(Action a);

private bool creating = true;

public void M()
{
WithAction(() => creating = false);

while (creating) {}
}
}

The IL for the M() method is pretty unremarkable.

Debug:

IL_0000: nop
// Call the WithAction method - I won't go into this
IL_0001: ldarg.0
IL_0002: ldarg.0
IL_0003: ldftn instance void C::'<M>b__2_0'()
IL_0009: newobj instance void [System.Private.CoreLib]System.Action::.ctor(object, native int)
IL_000e: callvirt instance void C::WithAction(class [System.Private.CoreLib]System.Action)
IL_0013: nop
IL_0014: br.s IL_0018
// loop start (head: IL_0018)
IL_0016: nop
IL_0017: nop
// Load 'this.creating'
IL_0018: ldarg.0
IL_0019: ldfld bool C::creating
// Store the value of 'this.creating' into local slot 0
IL_001e: stloc.0
// Load it back out of local slot 0
IL_001f: ldloc.0
// If it's true, go back to IL_0016
IL_0020: brtrue.s IL_0016
// end loop

IL_0022: ret

Release:

// Call the WithAction method - I won't go into this
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: ldftn instance void C::'<M>b__2_0'()
IL_0008: newobj instance void [System.Private.CoreLib]System.Action::.ctor(object, native int)
IL_000d: callvirt instance void C::WithAction(class [System.Private.CoreLib]System.Action)
// loop start (head: IL_0012)
// Load the value of 'this.creating' onto the stack
IL_0012: ldarg.0
IL_0013: ldfld bool C::creating
// If it's true, go back to IL_0012
IL_0018: brtrue.s IL_0012
// end loop
IL_001a: ret

(Annotations mainly by me)

As you can see, they're pretty much the same, except that the debug version has lots of nops (mainly so you can put breakpoints everywhere you'd want to), and it loads everything into local slots (mainly so you can look at them in the debugger).

However, once the JIT gets to them, they tell a different story. Just looking at the loop, in Debug:

L0052: nop
L0053: nop
L0054: mov eax, [ebp-0x4]
L0057: movzx eax, byte [eax+0x4]
L005b: mov [ebp-0x8], eax
L005e: cmp dword [ebp-0x8], 0x0
L0062: jnz L0052

Release:

L002f: movzx ecx, byte [esi+0x4]
L0033: test ecx, ecx
L0035: jnz L0033

You can see that the Debug version loads the value of this.creating on every loop iteration. The Release version however loads it once at L002f, and then never again - on each loop iteration it just does test ecx, ecx, never updating the value held in ecx.


This "makes sense" because if you're accessing a shared field (without any synchronization), the compiler and runtime are allowed to assume that the current thread is the only one accessing it. There are lots of optimizations which can be done because of this assumption: multiple consecutive reads can be condensed into a single read; same with writes; redundant reads or writes can be removed entirely, among others.

For example:

this.field = 1;
this.field = 2;
this.field = 3;

is indistinguishable (from the current thread's point of view) from:

this.field = 3;

The same logic applies to reading a field in a loop: if the current thread isn't writing to that field, the compiler and runtime are allowed to assume that nothing is.

More formally, this is written in the spec:

Execution order

Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. A side effect is defined as a read or write of a volatile field, a write to a non-volatile variable, a write to an external resource, and the throwing of an exception. The critical execution points at which the order of these side effects must be preserved are references to volatile fields (Volatile fields), lock statements (The lock statement), and thread creation and termination. The execution environment is free to change the order of execution of a C# program, subject to the following constraints:

  • Data dependence is preserved within a thread of execution. That is, the value of each variable is computed as if all statements in the thread were executed in original program order.
  • Initialization ordering rules are preserved (Field initialization and Variable initializers).
  • The ordering of side effects is preserved with respect to volatile reads and writes (Volatile fields). Additionally, the execution environment need not evaluate part of an expression if it can deduce that that expression's value is not used and that no needed side effects are produced (including any caused by calling a method or accessing a volatile field). When program execution is interrupted by an asynchronous event (such as an exception thrown by another thread), it is not guaranteed that the observable side effects are visible in the original program order.

If you're accessing a field which another thread is also accessing, you always need some sort of synchronization. This could be a lock statement, or it could be something else (such as one of the methods on the Interlocked class).


You can play with all of the code here on SharpLab - use the ComboBoxes in the top right to change between IL and JIT Asm, and Debug/Release.

Let me know if you want me to go into the IL in more detail.

Best practices to optimize memory in C#

You can use Redgate ANTS Memory profiler (non-free).

Or CLR profiler (free): https://msdn.microsoft.com/library/ms979205

GC.Collect() is not recommended even if it is required in some cases. Please have a look at below code:

private void WriteStringOnImage()
{
try
{
byte[] imgData = getData(@"E:\0000.tif");
using (System.Drawing.Image img = System.Drawing.Image.FromStream(new MemoryStream(imgData)))
{
for (int i = 1; i <= 1000; i++)
{
Bitmap img1 = new Bitmap(new Bitmap(img));
RectangleF rectf = new RectangleF(800, 550, 200, 200);
Graphics g = Graphics.FromImage(img1);
g.DrawString(i.ToString("0000"), new Font("Thaoma", 30), Brushes.Black, rectf);
img1.Save(@"E:\Img\" + i.ToString("0000") + ".tif");
g.Flush();
g.Dispose();
img1.Dispose();
GC.Collect();
}
}
}
catch (Exception){}
}

In the above example I used GC.Collect() because If I do not use GC.Collect() then it is taking memory around 1500mb. But after use of GC.Collect() if is never exceeding than 75mb

i.e. Memory utilization is decreased by 20 times.

But if GC.Collect() is being used excessively and there are not much unused objects lying in the memory then GC.Collect() will slow down your performance and it is time consuming.

You can also use Dispose() if it implements IDisposable.

If you are working with MemoryStream or any other type of stream, then you should use the using blocks.

Sometimes you also need to empty some object by making it null.

As we know data if we process XML data then it takes very heavy memory so we need to free the memory after use but XML class doesn't implement Idisposable interface so you have to make it null (e.g. xmldocument=null;)

You should also keep in mind about unnecessary object initialization.

e.g. Instead of:

ClassA abc=new ClassA();
abc=xyz;

Use:

ClassA abc=xyz;

Try to use method level variable instead of class level if it is being used in only one method.

Make sure you are clearing collection objects.

Keep watch on the memory usage by any third party tool which is being used in your application. Sometimes third party tools takes very high memory.

Use static only if it is must.

Use StringBuilder instead of String. Because if string is concatenated then a new memory is allocated so the old memory data is not being used but it is kept in RAM.

If any large object is being processed in hierarchical classes keep watch on it.

If any XML document is processed and has been kept in memory for future use and that will be use after any event then release that memory and load XML when the required event is fired.

Avoid cloning.

If You are working with string manipulation you can check the data for infinite loop. Sometime special Unicode characters like ellipsis(...) can create problem and causes for infinite loop.

You can also use dotTrace a memory profiler by Jetbrain.

You can also look into event log for any exception which is causing the issue.

If any bitmap object is being created and some image processing is being done then have a look on unmanaged Resources. A bitmap object take a huge memory for unmanaged resources and that might not be released.

As you have mentioned that you are also using SQL server then also keep watch on SQL server procedures and functions and their calling strategies.

In SQL Server if you are saving any data as image data type and if it is larger than 1mb then please use varbinary(MAX) with filestream property but it will work with SQL server 2008 or upper versions of SQL server.

What are the most important optimizing performance best practices in C#


  1. Strings are Immutable.
  2. Understand the using statement.
  3. Understand Boxing and how Generics help.
  4. Understand how the Garbage Collector works.
  5. Parallel programming in .Net 4.0
  6. Understand how File IO affects performance.

Eric Lippert talks alot about optimization. I would read his blog.

I would check out Jon Skeet's blog also.

Is there any way i can optimize this c# code?

It is not the length of the code that matters. It is repeated logic that should be avoided:

private void HideAndDisable(Button b)
{
b.IsEnabled = false;
b.Visibility = Visibility.Hidden;
}

private void ShowAndEnable(Button b)
{
b.IsEnabled = true;
b.Visibility = Visibility.Visible;
}

public void SomeMethod()
{
if(condition)
{
HideAndDisable(button1);
ShowAndEnable(button2);
}
else
{
HideAndDisable(button2);
ShowAndEnable(button1);
}
}

It leads to a code that:

  • is simpler to understand
  • can easily be reused
  • avoids hidden bugs
  • easier to change

Setting up a C# application for max performance build

These are the recommended settings that I would choose for a release build, all of these settings are found on the "Build" tab of the project properties:

  • Uncheck "Define DEBUG constant"
  • Uncheck "Define TRACE constant"
  • Check "Opimize code"
  • Under the "Advanced..." dialog set "Debug Info:" to "pdb-only"

You may also wish to consider using ngen to speed up application start time. This process must be done on the end user PC (normally as part of the installation process) however will generally only improve application performance the first time that it is run*. My advice would be to consider using ngen only if you have a specific concern over the cold boot time of your app.

What do these settings actually do?

DEBUG and TRACE constants

The DEBUG and TRACE constants impact any code enclosed in conditional directives, for example: (Substitute DEBUG for TRACE as desired)

#if DEBUG
// Anything here will not appear in the end output unless the DEBUG constant is defined
#endif

It also impacts any calls made to methods marked with the Conditional attribute such as Debug.Write and Trace.Write:

// The following call will not appear in the end output unless the DEBUG constant is defined
Debug.WriteLine("Test");

You can check both of these for yourself by using something like IL Spy.

Note that these constants have no other effect, for example the JITer doesn't behave differently if the DEBUG constant is defined. You will probably find that these have neglible effect in your application unless you make hefty use of conditional directives.

Optimize code

This controls what optimisation both the compiler (cs.exe) and the JIT compiler perform when compiling your code. You are likely to see the bulk of your performance improvements as a result of this setting.

The following question covers what this setting does in more detail:

  • Benefits of 'Optimize code' option in Visual Studio build

Debug info

The pdb-only setting tells the compiler to put all debug information in a separate .pdb (program database) file. As far as the end assembly is concerned this is exactly the same as the none setting in that the assembly is not impacted, however if you use the pdb-only setting (over the none setting) symbols are at least available if you wish (you don't have to distribute them if you don't want to). This can be pretty useful for example when debugging crash dumps.

Note that you can't "go back" and re-generate symbols for an existing assembly - once you have a lost the .pdb for an assembly (or chose not to create one in the first place) it is pretty much lost forever! Take care of it (especially for assemblies that you release "to the wild").

The only real difference that you will see here is output assembly size - this may impact loading times and memory footprint, but ultimately this setting probably wont have a particularly noticable effect.


(*) assuming that the user exercises most / all of the features of the application the first time they run it - the JITing process is done as a method is called. Read up on JITting / ngen for more detail.



Related Topics



Leave a reply



Submit