Expressions in C#...

A

Atmapuri

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
 
A

Atmapuri

Hi!

The fix for the problem is to write the expression as:

d = 2*c;
a = MyFun(d)*b;

That is to explicitely allocate the temporary variable....
But of course, that is not really a fix, it just points to
the problem...

Thanks!
Atmapuri
 
M

Marc Gravell

Sounds curious; can you reproduce this in simple (yet complete), postable
code?

Is it possible that MyFun is return an MyOjbects [sic] instance that
genuinely has the same length as b (100)? Equally, depending on whether
these are structs or classes, it might be that you are (in the non-shown
code) accidentally changing properties of the operands, and this behaviour
is a side-effect?

Marc
 
N

Nicholas Paldino [.NET/C# MVP]

That's actually very, very wrong. Something else is going on here. The
temporary variable that is created as a result of calling 2 * c isn't being
GC'ed because the reference is held on the stack (through the parameter)
when you pass it to MyFun.

It is most definitely something else. Without seeing the code for the
class where the operator is overloaded, as well as the code for MyFun, it's
impossible to say.
 
A

Atmapuri

Hi!
Sounds curious; can you reproduce this in simple (yet complete), postable
code?

Could be a tough job. The garbage collector is not deterministic....
Is it possible that MyFun is return an MyOjbects [sic] instance that
genuinely has the same length as b (100)? Equally, depending on whether
these are structs or classes, it might be that you are (in the non-shown
code) accidentally changing properties of the operands, and this behaviour
is a side-effect?

The code inside the loop works for 100 iterations and than fails?
This is C#. It should be impossible especially because the next
iteration does not depend upon the previous...

Struct works fine and classes fail...

Regards!
Atmapuri
 
A

Atmapuri

Hi!

It would be very difficult for me to post the code.
Can you think of anything why this would occur
considering that:

It works with struct but not with classes. I noticed
that the compiler copies the struct before passing it
to the function.

MyObjects has a finalizer declared. And the only
way I can imagine for the Length to be set to zero
is if that finalizer has been called on the temporary
object created by 2*c, before the object was used.

Thanks!
Atmapuri



Nicholas Paldino said:
That's actually very, very wrong. Something else is going on here.
The temporary variable that is created as a result of calling 2 * c isn't
being GC'ed because the reference is held on the stack (through the
parameter) when you pass it to MyFun.

It is most definitely something else. Without seeing the code for the
class where the operator is overloaded, as well as the code for MyFun,
it's impossible to say.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Atmapuri said:
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
 
M

Marc Gravell

It should be impossible especially because the next
iteration does not depend upon the previous...

In your example, with classes, it really, really *does*; the variables b & c
are declared and initialised *outside* of the loop, so if you are causing
side-effects in the objects then it will persist between iterations, and can
(when some critical condition [determined by the exact foobar] is met) cause
it to bomb. *Especially* for classes. For structs (without "ref" usage), the
memory contents will be cloned when passing between stack contexts, so any
side effect in the memory will only live as long as that stack - which
probably explains why structs appear to work. You just "get lucky" with
structs, in that they don't propegate bugs as readily. But the bug may still
be there. I reckon that at some point you are monkeying with the contents of
the class without cloning it first. As an example of a possible foobar
(operator with operand side-effects):

SomeClass operator *(SomeClass lhs, SomeClass rhs) {
lhs.Length *= rhs;
return lhs
}

This should be:

SomeClass operator *(SomeClass lhs, SomeClass rhs) {
SomeClass result = lhs.Clone();
result.Length *= rhs;
return result;
}

Marc
 
C

Christof Nordiek

Why do you think, the result depends on garbage collection?
The result of an operator shouldn't depent on instances ont referenced
(directly or inderectly)
by the operands. So it shouldn't depend on the existence of objetcs eligable
for collection.

Atmapuri said:
Hi!
Sounds curious; can you reproduce this in simple (yet complete), postable
code?

Could be a tough job. The garbage collector is not deterministic....
Is it possible that MyFun is return an MyOjbects [sic] instance that
genuinely has the same length as b (100)? Equally, depending on whether
these are structs or classes, it might be that you are (in the non-shown
code) accidentally changing properties of the operands, and this
behaviour
is a side-effect?

The code inside the loop works for 100 iterations and than fails?
This is C#. It should be impossible especially because the next
iteration does not depend upon the previous...

