Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

heisenbug #1

Closed
spirit11 opened this issue Apr 20, 2016 · 12 comments
Closed

heisenbug #1

spirit11 opened this issue Apr 20, 2016 · 12 comments

Comments

@spirit11
Copy link

Hello. Following code falls randomly at lines #1 or #2 with AccessViolationException only when debugger is not attached. Commenting out line #3 prevents code to fail.

 var N = 4096;
 var input = new RealArray(N);
 var spectrum = new ComplexArray(N);
 var output = new RealArray(N);
 var fft = Plan.Create1(N, input, spectrum, Options.Measure);
 var ifft = Plan.Create1(N, spectrum, output, Options.Measure);

 while (true)
 {
     Console.WriteLine("converting");
     fft.Execute(); // #1
     Console.WriteLine("converting back");
     ifft.Execute(); // #2
     Console.WriteLine("getting result");
     var result = output.ToArray(); // #3
 }
@wo80
Copy link
Owner

wo80 commented Apr 20, 2016

Nice one.

  • Allocating the result array before the loop and using output.CopyTo(result) works.
  • The single precision code works.
  • On my laptop, the double precision code always fails at heisenbug #1 on iterration 97

@wo80
Copy link
Owner

wo80 commented Apr 20, 2016

Setting private const int SIZE = 16; (instead of 8) for the double precision RealArray seems to fix the issue. Same for single precision (SIZE = 8).

No clue why 8 ( = sizeof(double)) doesn't work.

@spirit11
Copy link
Author

Using arrays of size N*2 also seems to fix the issue, but I'm afraid there is no reason to think that it is a proper solution. When arrays are of length N+N/2 it falls on 65 iteration. Very strange. I also checked it with x64 dll from http://www.fftw.org/install/windows.html. It fails too.

@spirit11
Copy link
Author

Perhaps RealArray must be allocated with double size because it's zero imaginary part is actually used during fft\ifft calculation. But following also fails on some runs

            var N = 4096;
            var input = new RealArray(N * 2);
            var spectrum = new ComplexArray(N);
            var output = new RealArray(N * 2);
            var fft = Plan.Create1(N, input, spectrum, Options.Measure);
            var ifft = Plan.Create1(N, spectrum, output, Options.Measure);

            while (true)
            {
                Console.WriteLine("converting");
                output.Set(Enumerable.Range(0, N * 2).Select(_ => 0.0).ToArray());

                var inputArray = input.ToArray(); // 1

                fft.Execute();
                Console.WriteLine("converting back");
                ifft.Execute();
                Console.WriteLine("getting result");
                var result = output.ToArray();
            }

And again after commenting out line 1 I can't reproduce error.

@wo80
Copy link
Owner

wo80 commented Apr 21, 2016

More fun facts:

  • N=1024 doesn't crash (or any other lower power of 2).
  • N=2048 does crash, N=2051 doesn't. EDIT: not really sure about that one, might be random.
  • Adding input.Clear(); at the beginning of the loop seems to fix the issue. EDIT: or maybe not ..

@wo80
Copy link
Owner

wo80 commented Apr 21, 2016

Wrapping the plans and arrays in a using statement seems to fix the issue. Does it work for you:

static void Test()
{
    const int N = 4096;

    using (var input = new RealArray(N))
    using (var spectrum = new ComplexArray(N / 2 + 1))
    using (var output = new RealArray(N))
    using (var fft = Plan.Create1(N, input, spectrum, Options.Measure))
    using (var ifft = Plan.Create1(N, spectrum, output, Options.Measure))
    {
        //double[] result= new double[N];

        int i = 0;
        while (i++ < 1000)
        {
            Console.Write("converting ... ");
            fft.Execute();
            Console.Write("back ... ");
            ifft.Execute();
            Console.Write("getting result (dobule) {0}:", i);

            var result = output.ToArray();
            //output.CopyTo(result);

            Console.WriteLine(" {0:0.000}, {1:0.000} ...", result[0], result[1]);
        }
    }

    Console.ReadLine();
}

@spirit11
Copy link
Author

Oh! spectrum is garbage collected otherwise !!!!

@spirit11
Copy link
Author

I checked this, finalizer is actually called before the crash. Maybe it's a good idea to add two references for input and output arrays as the fields in abstract plan to prevent such a mistake? Although in real case there will be some usage\modification of spectrum...

@wo80
Copy link
Owner

wo80 commented Apr 21, 2016

Well, ok, now it all makes more sense. Thought the "fix" by doubling the array size stays mysterious. Or using output.CopyTo(result), it shouldn't prevent spectrum to get garbage collected.

But maybe it's just a matter of how often we'd run the tests ...

Not sure if I want to put the array references in the plan base class. As you said, it's an artificial problem one wouldn't find in a real case.

EDIT: On the other hand, the plan relies on the arrays to be available for all it's lifetime, so why shouldn't it make sure by keeping the references.

@spirit11
Copy link
Author

It's not totally artifical problem. I faced it while trying to measure speed and accuracy of fft\ifft conversion sequence. Moreover, Array classess don't look like an abstraction, they are the part of implementation. Next step for me looks like a wrapper over wrapper as following:

  public class SpectrumCalculator : IDisposable
  {
        public SpectrumCalculator(int Length)
        {
            length = Length;
            signal = new RealArray(Length);
            spectrum = new ComplexArray(Length);
            fft = Plan.Create1(Length, signal, spectrum, Options.Measure); //may be lazy initialized
            ifft = Plan.Create1(Length, spectrum, signal, Options.Measure);
        }

        public Complex[] Fft(double[] Values)
        {

        }

        public double[] Ifft(Complex[] Spectrum)
        {

        }
    }

@wo80
Copy link
Owner

wo80 commented Apr 21, 2016

Sure, but the SpectrumCalculator will hold the references, so it shouldn't be a problem.

Check out the latest commit and close the issue if you think we've got it fixed.

@spirit11
Copy link
Author

Ok, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants