Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / CLR / September 2006

Tip: Looking for answers? Try searching our database.

Reflection in 1.1 and 2.0

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Eric - 12 Sep 2006 20:50 GMT
I have here a little sample which does reflection on a class.
I striked me that 1.1 is nearly two times faster.
Could me somebody give a hint why?

For the sample: Compile it via "csc.exe /optimize+ Program.cs" with the two
compiler for 1.1 and 2.0.

Thanks
Eric

namespace ComponentProfiling
{
   using System;
   using System.ComponentModel;
   using System.Reflection;

   public sealed class Person
   {
       private string _firstName;
       private string _lastName;
       private int _age;

       public string FirstName
       {
           get { return _firstName; }
           set { _firstName = value; }
       }

       public string LastName
       {
           get { return _lastName; }
           set { _lastName = value; }
       }

       public int Age
       {
           get { return _age; }
           set { _age = value; }
       }
   }
   
    class Program
    {
       static void Main(string[] args)
       {
           Console.WriteLine("Runtime version = {0}", Environment.Version);

           int iterations = args.Length > 0 ? int.Parse(args[0]) : 1000000;
           Console.WriteLine("Iterations = {0}", iterations.ToString("N0"));

           Person person = new Person();

           PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(Person));
           
           string test1 = typeof(TypeDescriptor).Name;
           
           TimeSpan span1 = Measure(iterations, person,
               properties["FirstName"],
               properties["LastName"],
               properties["Age"]);

           Console.WriteLine("{0} = {1}", test1, span1);
           
           string test2 = typeof(MyPropertyDescriptor).Name;
           
           TimeSpan span2 = Measure(iterations, person,
               new
MyPropertyDescriptor(typeof(Person).GetProperty("FirstName")),
               new MyPropertyDescriptor(typeof(Person).GetProperty("LastName")),
               new MyPropertyDescriptor(typeof(Person).GetProperty("Age")));

           Console.WriteLine("{0} = {1}", test2, span2);
           
           Console.WriteLine("{0}/{1} = {2:P}", test1, test2,
span1.TotalMilliseconds / span2.TotalMilliseconds);

           Console.WriteLine("Press ENTER to end.");
           Console.ReadLine();
       }

       static TimeSpan Measure(int iterations, Person person, params
PropertyDescriptor[] properties)
       {
           DateTime start;
           start = DateTime.Now;
           
           for (int i = 0; i < iterations; i++)
           {
               properties[0].SetValue(person, "John");
               properties[1].SetValue(person, "Doe");
               properties[2].SetValue(person, 22);                
           }
           
           return DateTime.Now - start;
       }

       public sealed class MyPropertyDescriptor : PropertyDescriptor
       {
           private readonly PropertyInfo _property;

           public MyPropertyDescriptor(PropertyInfo property)
               :
               base(property.Name, null)
           {
               _property = property;
           }

           public override bool CanResetValue(object component)
           {
               return false;
           }

           public override object GetValue(object component)
           {
               return _property.GetValue(component, null);
           }

           public override void ResetValue(object component)
           {
               throw new NotSupportedException();
           }

           public override void SetValue(object component, object value)
           {
               _property.SetValue(component, value, null);
           }

           public override bool ShouldSerializeValue(object component)
           {
               return false;
           }

           public override Type ComponentType
           {
               get { return _property.ReflectedType; }
           }

           public override bool IsReadOnly
           {
               get { return !_property.CanWrite; }
           }

           public override Type PropertyType
           {
               get { return _property.PropertyType; }
           }
       }
   }
}
Jeffrey Tan[MSFT] - 13 Sep 2006 10:27 GMT
Hi Eric,

Yes, I can reproduce the performance issue you reported.

Based on the research, I find that the IL generated by .Net1.1 and .Net2.0
C# compiler is the same. So the problem should be in .Net FCL or CLR.

I will try to contact our CLR team to confirm this issue. I will feedback
here ASAP. Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
Jeffrey Tan[MSFT] - 18 Sep 2006 04:09 GMT
Hi Eric,

Sorry for letting you wait.