Struct works fine and classes fail...

Regards!
Atmapuri
 
A

Atmapuri

Hi!
In your example, with classes, it really, really *does*; the variables b &
c

b and c are not modified inside of the loop and also not inside
of the operator. That is certain. This also does not account for
undeterministic
nature of the problem. If I debug the stuff, it works fine... It only
fails when being run free... only after about 100 iterations...
and only if the temporary variable is not assigned explicitely, but
the expression is passed directly to the function as a parameter.
(I double tested that...)
SomeClass operator *(SomeClass lhs, SomeClass rhs) {
lhs.Length *= rhs;
return lhs
}

This should be:

SomeClass operator *(SomeClass lhs, SomeClass rhs) {
SomeClass result = lhs.Clone();
result.Length *= rhs;
return result;
}

That is a deterministic case.. that will fail in the first loop.

Thanks!
Atmapuri
 
A

Atmapuri

Hi!
Why do you think, the result depends on garbage collection?
The result of an operator shouldn't depent on instances ont referenced
(directly or inderectly)
by the operands. So it shouldn't depend on the existence of objetcs
eligable for collection.

The iteration count at which the error happens is not fixed...
The loop will execute fine 100x and fail on 101 for example.
That points to the garbage collector, because the time at which
the garbage collector is run is determined by the CLR.

And it is a predictive pattern. Whenever you have a expression
passed directly to the function without assigning its result first to the
local variable. (which should not matter).

Thanks!
Atmapuri
 
N

Nicholas Paldino [.NET/C# MVP]

Without seeing the code for the things I mentioned, it's nearly
impossible. You might as well post it, otherwise, no one will be able to do
much but offer conjecture at this point.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Atmapuri said:
Hi!

It would be very difficult for me to post the code.
Can you think of anything why this would occur
considering that:

It works with struct but not with classes. I noticed
that the compiler copies the struct before passing it
to the function.

MyObjects has a finalizer declared. And the only
way I can imagine for the Length to be set to zero
is if that finalizer has been called on the temporary
object created by 2*c, before the object was used.

Thanks!
Atmapuri



Nicholas Paldino said:
That's actually very, very wrong. Something else is going on here.
The temporary variable that is created as a result of calling 2 * c isn't
being GC'ed because the reference is held on the stack (through the
parameter) when you pass it to MyFun.

It is most definitely something else. Without seeing the code for the
class where the operator is overloaded, as well as the code for MyFun,
it's impossible to say.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Atmapuri said:
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
 
A

Atmapuri

Hi!

Could it be a problem if I call GC.Collect from inside a
class constructor?

Thanks!
Atmapuri

Nicholas Paldino said:
That's actually very, very wrong. Something else is going on here.
The temporary variable that is created as a result of calling 2 * c isn't
being GC'ed because the reference is held on the stack (through the
parameter) when you pass it to MyFun.

It is most definitely something else. Without seeing the code for the
class where the operator is overloaded, as well as the code for MyFun,
it's impossible to say.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Atmapuri said:
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
 
C

Christof Nordiek

Hi,

if your class has a finalizer and you supose the problem is because of
GC-timing you could try to debug when the finalizer ist called.

If the problem doesn't occur with structsit could be a problem
with sides effects. You should never change the operands inside
a operator. Also MyFunc looks like a Method that computes a
new value and shouldn't change the argument in any way.

BTW, why are you using a finalizer at all?
Finalizer should only clean up unmanaged resources. Managed
resources will be finalized themselves if they are referenced only by
objects eligable for finalization.
Maybe you try to clean smoething up wich shouldn't be and thereby
destroy something.

hth

Atmapuri said:
Hi!

It would be very difficult for me to post the code.
Can you think of anything why this would occur
considering that:

It works with struct but not with classes. I noticed
that the compiler copies the struct before passing it
to the function.

MyObjects has a finalizer declared. And the only
way I can imagine for the Length to be set to zero
is if that finalizer has been called on the temporary
object created by 2*c, before the object was used.

Thanks!
Atmapuri



Nicholas Paldino said:
That's actually very, very wrong. Something else is going on here.
The temporary variable that is created as a result of calling 2 * c isn't
being GC'ed because the reference is held on the stack (through the
parameter) when you pass it to MyFun.

It is most definitely something else. Without seeing the code for the
class where the operator is overloaded, as well as the code for MyFun,
it's impossible to say.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Atmapuri said:
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
 
