Showing posts with label LINQ. Show all posts
Showing posts with label LINQ. Show all posts

Thursday, January 21, 2010

LINQ extension method to Capitalize string

LINQ extension method to Capitalize first letter of each word in a sentence:

public static string Capitalize(this string s)
{
    return s.ToCharArray().Aggregate(String.Empty,
      (currentWord, nextWord) =>
        currentWord.Length == 0 && nextWord != ' ' ? nextWord.ToString().ToUpper() : (
          currentWord.EndsWith(" ") ? currentWord + nextWord.ToString().ToUpper() :
            currentWord + nextWord.ToString()
      )
    );
}

Thursday, January 14, 2010

Return collection of anonymous type from method in LINQ

Technorati Tags:
Let’s say you have some method that execute a LINQ query over collection and constructs a collection of (new) anonymous type as result. Here is how you can return it and use it below the scope of the method:
public class Program
{
public static IEnumerable<T> GetExampleCollection<T>(T type)
{
// generate some random collection simulating source data
var random = new Random();
var collection =
Enumerable.Repeat(0, 100).Select(
i => new { ClientId = random.Next(), OrderId = random.Next(), Price = random.NextDouble() });

// run some linq query over it
var queryOverCollection = from item in collection
where item.ClientId % 2 == 0
select new { DoublePrice = item.Price * 2.0 };
IQueryable v = (from item in collection
where item.ClientId % 2 == 0
select new { DoublePrice = item.Price * 2.0 }).AsQueryable();

return queryOverCollection as IEnumerable<T>;
}

static void Main(string[] args)
{
// bring collection of anonymous type items from a method!
var v = GetExampleCollection(new { DoublePrice = new double() });
foreach (var item in v)
{
// ... and use it!
Console.WriteLine(item.DoublePrice);
}
}
}



Note, that anonymous type is resolved on the assemble level, so you can’t reference it in another assembly.

Wednesday, September 19, 2007

.NET 3.5 Beta 2 - no LINQ performance improvements?


     Today I've got a comment on my post where I compared performance of LINQ vs. DAAB vs. ADO.NET, saying that I should repeat my test with .NET Framework 3.5 beta 2. Indeed there are a lot of posts about performance improvements introduced in it, so I've rushed and repeated my test, compiling it in Microsoft Visual Studio 2008 Beta 2, targeting .NET 3.5 beta 2.
     Not sure that proverb "no news - good news" is correct in this case, but - well - no news. I mean that performance results are absolutely the same - LINQ is about 30% slower than raw ADO.NET (4.28 ms per call for ADO.NET vs. 5.51 ms per call for LINQ). Also, using LINQ rather than ADO.NET makes application much more CPU intensive (something like ~20% of CPU intensity with LINQ vs. ~2% of CPU intensity with ADO.NET - difference of order of magnitude!). You may see screenshots below.

Timing results


CPU Intensity.(First third - ADO, second - DAAB, third - LINQ.)

So it seems to me that there are no significant performance improvements, unless there is something fundamentally wrong with my tests (check code here).

     Yet - I want to develop here something I've mentioned in my previous post. This additional performance cost doesn't mean "don't use LINQ". You'll probably never will get to performance incur of 30% in the real project. In the real life scenario you'll never abuse your DB by shooting thousands of SP calls in a batch, without doing something with results. There is always some business logic around, some DAL, some timings, etc - so the performance costs are never so high and they will be spread around evenly, rather than composed into a performance-loss peak. Also - mind that LINQ greatly improves coding productivity, decreases amount of code and project complexity. This means that developers would spend less timing coding and the costly thing for the project is the developer time. In the very worst case - profile your application to find the bottlenecks of 20:80 rule and use ADO.NET in critical sections of your code. And buy quicker CPU - they are cheap today :) ...

P.S.
     Few links to related articles:


Monday, July 16, 2007

Benchmarking LINQ vs. DAAB vs. ADO.NET



Abstract: This article presents benchmarking results of performance of SP calls using LINQ, Data-access application block and ADO.NET.

Some time ago I was doing LINQ performance benchmarking, looking for an option of replacing our existing data layer with it. One thing that was especially important for me to check - was performance of executing SP's. The reason is that most of our DAL logic resides in SP's at one hand and that performance-critical parts are implemented as SP's as well (for all the good reasons, like possibility to benefit from queries compilation etc).

Below are performance benchmarking results of execution uspGetManagerEmployees SP from AdventureWorks DB. (Basically it does some sort of JOIN to retrieve records of employees data for the specified Manager ID).
I tested three data-access models - raw ADO.NET vs. DAAB (our current DAL workhorse) vs. LINQ (May 2006 preview version).