I am trying to contact our CLR team regarding this performance issue these
days. After several round of emails, I finally find the correct dev team
that is responsible for System.ComponentModel.TypeDescriptor namespace
changes. The dev team is current working on this issue now.

I will feedback any progress here ASAP. Thanks for your patient.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
Atif Aziz - 18 Sep 2006 10:05 GMT
Hi Jeffrey,

The problem is in the System.Reflection namespace and nothing to do with
System.ComponentModel or TypeDescriptor. Here's a bit of background. I
originally wrote the sample and pointed out the problem to a distribution
list within our company. Eric kindly posted the problem here with the sample
included verbatim. However, the sample is a little too elaborate to prove the
point. Originally, I was testing the relative performance of doing a
property-set via PropertyDescriptor.SetValue versus PropertyInfo.SetValue. Of
course, PropertyDescriptor.SetValue internally eventually calls
PropertyInfo.SetValue but goes through a lot more hoops (such as engaging
with  IComponentChangeService if on e is around) before just doing that and I
wanted to measure the cost of those hoops. My sample code creates its own
variant of PropertyDescriptor implementation that simply calls
PropertyInfo.SetValue. The idea here was to include the cost of v-table
dispatch across both measurements. Anyhow, I've reworked the sample to show
that the performance issue exists solely within the System.Reflection space:

/* ============ C# SAMPLE START ============ */

using System;
using System.Reflection;

public class Person
{
   private string _firstName;
   private string _lastName;
   private int _age;

   public string FirstName
   {
       get { return _firstName; }
       set { _firstName = value; }
   }

   public string LastName
   {
       get { return _lastName; }
       set { _lastName = value; }
   }

   public int Age
   {
       get { return _age; }
       set { _age = value; }
   }
}

class Program
{
   static void Main(string[] args)
   {
       Console.WriteLine("Runtime version = {0}", Environment.Version);

       int iterations = args.Length > 0 ? int.Parse(args[0]) : 1000000;
       Console.WriteLine("Iterations = {0}", iterations.ToString("N0"));

       Person person = new Person();

       PropertyInfo fn = typeof(Person).GetProperty("FirstName");
       PropertyInfo ln = typeof(Person).GetProperty("LastName");
       PropertyInfo age = typeof(Person).GetProperty("Age");

       DateTime start = DateTime.Now;

       for (int i = 0; i < iterations; i++)
       {
           fn.SetValue(person, "John", null);
           ln.SetValue(person, "Doe", null);
           age.SetValue(person, 42, null);
       }

       Console.WriteLine(DateTime.Now - start);
   }
}

/* ============= C# SAMPLE END ============= */

Assuming the sample is saved as the file Program.cs, compile using:

csc /optimize+ Program.cs

Needless to say, the csc.exe should come from the version of the framework
against which you wish to make performance test. I would recommend compiling
the program with v1.0.3705. Then run it against the various versions using
the following batch to see the differences:

@REM ========== BATCH START ============

@echo off

setlocal
set vers=%1
if "%1"=="" set vers=v1.0.3705 v1.1.4322 v2.0.50727
for %%i in (%vers%) do call :run %%i
goto exit

:run
set COMPLUS_Version=%1
program.exe

:exit

REM ============ BATCH END =============

If you run this against 1.x and 2.0, you'll find that 2.0 is consistently
twice as slow as 1.x for setting a property. On my machine, the results are:

Runtime version = 1.0.3705.6018
Iterations = 1,000,000
00:00:02
Runtime version = 1.1.4322.2032
Iterations = 1,000,000
00:00:02.1093750
Runtime version = 2.0.50727.42
Iterations = 1,000,000
00:00:04.5468750

- Atif

> Hi Eric,
>
[quoted text clipped - 28 lines]
> ==================================================
> This posting is provided "AS IS" with no warranties, and confers no rights.
Stuart Carnie - 18 Sep 2006 23:47 GMT
I'd suggest you take advantage of Lightweight Code Generation (LCG).

I knocked a v2.0 sample together using your example.

NO LCG:
Runtime version = 2.0.50727.42
Iterations = 1,000,000
00:00:04.8898738