M

Marc Gravell

That is a deterministic case.. that will fail in the first loop.

My example was for demonstration only, to illustrate what I meant be
side-effects.
b and c are not modified inside of the loop and also not inside
of the operator. That is certain.

Well, without the operator code I couldn't (and still can't) see this, but
you'll have
to excuse me for looking for the straightforward explanations first. A
demonstrable
example would be the ideal, but I gather that isn't going to happen...

Since finalizers are involved, and since we have (seemingly) exhausted the
simple
options, then it is possible that you are racing the GC; does this article
help any?

http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx

Marc
 
W

Willy Denoyette [MVP]

| Hi!
|
| Could it be a problem if I call GC.Collect from inside a
| class constructor?
|

Please post your code, it makes no sense to ask further questions you only
can answer.

Willy.
 
A

Atmapuri

Hi!

I did not think it was possible, but heck :) Here is the complete
source that gives reproducable error at least on my machine.
After you run it, you should get an exception Length = 0 indicating
that the problem is present..


public class Vec1
{
public int Length;
public Vec1()
{
Length = 100;
}
}


public class Vector1
{
internal Vec1 Data;
static private int VecIterCount = 0;
static private int GCInterval = 200;
static private int GCGeneration = 0;

protected virtual void Dispose(bool disposing)
{
if (Data != null)
{
Data.Length = 0;
}
}

~Vector1() //finalizer
{
Dispose(false);
}

public void CreateFromCache()
{
if (Data == null)
{
Data = new Vec1();
VecIterCount++;
if ((VecIterCount % GCInterval) == 0)
{
GC.Collect(GCGeneration);
VecIterCount = (VecIterCount % GCInterval) + 1;
}
}
}

public Vector1(int aLength)//: base(IntPtr.Zero, true)
{
Data = null;
CreateFromCache();
Data.Length = aLength;
}

public Vector1(Vec1 Src)//: base(IntPtr.Zero, true)
{
Data = null;
CreateFromCache();
Data.Length = Src.Length;
}

public static implicit operator Vec1 (Vector1 AValue)
{
return AValue.Data;
}

public int Length
{
get
{
return this.Data.Length;
}
set
{
this.Data.Length = value;
}
}

public static Vector1 operator *(Vector1 Left, double Right)
{
Vector1 vector = new Vector1(0);
vector.Length = Left.Length;
return vector;
}
public static Vector1 operator *(double Left, Vector1 Right)
{
Vector1 vector = new Vector1(0);
vector.Length = Right.Length;
return vector;
}
public static Vector1 operator *(Vector1 Left, Vec1 Right)
{
Vector1 vector = new Vector1(0);
vector.Length = Left.Length;
return vector;
}
public static Vector1 operator *(Vec1 Left, Vector1 Right)
{
Vector1 vector = new Vector1(0);
vector.Length = Right.Length;
return vector;
}
public static Vector1 operator *(Vector1 Left, Vector1 Right)
{
Vector1 vector = new Vector1(0);
vector.Length = Left.Length;
return vector;
}
}

public class Vec1Math
{
public static Vector1 Exp(Vec1 x)
{
Vector1 vector = new Vector1(0);
vector.Length = x.Length;
if (vector.Length == 0) throw new Exception("Length is zero");
return vector;
}

public static Vector1 Sqrt(Vec1 x)
{
Vector1 vector = new Vector1(0);
vector.Length = x.Length;
if (vector.Length == 0) throw new Exception("Length is zero");
return vector;
}
}

//This functions must be called in a loop:

private double MyFunction(int Iterations)
{
double a = 1;
Vector1 xx, x, res;
x = new Vector1(100);
int counter = Environment.TickCount;
for (int i = 0; i < Iterations; i++)
{
xx = x*x;
res = Vec1Math.Sqrt(-0.5 * a * xx) * xx * Vec1Math.Exp(4 *
Math387.INVTWOPI * x);
}
return Environment.TickCount - counter;
}

//here is the main event

