473,441 Members | 1,515 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,441 software developers and data experts.

Expressions in C#...

Hi!

I noticed a very funny behaviour when overloading operators
on objects in C#:

MyOjbects a,b,c,d;
....
//objects are created and initalized here...
....
b.Length = 100;
c.Length = 100;

for (i = 0; i < 10000; i++)
{
a = MyFun(2*c)*b;
}

Inside the overladed multiplication operator, I check:

if (Op1.Length == Op2.Length) throw....

And the exception triggers after about 100 iterations...!!?
Somehow the internal objects of the temporary variable
created by 2*c passed to MyFun are being
garbage collected (nullified) before the temporary variable is used for
the first time...

Are there some known issues with this?

Thanks!
Atmapuri
Sep 4 '06
66 2818
Atmapuri <di*@community.nospamwrote:
Only if objects are small and operations on them short.
No. How can the size of the object make any difference to the *time*
performance cost due to assignments etc (anything which changes the
reference count)?

Because large objects contain a lot of data. And a lot of data
needs a lot of processing. If you have a function call that works
on a lot of data, the overhead of reference count is basically zero.
The amount of data isn't always related to the amount of processing
required on it. You might need to do a huge amount of work on a few
numbers, or you might need to just increment every byte within a huge
array.
In case of garbage collector however, the overhead is growing
with the size of the object. In the example I posted, if you allocate
arrays of double and of Length, and also perform the
computations, you will noticed that garbage collector takes 75%
of time... and computation only 25%. I measured that with a
profiler..

Thats because if you allocate 100 000 objects of 8kB within
a tight loop the GC is working like crazy.... and reference
count is not affected by object size. Its overhead is constant
and for large objects basically zero.
Again, that entirely depends on what you do with the objects. If you
pass the object references around more than you deal with the actual
data, then it's *not* zero.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Sep 6 '06 #51
Hi!
Again, that entirely depends on what you do with the objects. If you
pass the object references around more than you deal with the actual
data, then it's *not* zero.
Yes. But that is "theory". In reality the cost of summing in an expression
together two double arrays of 1000 elements 100000x with reference counting
takes
100% of time for the math operation and 24KB of memory.

With garbage collection it takes cca 50MB of memory and 75% of CPU
is used by the garbage collector..

Regards!
Atmapuri

Sep 6 '06 #52
Atmapuri <di*@community.nospamwrote:
Again, that entirely depends on what you do with the objects. If you
pass the object references around more than you deal with the actual
data, then it's *not* zero.

Yes. But that is "theory". In reality the cost of summing in an expression
together two double arrays of 1000 elements 100000x with reference counting
takes
100% of time for the math operation and 24KB of memory.

With garbage collection it takes cca 50MB of memory and 75% of CPU
is used by the garbage collector..
You seem determined to work against garbage collection, so I suggest
you use a different platform. It's almost never a good idea to work in
an environment which is fundamentally opposed to the way you like to
work. If Delphi 2006 works well for you, why not use that? You could
always write most of your performance intensive code in that and use
P/Invoke to access it via .NET if you need to interoperate with other
..NET code.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Sep 6 '06 #53
Hi!
You seem determined to work against garbage collection, so I suggest
you use a different platform. It's almost never a good idea to work in
an environment which is fundamentally opposed to the way you like to
work.
It has nothing to do with the way I like to work, but with the way
equations are written in the books and that is how people like to
write them. It is my job to make them as happy as possible
when they work in .NET and therefore have to turn .NET/C#/GC inside out...
:)

I dont see C# as being too far from being able to handle
that efficiently. I think GC is good, but needs some tweaking.
How and what exactly... is left open... If the SafeHandle object
would be disposed by the compiler, when its reference count would fall to
zero
at the end of procedure it would already solve 90% of the issues..

And why stick with nondeterministic finalization if it is obvous
that you can also provide an alternative? Its not the question
of one or the other... Let the programmer choose depending
on the problem...

Thanks!
Atmapuri
Sep 6 '06 #54