As a prerequisite for running this test you'll need AdventureWorks DB and installation of LINQ.
Few side-notes:
  • I've left opening of DB connection out of the context of benchmarked section. Only SP execution is measured.

  • I use ExecuteReader rather than ExecuteDataSet, as it is much more lightweight. For LINQ benchmark I using GetEnumerator().

So here goes the test code:

1. Plain ADO.NET:


public static void SqlSPBench()
{
string connectionString =
"Data Source=localhost;Initial Catalog=AdventureWorks;Integrated Security=True";

using( SqlConnection conn = new SqlConnection( connectionString ) )
{
conn.Open();

Stopwatch watch = new Stopwatch();
watch.Start();

int count = 10000;
for( int i = 0; i < count; i++ )
{
using( SqlCommand cmd = new SqlCommand() )
{
cmd.Connection = conn;
cmd.CommandText = "uspGetManagerEmployees";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add( new SqlParameter( "ManagerID", 3 ) );

using( SqlDataReader reader = cmd.ExecuteReader( CommandBehavior.Default ) )
{
//while (reader.Read())
//{
// Console.WriteLine(reader[3]);
//}
}
}
}
watch.Stop();
Console.WriteLine(
"Running " + count +
" SP's using SQL ExecuteReader took " + watch.ElapsedMilliseconds + " ms");
Console.ReadLine();
}
}


2. DAAB (Data Access Application Block):


public static void SqlSpDAAB()
{
Database adventureWorksDataBase = DatabaseFactory.CreateDatabase( "AdventureWorks" );

Stopwatch watch = new Stopwatch();
watch.Start();

int count = 10000;
for( int i =0; i<count; i++ )
{
using( DBCommandWrapper sp_uspGetManagerEmployees =
adventureWorksDataBase.GetStoredProcCommandWrapper(
"uspGetManagerEmployees" ) )
{
sp_uspGetManagerEmployees.AddInParameter( "@ManagerID", DbType.Int32, 3 );
using( IDataReader dataReader =
adventureWorksDataBase.ExecuteReader( sp_uspGetManagerEmployees ) )
{
//DataSet results =
// adventureWorksDataBase.ExecuteDataSet( sp_uspGetManagerEmployees );
}
}
}
watch.Stop();
Console.WriteLine(
"Running " + count + " SP's using DAAB took " +
watch.ElapsedMilliseconds + " ms");
Console.ReadLine();
}


3. LINQ (using ORM layer produced by SQLMetal.exe - "c:\Program Files\LINQ Preview\Bin\SqlMetal.exe" /server:. /database:AdventureWorks /pluralize /sprocs /code:AdventureWorks.cs):



public static void LinqSPBench()
{
string connectionString =
"Data Source=localhost;Initial Catalog=AdventureWorks;Integrated Security=True";
AdventureWorks adventureWorks = new AdventureWorks( connectionString );

Stopwatch watch = new Stopwatch();
watch.Start();

int count = 10000;
for( int i =0; i<count; i++ )
{
StoredProcedureResult<UspGetManagerEmployeesResult> result =
adventureWorks.UspGetManagerEmployees( 3 );
IEnumerator<UspGetManagerEmployeesResult> enumerator = result.GetEnumerator();
//while( enumerator.MoveNext() )
//{
// Console.WriteLine( enumerator.Current.LastName );
//}
}
watch.Stop();
Console.WriteLine(
"Running " + count +
" SP's using LINQ took " + watch.ElapsedMilliseconds + " ms");
Console.ReadLine();
}


Here are timing results of this benchmarking test:
  • Running 10000 SP's using SQL ExecuteReader took 42806 ms

  • Running 10000 SP's using DAAB took 46443 ms

  • Running 10000 SP's using LINQ took 55136 ms




Results
So, comparing to the raw ADO.NET - DAAB is 8% slower and LINQ is 28% slower.
Comparing to DAAB - LINQ is 18% slower.
CPU usage intensity is about 2% average for raw ADO.NET vs. about 8% for DAAB vs. about 20% for LINQ - order of magnitude worse than raw ADO.NET (CPU graphs captured with perfmon below).


Fig. 1 ADO.NET - CPU usage


Fig. 2 DAAB - CPU usage



Fig. 3 LINQ - CPU usage

In overall - this performance overhead is quite disappointing. I still consider to use LINQ, as we have DAL/BLL servers in our architecture, which we may merely upgrade to faster hardware, while isolating thin-client computers from this performance overhead. I think it's a big challenge for the LINQ team to address its performance.