USING LCG:
Runtime version = 2.0.50727.42
Iterations = 1,000,000
00:00:00.0937632

- - - - - COPY BELOW - - - - - -

#define LCG
// remove this define to use the old PropertyInfo way.

using System;
using System.Reflection;
using System.Reflection.Emit;

public class Person
{
    private string _firstName;
    private string _lastName;
    private int _age;

    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }

    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }

    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Runtime version = {0}", Environment.Version);

        int iterations = args.Length > 0 ? int.Parse(args[0]) : 1000000;
        Console.WriteLine("Iterations = {0}", iterations.ToString("N0"));

        Person person = new Person();
        person.FirstName = "Hello";

        PropertyInfo fn = typeof(Person).GetProperty("FirstName");
        PropertyInfo ln = typeof(Person).GetProperty("LastName");
        PropertyInfo age = typeof(Person).GetProperty("Age");

        DateTime start = DateTime.Now;

#if LCG
        FastSetterDelegate<string> fns = GenerateFastSetter<string>(fn);
        FastSetterDelegate<string> lns = GenerateFastSetter<string>(ln);
        FastSetterDelegate<int> ages = GenerateFastSetter<int>(age);

        for (int i = 0; i < iterations; i++)
        {
            fns(person, "John");
            lns(person, "Doe");
            ages(person, 42);
        }
#else
        for (int i = 0; i < iterations; i++)
        {
            fn.SetValue(person, "John", null);
            ln.SetValue(person, "Doe", null);
            age.SetValue(person, 42, null);
        }
#endif
        Console.WriteLine(DateTime.Now - start);
    }

    static FastSetterDelegate<T> GenerateFastSetter<T>(PropertyInfo pi)
    {
        DynamicMethod dm = new DynamicMethod(pi.Name, null, new Type[] { typeof(object), typeof(T) }, typeof(Program).Module);

        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.EmitCall(OpCodes.Callvirt, pi.GetSetMethod(), null);
        il.Emit(OpCodes.Ret);

        return (FastSetterDelegate<T>)dm.CreateDelegate(typeof(FastSetterDelegate<T>));
    }

    delegate void FastSetterDelegate<T>(object obj, T value);
}

> I have here a little sample which does reflection on a class.
> I striked me that 1.1 is nearly two times faster.
[quoted text clipped - 146 lines]
>     }
> }
Atif Aziz - 19 Sep 2006 10:44 GMT
Hi Stuart,

I'm well aware of LCG, and in fact, I'm not even sure why you need it in
2.0. You can get the same results without LCG using purely delegates. Define
the delegate like this:

delegate void ValueSetter<TContainer, TValue>(TContainer container, TValue
value);

Then create it like this:

ValueSetter<Person, string> fnSetter = (ValueSetter<Person, string>)
Delegate.CreateDelegate(stringSetterType, fn.GetSetMethod());

And use it as you did:

fnSetter(person, "John");

Less code and doesn't require LCG or IL magic and is even strongly typed on
the container type (Person). Incidentally, this version performs 145 times
faster than using reflection.

Anyhow I'm interested in understanding where the performance issues are
occurring. Generally, things should get faster with each version of the
framework, but going twice as slow as previous version mandates at least a
warning or notice in the documentation. Don't you think? The problem is that
we can't upgrade all applications to 2.0 overnight. Moreover, loading 1.x
assemblies is 2.0 apps will upgrade automatically at runtime but suffer twice
the performance if they're internally using reflection.

BTW, have you tried making Person private? The performance slows down by a
factor of 4 times than when its public. Here the behavior is fairly identical
across 1.x and 2.0. If it's a CAS issue, then I can't imagine why I'm being
more taxed for accessing private types more than public ones! It's also quite
a tax for a fully trusted application.

> I'd suggest you take advantage of Lightweight Code Generation (LCG).
>
[quoted text clipped - 250 lines]
> >     }
> > }
Stuart Carnie - 19 Sep 2006 21:19 GMT
Sure thing, your particular example is certainly better :).  What I presented was just an example.  I'm sure you are aware that
LCG is significantly more powerful than that.