"Atmapuri" <di*@community.nospamwrote in message
news:u5**************@TK2MSFTNGP02.phx.gbl...
| Hi!
|
| >Only if objects are small and operations on them short.
| >
| No. How can the size of the object make any difference to the *time*
| performance cost due to assignments etc (anything which changes the
| reference count)?
|
| Because large objects contain a lot of data. And a lot of data
| needs a lot of processing. If you have a function call that works
| on a lot of data, the overhead of reference count is basically zero.
|
| In case of garbage collector however, the overhead is growing
| with the size of the object. In the example I posted, if you allocate
| arrays of double and of Length, and also perform the
| computations, you will noticed that garbage collector takes 75%
| of time... and computation only 25%. I measured that with a
| profiler..
|
| Thats because if you allocate 100 000 objects of 8kB within
| a tight loop the GC is working like crazy.... and reference
| count is not affected by object size. Its overhead is constant
| and for large objects basically zero.
|
| Regards!
| Atmapuri
|
|

Well I ran your code on my box, with following code in Main and
MaxwellExpression1:

class Tester
{
static void Main()
{

int iters = 100000;
double res = MaxwellExpression1(iters);
Console.WriteLine(res);
}
static double MaxwellExpression1(int Iterations)
{
double a = 1;
Vector1 xx, res1, x1;
res1 = null;
x1 = new Vector1(1000);
int counter = Environment.TickCount;
for (int i = 0; i < Iterations; i++)
{
xx = x1 * x1;
res1 = Math.Sqrt(4 * 2 * a) * a * xx * Vec1Math.Exp(-0.5 * a *
xx);
}
int result = Environment.TickCount - counter;
return result;
}
}

this gives as result = 7520 ticks (7.5 secs.) with an "average % in GC" =
10.4.%
but again you have a 'bug' in you code...
[1] your Vec1 contructor looks like this:

public Vec1()
{
Length = 100;
}

and your Vector1 constructor:
public Vector1(int aLength)
{
Data = null;
Data = new Vec1();
Data.Length = aLength;
that means that you firts create an instance of Vec1 with a size of 100
elements, directly followed by a new instantiation now with Lenth size, the
first Vec1 instantiation is of no use, it only increases the GC pressure.

The sme test with Length = 100; removed resutls in:
result ~ 7440 ticks with 8.7% Average GC%.

Your 75% in GC is not normal, and makes me think that you are watching this
in a profiler, something you should never do, simply run your code and watch
the perf counters in Permon.
Also, allocating arrays of doubles are expensive, an array of 1000 or more
doubles ends on the Large Object Heap, this heap is never compacted and only
collected when a the GC does a full collect (this collects the whole heap
Gen0,1,2 and LOH), quite expensive.
To illustrate this you can run the test with 999 elements and watch the
difference.
On my box it gives result = ~5000 ticks with ~4% Average in GC.

Another remark is that you don't allocat100000 * 8K objects you actually
allocate 3 * 8K objects 100000 times (100000 iterations) + the 100000 times
800 bytes (see [1] above).

Other questions are- what OS and Framework are you running this on?
How did you compile the program? (debug/release/optimized?)

Willy.
Sep 6 '06 #55
Atmapuri <di*@community.nospamwrote:
You seem determined to work against garbage collection, so I suggest
you use a different platform. It's almost never a good idea to work in
an environment which is fundamentally opposed to the way you like to
work.

It has nothing to do with the way I like to work, but with the way
equations are written in the books and that is how people like to
write them. It is my job to make them as happy as possible
when they work in .NET and therefore have to turn .NET/C#/GC inside out...
:)

I dont see C# as being too far from being able to handle
that efficiently. I think GC is good, but needs some tweaking.
How and what exactly... is left open... If the SafeHandle object
would be disposed by the compiler, when its reference count would fall to
zero at the end of procedure it would already solve 90% of the issues..
Except that that requires reference counting...
And why stick with nondeterministic finalization if it is obvous
that you can also provide an alternative? Its not the question
of one or the other... Let the programmer choose depending
on the problem...
The "some types being reference counted" idea is addressed in the
article I posted a link to.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Sep 6 '06 #56
Willy Denoyette [MVP] <wi*************@telenet.bewrote:

