skwear skwear - 1 month ago 5
C# Question

Is there a faster way to loop over an array in safe mode

I am writing a method to measure the frequency of a sampled sine wave. It takes a somewhat large 1D array (10^3 to 10^4 samples order of magnitude) and returns a

double
. A helper methods is also called within the body of the method that checks whether the wave is crossing zero. Here is an example of what I have written:

public static double Hertz(float[] v, int n) {
int nZCros = 0
for (int i = 1; i < n; i++) {
if (IsZeroCrossing(v.Skip(i - 1).ToArray())) {
++nZCros;
}
}
return (double)nZCros / 2.0;
}
private static bool IsZeroCrossing(float[] v) {
bool cross;
//checks if the first two elements of the array are opposite sign or not
return cross;
}


My problem is that method takes 200-300 ms to run. So I decided to try using
unsafe
and pointers, like this,

public unsafe static double Hertz(float* v, int n) {
int nZCros = 0
for (int i = 1; i < n; i++) {
if (IsZeroCrossing(&v[i - 1])) {
++nZCros;
}
}
return (double)nZCros / 2.0;
}
private unsafe static bool IsZeroCrossing(float* v) {
bool cross;
//checks if the first two elements of the array are opposite sign or not
return cross;
}


which runs in 2-4 ms.

However, I am not really comfortable with venturing outside the recommended bounds. Is there a way to achieve the same speed in a safe context? And if there isn't, does it defeat the purpose of using C#? Should I really be using C# for these kind of signal processing applications and scientific implementations?

This is just one of many DSP methods I'm writing which take a large array of samples as an input. But this one got me to realize there was a problem, because I accidentally put in 48000 samples instead of 4800 when I was testing this method and it took 20 seconds to return a value.

Thank you.

UPDATE: I tried adding
Take(2)
after
Skip(i - 1)
in the former snippet. This brought it down to 90-100 ms, but the question still stands.

Answer

You don't need to pass a copy of the array elements to IsZeroCrossing().

Instead, just pass the two elements you are interested in:

private static bool IsZeroCrossing(float elem1, float elem2)
{
    return elem1*elem2 < 0.0f; // Quick way to check if signs differ.
}

And call it like so:

if (IsZeroCrossing(v[i-1], v[i]) {

It's possible that such a simple method will be inlined for a release build, making it as fast as possible.