How about a generic 'copy public read/write properties' delegate.  The delegate is strongly typed, and you can generate one for
any object that has public, read/write properties.

Runtime version = 2.0.50727.42
Iterations = 1,000,000
00:00:00.0625004
FirstName: John, LastName: Doe, Age: 64   

Cheers,

Stu

/* ********** COPY BELOW ********** */

using System;
using System.Reflection;
using System.Reflection.Emit;

public class Person
{
    private string _firstName;
    private string _lastName;
    private int _age;

    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }

    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }

    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
}

delegate void CopyPublicPropertiesDelegate<T>(T source, T dest);

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Runtime version = {0}", Environment.Version);

        int iterations = args.Length > 0 ? int.Parse(args[0]) : 1000000;
        Console.WriteLine("Iterations = {0}", iterations.ToString("N0"));

        Person srcperson = new Person();
        srcperson.FirstName = "John";
        srcperson.LastName = "Doe";
        srcperson.Age = 64;

        Person dstperson = new Person();

        DateTime start = DateTime.Now;

        CopyPublicPropertiesDelegate<Person> cd = GenerateCopyDelegate<Person>();

        for (int i = 0; i < iterations; i++)
        {
            cd(srcperson, dstperson);
        }

        Console.WriteLine(DateTime.Now - start);

        Console.WriteLine("FirstName: {0}, LastName: {1}, Age: {2}", dstperson.FirstName, dstperson.LastName, dstperson.Age);
    }

    static CopyPublicPropertiesDelegate<T> GenerateCopyDelegate<T>()
    {
        Type type = typeof(T);

        DynamicMethod dm = new DynamicMethod(type.Name, null, new Type[] { typeof(T), typeof(T) }, typeof(Program).Module);

        ILGenerator il = dm.GetILGenerator();

        PropertyInfo[] props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (PropertyInfo prop in props)
        {
            if (prop.CanRead && prop.CanWrite)
            {
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Ldarg_0);
                il.EmitCall(OpCodes.Callvirt, prop.GetGetMethod(), null);
                il.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), null);
            }
        }
        il.Emit(OpCodes.Ret);

        return (CopyPublicPropertiesDelegate<T>)dm.CreateDelegate(typeof(CopyPublicPropertiesDelegate<T>));
    }

}

> Hi Stuart,
>
[quoted text clipped - 286 lines]
>>>     }
>>> }
Atif Aziz - 19 Sep 2006 21:42 GMT
Hi Stuart,

But once more, why use LCG? Why not just use the standard delegate
infrastructure to get at the same result? Also you need permissions to emit
code with the LCG version whereas using delegates does not appear to require
permissions beyond those for public reflection and so you can have a fast
solution even in low trust environments.

> Sure thing, your particular example is certainly better :).  What I presented was just an example.  I'm sure you are aware that
> LCG is significantly more powerful than that.
[quoted text clipped - 289 lines]
> >>>
> >>>             int iterations = args.Length > 0 ? int.Parse(args[0]) : 1000000;
Stuart Carnie - 20 Sep 2006 01:03 GMT
It would not be as efficient in the 'copy' case, as I've emitted 4 IL instructions to copy from the source object to the
destination.

Correct me, if I'm wrong, but you would have to create two delegates per property.  One to get the value from the source and a
second to set the value on the destination.

I'm making one delegate call, and then a callvirt to each get/set.

A class with 6 public properties (6 set_* and 6 get_*), 1,000,000 iterations

-----------------------------------------------------
| Method  |   Calls (per iteration)   | Total calls |
-----------------------------------------------------
|         | Delegate  | get_* | set_* |             |
-----------------------------------------------------
| LCG     | 1         | 6     | 6     | 13,000,000  |
-----------------------------------------------------
| Delegate| 12        | 6     | 6     | 24,000,000  |
-----------------------------------------------------

Your delegate solution is is still significantly more efficient that reflection.  I was just providing another alternative.

Cheers,

Stu

