6

I'm about to write a set of methods on a server application that take messages received from TCP socket, encode/decode them, and encrypt/decrypt them.

Considering messages are defined as special types, each with its own set of properties and total length, I need to opt for one of the following solutions: 1. Make methods that use generics, such as Encode<T>(...) where T : IMessage and then implement encoder/decoder for each type of message and have ResolveEncoder<T> that would pick encoder for wanted message or 2. Make method that uses any type of message, as long as it implements IMessage, such as Encode(IMessage message), and use System.Reflection to determine everything I need to know about it.

I'm more interested in doing solution #2 as it makes my code 30 times smaller. However, I'm worried whether constant reflection of properties will hit the performance. Now as I'm time limited to finish the project, I can't really "experiment".

I would be grateful for any personal experiences or links with benchmarks on how performance falls with either of solutions.

Ry-
  • 218,210
  • 55
  • 464
  • 476
Admir Tuzović
  • 10,997
  • 7
  • 35
  • 71
  • 1
    Reflection will definitely decrease the performance. Post your generic code to see if maybe it can be reduced in ways you didn't think of, which will be it more tolerable. – SimpleVar Apr 18 '12 at 03:37
  • 2
    Don't prematurely optimize. Yes reflection is slower than native code, but if its not the ACTUAL bottleneck then its irrelevant. It is possible to use Reflection with Generics in some scenarios to cache your reflected methods upon first execution to improve performance. – cfeduke Apr 18 '12 at 03:40

6 Answers6

6

Now as I'm time limited to finish the project, I can't really "experiment".

Then your real constraint is not performance, but rather which can you code and test and debug in the given time constraint. Supposing that your claim that the reflection version is 30x smaller, it sounds like that's what you should lean towards.

However, five points:

  1. I doubt that 30x is the right estimate, it's probably much smaller.
  2. Reflection will be less performant than using generics. There's no question about that.
  3. "However, I'm worried whether constant reflection of properties will hit the performance." You can mitigate some of this by aggressively caching the PropertyInfos and what not that you load by reflection.
  4. You can do something akin to reflection using expression trees, and you'll see a significant performance boost from doing so. Here's a blog post that will push you in the right direction on this issue. However, if you're not already familiar with working with expression trees, given your time constraint, it is a risk to take on coding, testing and debugging using a concept that you're not familiar with.
  5. Are you sure that that this is even a performance bottleneck? It wouldn't shock me if network latency dominates here, and you're prematurely optimizing.
jason
  • 236,483
  • 35
  • 423
  • 525
  • It really is 30x reduction. I've tried to post response to my topic with a code, but stackoverflow won't let me for the next 6 hours. I don't think expression trees will help me much. I'm going only 1 level in depth, so there's no usual optimization that's done with binary trees (reducing double passing over same property when scanning it and it's children later on). Or am I wrong on that one? I was thinking that TCP might be bigger bottleneck, but I'm not yet able to estimate that yet either... – Admir Tuzović Apr 18 '12 at 05:12
  • 2
    Yes, you're wrong about expression trees. Look, if network latency is the bottleneck as I said already in my answer, then I'm not sure why you're losing sleep over this given that your real constraint is a project deadline. Code it in a way that you know how and can meet your deadline. If the performance does not matter because this isn't a bottleneck, why are you worrying about performance? **Moreover, you should never be guessing when working on performance anyway.** – jason Apr 18 '12 at 11:33
4

Reflection can be fast enough. But need to be implemented correctly.

Reflection Performance -

Fast and Light Functions

typeof
Object.GetType
typeof == Object.GetType
Type equivalence APIs (including typehandle operator overloads)
get_Module
get_MemberType
Some of the IsXX predicate APIs
New token/handle resolution APIs in the .NET Framework 2.0

Costly Functions

GetXX  APIs (MethodInfo, PropertyInfo, FieldInfo, and so on)
GetCustomAttributes
Type.InvokeMember
Invoke APIs (MethodInfo.Invoke, FieldInfo.GetValue, and so on)
get_Name (Name property)
Activator.CreateInstance

Source - Dodge Common Performance Pitfalls to Craft Speedy Applications

MethodInfo can be speed up - Improving performance reflection , what alternatives should I consider

Good links:
How costly is .NET reflection?
http://www.codeproject.com/Articles/18450/HyperDescriptor-Accelerated-dynamic-property-acces

Community
  • 1
  • 1
A G
  • 21,087
  • 11
  • 87
  • 112
0

Generics are bound at compile time, and will be as performant as normal types. Reflection will come at a huge cost.

CassOnMars
  • 6,153
  • 2
  • 32
  • 47
0

If performance is your concern, consider generating DynamicMethod for each type of message. Code that will generate dynamic method will be the same for all message types (just like it would be with reflection), but once dynamic method will be generated, you will have no performance hit of reflection. Disadvantage is that code is harder to write and more difficult do debug, so this might not be best option, if you are in time pressure.

Ňuf
  • 6,027
  • 2
  • 23
  • 26
0

One approach which is often helpful is to use Reflection with static generic classes. This is the approach taken by things like EqualityComparer<T>.Default. Essentially, what happens it that for any particular type T, the first reference to EqualityComparer<T>.Default will have to use Reflection to determine whether type T implements IComparable<T> and construct either an implementation of IEqualityComparer<T> that operates on an unconstrained T and uses Object.Equals, or else one which constraints T to IEqualityComparer<T> and uses IEqualityComparer<T>.Equals. Using reflection for that purpose is somewhat slow, but it only needs to be done once for any particular type T. Once the implementation is constructed, it will be stored in a static field of EqualityComparer<T>, so any future requests to get the default comparer for that type will be able to simply use the earlier-constructed IEqualityComparer<T> implementation.

This approach combines the speed of generics with the flexibility to do things for which generics alone are not quite sufficient.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

Current code does this:

protected void Decode<T>(Action<BinaryReader> action, byte[] buffer) {...}

Creates MemoryStream stream. Creates BinaryReader reader on the stream. And then performs action(reader).

Now let's assume we have HelloMessage.cs that only has two properties, an byte Id and int Descriptor. The Decode method of decoder class HelloMessageCodec.cs looks like this:

public HelloMessage Decode(byte[] buffer)
{
    var message = new HelloMessage();
    Decode<HelloMessage>(reader => 
        { message.Id = reader.ReadByte();
          message.Descriptor = reader.ReadInt32();
        }, buffer);
    return message;
}

This is really nicely done, however, there is about 15 types of messages, and each type has it's own xxxMessageCodec.cs with Decode and Encode implementations, each having properties manually passed within Action delegate.

Now I have to do encryption, which means, should I be following this pattern, I'd have to build 15 different message encryptors.

I've already reworked all codec code to solution #2 (with reflection), and instead of having 15 x 2 functions, I only have 2 functions + no underlying levels just two functions that process messages by their properties and invoking ReadByte, ReadInt32,... depending on the typeof property.

So, I've done the work, but I don't have enough time to test performance as I have to move on with working on encryption. I have to pick one of two solutions to carry on, and I have sleeping issue whether the smaller solution (the reflection one) would side-kick me back in the face later on :)

Admir Tuzović
  • 10,997
  • 7
  • 35
  • 71