<snip - interesting to see the different figures you got, Willy!>
Also, allocating arrays of doubles are expensive, an array of 1000 or more
doubles ends on the Large Object Heap, this heap is never compacted and only
collected when a the GC does a full collect (this collects the whole heap
Gen0,1,2 and LOH), quite expensive.
Just to clarify - did you really mean 1000 or more, or did you mean
10000 or more? I thought the LOH only kicked in around 80K, not 8K.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Sep 6 '06 #57

"Atmapuri" <di*@community.nospamwrote in message
news:eu****************@TK2MSFTNGP05.phx.gbl...
| Hi!
|
| You seem determined to work against garbage collection, so I suggest
| you use a different platform. It's almost never a good idea to work in
| an environment which is fundamentally opposed to the way you like to
| work.
|
| It has nothing to do with the way I like to work, but with the way
| equations are written in the books and that is how people like to
| write them. It is my job to make them as happy as possible
| when they work in .NET and therefore have to turn .NET/C#/GC inside out...
| :)
|
| I dont see C# as being too far from being able to handle
| that efficiently. I think GC is good, but needs some tweaking.
| How and what exactly... is left open... If the SafeHandle object
| would be disposed by the compiler, when its reference count would fall to
| zero
| at the end of procedure it would already solve 90% of the issues..
|

This is pure nonsense, but if you think it's all that simple , just grab a
copy of Rotor and implement your 'solution', we will all be happy to
investigate the results.
| And why stick with nondeterministic finalization if it is obvous
| that you can also provide an alternative? Its not the question
| of one or the other... Let the programmer choose depending
| on the problem...
|
| Thanks!
| Atmapuri
|
|

Your conclusions are based on false results, post a complete sample
(preferably a console sample)that compiles without us needing to tweak the
code to make it compile, post how you compiled (preferable from the command
line) and how you measure CPU and GC consumption, also post the test
environment HW (CPU memory and cache size(s)), OS and CLR versions.

Willy.


Sep 6 '06 #58

"Jon Skeet [C# MVP]" <sk***@pobox.comwrote in message
news:MP************************@msnews.microsoft.c om...
| Willy Denoyette [MVP] <wi*************@telenet.bewrote:
|
| <snip - interesting to see the different figures you got, Willy!>
|

Well, it is not surprising after all. There is a lot more to tell how this
code behaves on the CLR, ut I really have no time nor the ambition for the
moment.

| Also, allocating arrays of doubles are expensive, an array of 1000 or
more
| doubles ends on the Large Object Heap, this heap is never compacted and
only
| collected when a the GC does a full collect (this collects the whole
heap
| Gen0,1,2 and LOH), quite expensive.
|
| Just to clarify - did you really mean 1000 or more, or did you mean
| 10000 or more? I thought the LOH only kicked in around 80K, not 8K.
|

Yep, I really mean 1000 elements of doubles, 999 elements end on the
Generational heap.

The LOH is used to allocate objects (array's) of 85Kb, EXCEPT for arrays
of doubles, here the threshold is 8000 bytes (the array). I was highly
surprised myself and really thought it was a bug, the first time I saw this,
but after some digging with the debugger I noticed that a different
allocation path was chosen for double types array, so it looks like it's
intentional. This whole discussion reminds me that I should file a RFC on
this.

Willy.
Sep 6 '06 #59
Willy Denoyette [MVP] <wi*************@telenet.bewrote:

<snip>
Yep, I really mean 1000 elements of doubles, 999 elements end on the
Generational heap.

The LOH is used to allocate objects (array's) of 85Kb, EXCEPT for arrays
of doubles, here the threshold is 8000 bytes (the array). I was highly
surprised myself and really thought it was a bug, the first time I saw this,
but after some digging with the debugger I noticed that a different
allocation path was chosen for double types array, so it looks like it's
intentional. This whole discussion reminds me that I should file a RFC on
this.
Wow. That's really strange. Thanks for the heads up...

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Sep 7 '06 #60
Hi!
this gives as result = 7520 ticks (7.5 secs.) with an "average % in GC" =
10.4.% but again you have a 'bug' in you code...
Data.Length = aLength;
As you noticed the difference only made for 2.5% which is negligable.
The way I got 75% of difference is pinning down the arrays inside
each of the operators and passing the arrays to external SSE2
optimized dll. The cost of the math operation was reduced drastically
and GC cost landed at 75%. The relative cost of pinning down
was negligable...

Furthermore, I evaluated the formula also by reusing the
same arrays (without the expression) and the result was:

1.) Plain C# function working on one element at a time: 1600ms
2.) Expression in C# on 1000 elements with math in SSE2: 1200ms
3.) Expression in C# with 1000 elements with math in C#: 1900ms
4.) Formula evaluation without releasing arrays and without
pinning down: 300ms (no expression but methods passing pointers around
and arrays pinned down before the timer starts).