> Hi Stuart,
>
[quoted text clipped - 297 lines]
>>>>>
>>>>>             int iterations = args.Length > 0 ? int.Parse(args[0]) : 1000000;
Atif Aziz - 20 Sep 2006 09:41 GMT
Yes, there's nothing that's going to beat code that's custom-tailored to a
particular scenario. In your case, it's good for copying public properties
from one instance to another of the same type but then that's where it ends.
I didn't mean to get into a discussion about the best and fastest way to set
properties going from 1.x to 2.0. My original code was setting properties
because that seemed simple enough to illustrate the drastic change in
reflection-invocation performance that I haven't seen reported anywhere so
far. Internally, all invocations-via-reflection go through MethodBase.Invoke
(as you probably know) so one could illustrate the same problem with invoking
a method rather than setting properties. Anyway, for better or worse, it's
officially a bug now, according to MS.

> It would not be as efficient in the 'copy' case, as I've emitted 4 IL instructions to copy from the source object to the
> destination.
[quoted text clipped - 286 lines]
> >>>>>
> >>>>> namespace ComponentProfiling
Stuart Carnie - 20 Sep 2006 17:32 GMT
Totally understand.  I didn't mean to hijack the thread.

I seem to have been advocating LCG lately :)

I had a scenario where I was constructing lots of objects, which are loaded dynamically, and Activator.CreateInstance is
notoriously slow, so I created a delegate from a DynamicMethod to call the constructor directly.  Much better :)

Good work on finding the issue - and glad to see it has been raised as a bug.

Cheers,

Stu

> Yes, there's nothing that's going to beat code that's custom-tailored to a
> particular scenario. In your case, it's good for copying public properties
[quoted text clipped - 298 lines]
>>>>>>>
>>>>>>> namespace ComponentProfiling
Stuart Carnie - 19 Sep 2006 21:24 GMT
That's a very interesting observation - private types slows down by a factor of 4?

I definitely agree, performance characteristics would be useful, especially for something as fundamental as reflection.

I did some similar profiling exercises with ADO.NET and the DataSet in v2.0.  Whilst the performance was a huge improvement, the
RBTree and some other structures used internally to manage the indexes increased the memory requirements by 2 times!  So, for
those loading large data sets (that contributed a significant percentage of their applications working set) effectively doubled!

Cheers,

Stu

> Hi Stuart,
>
[quoted text clipped - 286 lines]
>>>     }
>>> }
Atif Aziz - 19 Sep 2006 21:45 GMT
Yeah, I'm hoping Jeffrey or someone from the CLR team will get back to us on
this to shed some light about what has changed internally between 1.x and 2.0.

> That's a very interesting observation - private types slows down by a factor of 4?
>
[quoted text clipped - 290 lines]
> >>>                 get { return !_property.CanWrite; }
> >>>             }
Jeffrey Tan[MSFT] - 20 Sep 2006 07:26 GMT
Hi Eric,

The CLR team finally filed this issue as a bug in the internal database
now. Thank you for reporting this issue. If there is any interesting
information available I will come back and post here. Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
Atif Aziz - 20 Sep 2006 09:44 GMT
Hi Jeffrey,

Does this also bug also apply to my comment about performance degrading by
nearly 4 times if your target public members of a private type, and from
within the same assembly that resides in a trusted zone?

- Atif

> Hi Eric,
>
[quoted text clipped - 23 lines]
> ==================================================
> This posting is provided "AS IS" with no warranties, and confers no rights.
Eric - 21 Sep 2006 08:39 GMT
Hi Jeffrey,

Thanks for taking care about the Issue.
Could you give us some more information about the bug?
Where's the problem?

Eric

> Hi Eric,
>
[quoted text clipped - 23 lines]
> ==================================================
> This posting is provided "AS IS" with no warranties, and confers no rights.
Jeffrey Tan[MSFT] - 21 Sep 2006 10:23 GMT
Hi Eric,

Thanks for the feedback.

Sorry, since the bug is just filed, our CLR engineer is still working on
the bug now and I did not see much information in the bug record currently.
It may require some time for the engineer to figure out the root cause,
anyway, I will check the bug after some time and feedback interesting
information here.

Thanks for the understanding.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.