ScopedNoDenormals issue

I’ve been using the ScopedNoDenormals code from this thread: State of the Art Denormal Prevention and as far as I could tell it was working well. I just switched to the built-in ScopedNoDenormals code in 5.1.2 and there’s definitely a difference…and not a good one. Either I’m not using the new code correctly, or it doesn’t work well or at all.

All I have is this at the start of my processBlock():

ScopedNoDenormals ndn;

When I use the original code, I see no difference in CPU usage when my plugin is playing notes or not. When I use the new code, I see the CPU go WAY up when I’m not playing notes.

There are quite a few IIRFilters in my code whether notes are down or not, so it seems to me that the old code is preventing the denormals and the new code isn’t.

This is true on Mac and Windows.

Am I missing something?

2 Likes

The code seems identical besides only getting compiled if JUCE_USE_SSE_INTRINSICS is 1.

On windows, have you maybe not activated SSE2 usage?

On mac (at least 64-bit) it seems impossible to deactivate. However I just realize it’s also 0 for me :frowning: on Mac OSX with XCode.

It appears the problem is include order. JUCE_USE_SSE_INTRINSICS is defined in juce_audio_basics.cpp and juce_FloatVectorOperations.h gets included in JuceHeader.h way before that.

To sum up the JUCE_USE_SSE_INTRINSICS macro is just not available in that header file! I guess its definition should be moved to this very same header file.

So this is a serious problem! I removed all other denormal protection in my code and relied on this new class.

For now we users can just remove the define check if we know to always support SSE2

2 Likes

I now moved to my own similar class for denormalization prevention. Unfortunately the JUCE one is broken in multiple ways. Seeing this is now part of the standard “New Plugin” code, it needs fixing for sure.

Due to include order the class never does anything! On non intel platforms and platforms that do not support intel cpu intrinsics, it does nothing. If users of the code see ScopedNoDenormals in the main plugin process routine, they might assume Denormals are prevented, but in reality that might not be the case! So on other platforms there should be a warning or compilation should fail or even better, there should be alternative implementations for all supported platforms.

4 Likes

Thanks for reporting. I’ll get this fixed.

Edit: A fix for this will appear on develop shortly.

1 Like

Fabian - how about popping this in the class as well so it’s easy to write assertions that check denormals have been disabled:
static bool denormalsDisabled()
{
auto mxcsr = _mm_getcsr();
return (mxcsr & 0x8040) == 0x8040; // the DAZ and FZ bits
}

Also, this is a pretty substantial bug. How long does it take to get the develop feature into the main branch :slight_smile:

1 Like

Yes, please at least cherry-pick this fix as a hotfix onto master branch, until next version.

OK - I’ve pushed the hotfixes to master and added @jimc 's denormalsDisabled method to develop with commit 2ac2a39.

2 Likes

I think a method to check denormals have been disabled should not check processor flags because there might be architectures that don’t support these calls. In my opinion a check should be done by forcing numbers to go denormal with standard operations and check the result. This way the check would work on all platforms and cases where denormals could happen would be easily detectable. Here’s an attempt at such code:

    // check flush to zero is working
    float verysmall = numeric_limits<float>::min(); // min does not include denormal numbers!
    verysmall *= 0.1f; // force number into the denormal range
    if (verysmall != 0.f) {
        DBG("Denormals detected!");
    }

I’m not so sure this is a good test as the test may depend on the compiler settings and platform (undefined behaviour). For example, on arm, NEON registers never use denormals. So if the compiler chooses to do the multiplication in a NEON register (for whatever optimisation reason), it will look like denormals are disabled. However, any subsequent instruction which uses the normal FPU registers/instructions can still get denormals.

IMO checking the actual cpu status register is a better idea.

1 Like

Right. Just now I saw that the test returns false in case there is no code for checking registers… so it’s all good. Sorry about the noise.