So, for the case #2, the overhead of GC as measured by
AQTime was 75%.
Also, allocating arrays of doubles are expensive, an array of 1000 or more
doubles ends on the Large Object Heap, this heap is never compacted and
only
Interesting. How would you fix the design to achieve 300ms of
the case #4 for expression in case #3?

The difference is huge. The application is very common and
practical and technology should server the people... and not
vice versa...

Thanks!
Atmapuri
Sep 7 '06 #61
Is there any easy way to tell if an object is on the LOH? Does it's going
straight to gen2 indicate this? (reliably) if so, I get the following (where
"Approx mem size" is Marshal.SizeOf(typeof(T)) * length)

Approx mem size, Type[array size]: initial generation
339948 Boolean[84987]: 0
339952 Boolean[84988]: 2
84987 Byte[84987]: 0
84988 Byte[84988]: 2
84986 Int16[42493]: 0
84988 Int16[42494]: 2
84984 Int32[21246]: 0
84988 Int32[21247]: 2
84984 Int64[10623]: 0
84992 Int64[10624]: 2
84984 Single[21246]: 0
84988 Single[21247]: 2
84976 Decimal[5311]: 0
84992 Decimal[5312]: 2
7992 Double[999]: 0
8000 Double[1000]: 2

Sep 7 '06 #62

"Marc Gravell" <ma**********@gmail.comwrote in message
news:uX**************@TK2MSFTNGP04.phx.gbl...
| Is there any easy way to tell if an object is on the LOH? Does it's going
| straight to gen2 indicate this? (reliably) if so, I get the following
(where
| "Approx mem size" is Marshal.SizeOf(typeof(T)) * length)
|

No, small objects always start their life in Gen0 while large objects (>85K
and >8K for doubles) are allocated from the LOH. Objects can never start
their life in Gen1 or Gen2.
One sure way to know the location of your managed objects in the heap is by
loading the SOS.DLL extention in the debugger.

| Approx mem size, Type[array size]: initial generation
| 339948 Boolean[84987]: 0
| 339952 Boolean[84988]: 2
| 84987 Byte[84987]: 0
| 84988 Byte[84988]: 2
| 84986 Int16[42493]: 0
| 84988 Int16[42494]: 2
| 84984 Int32[21246]: 0
| 84988 Int32[21247]: 2
| 84984 Int64[10623]: 0
| 84992 Int64[10624]: 2
| 84984 Single[21246]: 0
| 84988 Single[21247]: 2
| 84976 Decimal[5311]: 0
| 84992 Decimal[5312]: 2
| 7992 Double[999]: 0
| 8000 Double[1000]: 2
|

Looks like Gen 2 and LOH are treated as equal here, don't know how you
obtained this figure, but you should take care what function you use to get
at these counters, most of them initiate a GC defeating the purpose of your
investigation.

Willy.


Sep 7 '06 #63
Marc Gravell wrote:
Is there any easy way to tell if an object is on the LOH?
A highly reliable way is by debugging the application, examining the
state of the GC heap with the SOS functions. That'll tell you the
segments used for gen0/1, gen2 and LOH.

As a matter of interest: the code generated for allocating an array of
1000 doubles is something like this:

---8<---
00ca009c bae8030000 mov edx,3E8h
00ca00a1 b95a9d1579 mov ecx,offset mscorlib_ni+0x99d5a
(79159d5a)
00ca00a6 e8d521c7ff call 00912280 (JitHelp:
CORINFO_HELP_NEWARR_1_ALIGN8)
--->8---

3E8h = 1000.

Peeking into 00912280, it starts like this:

---8<---
00912280 51 push ecx
00912281 52 push edx
00912282 8b4902 mov ecx,dword ptr [ecx+2]
00912285 81fae8030000 cmp edx,3E8h
0091228b 7367 jae 009122f4
0091228d 0fb701 movzx eax,word ptr [ecx]
--->8---

This CORINFO_HELP_NEWARR_1_ALIGN8 isn't used for longs. For that,
CORINFO_HELP_NEWARR_1_VC is used.

-- Barry

--
http://barrkel.blogspot.com/
Sep 7 '06 #64
I was simply asking the CLR via:
T[] data = new T[length];
int gen = GC.GetGeneration(data);

This was purely out of interest, mind. Your point about kicking off a GC is
well taken - this wasn't production code, just a "hmm that's interesting...
I wonder what that looks like..." piece of throw-away code in responnse to
your 1000 double claim, which appears to be entirely accurate. Interesting
that the large objects (appear to) report as gen2, though. I guess I was
actually expecting to see -1 or "GC.MaxGeneration + 1" (if you see what I
mean).

So far, I've reseisted the urge to fight the GC; I'm generally of the
opinion "leave it [the hell] alone", but it can sometimes be illuminating to
peek under the covers a little bit. Code monkey voyeurism.

Thanks both Willy and Barry - some interseting info.

Marc
Sep 7 '06 #65
Hi!
No, small objects always start their life in Gen0 while large objects
(>85K
and >8K for doubles) are allocated from the LOH. Objects can never start
I also noticed a small dip when the double array exceeded 1000 elements.
But that was only about 15% or so and in compare to to the total cost
of GC almost irrelevant..

Thanks!
Atmapuri

Sep 7 '06 #66

--
Billy
"Atmapuri" wrote:
Hi!

I noticed a very funny behaviour when overloading operators
on objects in C#:

MyOjbects a,b,c,d;
....
//objects are created and initalized here...
....
b.Length = 100;
c.Length = 100;

for (i = 0; i < 10000; i++)
{
a = MyFun(2*c)*b;
}

Inside the overladed multiplication operator, I check:

if (Op1.Length == Op2.Length) throw....

And the exception triggers after about 100 iterations...!!?
Somehow the internal objects of the temporary variable
created by 2*c passed to MyFun are being
garbage collected (nullified) before the temporary variable is used for
the first time...

Are there some known issues with this?

Thanks!
Atmapuri
Nov 2 '06 #67

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

72
by: Raymond Hettinger | last post by:
Peter Norvig's creative thinking triggered renewed interest in PEP 289. That led to a number of contributors helping to re-work the pep details into a form that has been well received on the...
1
by: Kenneth McDonald | last post by:
I'm working on the 0.8 release of my 'rex' module, and would appreciate feedback, suggestions, and criticism as I work towards finalizing the API and feature sets. rex is a module intended to make...
5
by: jens | last post by:
I was wondering if anyone could tell be whether it's possible to instruct preg_match not to save subexpressions matches under 0, 1...n keys when using named subexpressions; I.e instruct preg_named...
7
by: mark | last post by:
Access 2000: I creating a report that has a record source built by the user who selects the WHERE values. An example is: SELECT * FROM CHARGELOG WHERE STDATE Between #10/27/2003# And...
2
by: Sehboo | last post by:
Hi, I have several regular expressions that I need to run against documents. Is it possible to combine several expressions in one expression in Regex object. So that it is faster, or will I...
4
by: Együd Csaba | last post by:
Hi All, I'd like to "compress" the following two filter expressions into one - assuming that it makes sense regarding query execution performance. .... where (adate LIKE "2004.01.10 __:30" or...
56
by: Luke Matuszewski | last post by:
I am designing the library, which will hidden all its functions within singleton object ... So for clients they will use it like . eg. system.getElementWithId('ruler'); At library side, i...
2
by: aarklon | last post by:
Hi all, Recently i was reading a c book which contained a section called C pitfalls. it had a paragraph on the following lines:- ...
1
by: Allan Ebdrup | last post by:
I have a dynamic list of regular expressions, the expressions don't change very often but they can change. And I have a single string that I want to match the regular expressions against and find...
3
by: RobG | last post by:
There has been a discussion on the iPhone web development group where an opinion has been expressed that function expressions are bad for performance and can be avoided by using function...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.