private void button1_Click(object sender, System.EventArgs e) {

iters = 1000000;
for (int i=0;i<=5;i++)
{
Function(iters);
iters /= 10;
Refresh;
}

It takes on my machine about 5 seconds before the error surfaces...
It may be that due to different loads, the time will be different on
other machines... Increase the Iters count if it finishes too quick
or does not trigger a Length = 0 exception... The loop must run
at least for a few seconds... This was done on Pentium M 1.7GHz
and VS2005.

Thanks!
Atmapuri
 
B

Bill Butler

Atmapuri said:
Hi!

It would be very difficult for me to post the code.
Can you think of anything why this would occur
considering that:

Since you obviously won't post the code, here are some observations.

The chances that this is a framework bug are small.
The chances that the design is flawed is quite high.

You haven't expained
1. What are MyOjbects?
2. What is Length for? Is that what gets multiplied?
3. What does MyFun() do? Does it modify the input or just pass it
through
4. Why are you throwing an exception if the Lengths are equal?
5. Given your example operator * must be overloaded at least one
more time to handle multiplication by a constant

At a minimum, you should post the code for the * operator, and a
simplified MyFun() or there is really nothing more that we can do.

Here is a quick sample that that does what you showed...but no error
Obviously, since you didn't show MyFun, Or the guts of any of your
multiplication operators this is very simple.

Oh,well
Good luck
Bill

using System;

namespace Stupidhead
{
public class TEST
{
public static MyObject MyFun(MyObject param)
{
// Console.WriteLine("MyFun(MyObject({0}))",param.Length);
return param;
}

public static void Main(string[] args)
{
MyObject a = null;
MyObject b = new MyObject();
MyObject c = new MyObject();

b.Length = 100;
c.Length = 100;


for (int i = 0; i < 1000000; i++)
{
a = MyFun(2*c)*b;
// Console.WriteLine("---------------------");
}
}
}

public class MyObject
{
private long length;
public long Length {get{return length;} set{length = value;}}

public static MyObject operator *(MyObject lhs, MyObject rhs)
{
if(lhs.Length == rhs.Length)
Console.WriteLine("ooops");
MyObject tmp = new MyObject();
tmp.Length = lhs.Length * rhs.Length;
// Console.WriteLine("operator *(MyObject({0}), MyObject({1})) =
{2}", lhs.Length, rhs.Length, tmp.Length);
return tmp;
}

public static MyObject operator *(long k, MyObject rhs)
{
MyObject tmp = new MyObject();
tmp.Length = k * rhs.Length;
// Console.WriteLine("operator *(constant({0}), MyObject({1})) =
{2}", k, rhs.Length, tmp.Length);
return tmp;
}

public static MyObject operator *(MyObject lhs, long k)
{
MyObject tmp = new MyObject();
tmp.Length = lhs.Length * k;
// Console.WriteLine("operator *(MyObject({0}), constant({1})) =
{2}", lhs.Length, k, tmp.Length);
return tmp;
}
}
}
 
L

Lebesgue

The code you have posted doesn't compile. What is the class Math387? What is
"Refresh;" ?

These were compile errors.
But, in gods sake, my dear, oh, no, oh, ...why did you implement a Dispose
method that just sets a field to zero??
And why did you implement a finalizer? What's the CreateFromCache() ??
Where's the cache? Why are you calling GC.Collect on every 200th call to the
CreateFromCache method???

Maybe you should tell us what are you trying to do in the first place. This
code is uncompilable and ununderstandable.
 
A

Atmapuri

Hi!
The code you have posted doesn't compile. What is the class Math387?

It declares a constant. You should delete it.
"Refresh;" ?

Refresh is a method of Winform. It is suppose to affect
the speed of loops... You are suppose to:

1.) Create a new project.
2.) Place a button on the form.
3.) Double click the button.
4.) Fill in the on_button event.
5.) Paste in the rest of the code
These were compile errors.
But, in gods sake, my dear, oh, no, oh, ...why did you implement a Dispose
method that just sets a field to zero??
And why did you implement a finalizer? What's the CreateFromCache() ??
Where's the cache? Why are you calling GC.Collect on every 200th call to
the CreateFromCache method???

Maybe because 50.000 lines of code is missing?

Thanks!
Atmapuri
 
A

Atmapuri

Hi!
Since you obviously won't post the code, here are some observations.

If you would have read a few posts down, you would have found it.
Oh,well
Good luck

Thanks :) When you have very large projects with many dependancies
it can be a great challenge to extract a small and simple test case.
It can also be very time consuming and finding a workaround is
much simpler. So its not that I dont want to post immediately, but the
possible
work needed usually makes me shy in such cases and any
shortcuts I can get are most welcome...

Best Regards!
Atmapuri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top