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

Arithmetic overflow when running 32 CPUs -> Fix for it #90

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

vsalomaki
Copy link

When running the code on a system with 32 (virtual) CPUs, the Process.GetCurrentProcess().ProcessorAffinity overflows 32-bit integer.
Example of such system is c4.8xlarge at Amazon,

When running the code on a system with 32 (virtual) CPUs, the Process.GetCurrentProcess().ProcessorAffinity overflows 32-bit integer.
Example of such system is c4.8xlarge at Amazon,
@XeonMan
Copy link

XeonMan commented Jan 29, 2016

I am getting the same on a dual X7550 system (32 threads)...

Don't think using a Long is the solution, as num is being used to get the processor count, as in the code comments just below:

             // if there is more than one processor, use processor count +1
              if (num != 1)

Suggest this instead:

#89

identical to your

vsalomaki@336f721

@vsalomaki
Copy link
Author

Actually the "long-solution" does work, because it's casted back to int.
Anyway, it seems that this was a duplicate solution
+1 for commit

@XeonMan
Copy link

XeonMan commented Jan 29, 2016

It works in that it does not throw an exception, but I'm getting 4294967295 on a 32 processor system if using Long, which I don't think is the intended value.

See here:

http://stackoverflow.com/questions/34780520/encog-dotnet-3-3-multithread-error

ProcessorCount returns 32 correctly, which seems to me is the intended value for the code logic afterwards.

Thoughts?

+1 for commit too

@vsalomaki
Copy link
Author

Where do you read that value, from the variable 'num'? How do you read it?
I'm not sure if you tested my 'original' fix as is, but your result seems like an overflow or an underflow.

Anyway, the large "number" returned is by MS-design. In fact, it's not really a number, but a bitmask.
See: https://msdn.microsoft.com/en-us/library/system.diagnostics.process.processoraffinity(v=vs.110).aspx

ProcessorAffinity:
A bitmask representing the processors that the threads in the associated process can run on. The default depends on the number of processors on the computer. The default value is 2^n -1, where n is the number of processors.

When using the Environment.ProcessorCount, we are actually overriding the (possible) processor-count intended for the process.

@jeroldhaas
Copy link
Contributor

It works in that it does not throw an exception, but I'm getting 4294967295 on a 32 processor system if using Long, which I don't think is the intended value.

Actually, it seems it is.

In FSI:

Math.Log(4294967295., 2.);;
val it : float = 32.0

According to this, using int or long will result in incompatibility for the opposing platform (64 or 32 bit). A precompiler definition may be the best answer, here.

@jeroldhaas
Copy link
Contributor

Could you test this to see if it's working as expected?

Proposing:

              if (threads == 0)
              {
#ifdef _WIN64
                var num = (int) (Math.Log(((long) Process.GetCurrentProcess().ProcessorAffinity + 1), 2));
#else
                var num = (int) (Math.Log(((int) Process.GetCurrentProcess().ProcessorAffinity + 1), 2));
#endif

                  // if there is more than one processor, use processor count +1
                  if (num != 1)

Log2 with long looks like it can result in a value of 31, so this may be a bad solution. Use of float does resolve to 32, however, and very well may be a good adaptation.

Something akin to:

var num = (int) (Math.Log(((float) Process.GetCurrentProcess().ProcessorAffinity + 1), 2.0));

If this could be tested as well... I've only 8 cores here.

@XeonMan
Copy link

XeonMan commented Jan 29, 2016

Yeah, I was thinking of:

var num = (int)(Math.Log(((double)Process.GetCurrentProcess().ProcessorAffinity + 1), 2));

Going to try to test all provided solutions on my 32 core system later in the day (no access right now) and I'll post back.

@jeroldhaas
Copy link
Contributor

That extra precision may very well prove to be a good future-forward solution.

I think pending some tests (which you two would have to champion, @XeonMan and @vsalomaki 😄 ), we may have a proper solution.

@XeonMan
Copy link

XeonMan commented Jan 29, 2016

My system (HP DL580G7) can go to 64 cores in the near future (I already have the CPUs... just need 2 extra heatsinks and a couple of extra memory cartridges) to an absolute max of 80 cores (that entails changing all CPUs and memory cartidges; i.e.: expensive!), so I guess we do have to future-proof the solution...

@jeroldhaas
Copy link
Contributor

Beyond 64 cores, CPUs are segmented into groups, so this code block will have to be handled differently (and likely differently per-platform) when core count reaches that scale.

For the time being, I think working with the premise of a 64-core ceiling should suffice for the majority percentile of end-users.

@XeonMan
Copy link

XeonMan commented Jan 29, 2016

Agreed.

I'll get to work on this during the evening and post back.

BTW, I actually mean threads (or virtual processors?) in my case... So that's actually 32 physical cores + 32 HT, or a max of 40 physical + 40 HTs...

@XeonMan
Copy link

XeonMan commented Jan 29, 2016

Just a thought... if we're limiting to a premise of a 64 core ceiling, wouldn't Enviroment.ProcessorCount suffice (because of the 64 core grouping, etc., etc.)?

@jeroldhaas
Copy link
Contributor

Using ProcessorCount would ignore the processor affinities set to the process by the user.

jeroldhaas added a commit to jeroldhaas/encog-dotnet-core that referenced this pull request Jan 29, 2016
Integer overflow when core count causes `ProcessorAffinity` bitmask to go above 32-bit capabilities.
@XeonMan
Copy link

XeonMan commented Jan 29, 2016

Gotcha. Thanks.

@vsalomaki
Copy link
Author

I would go with the ProcessorCount-solution as the primary solution for easier code maintenance and readability,32bit and 64bit. (Mind you, there are no constraints about using 64bit numbers in a 32bit system; just a matter of performance)

As the secondary (more optimal?) solution, one should use the ProcessorAffinity as it was (probably) supposed to be user: that is, instead of using Math.Log(), one should use bit shifting.

@XeonMan
Copy link

XeonMan commented Jan 30, 2016

Well, I do run Encog full blast on my machine, so I am in fact using ProcessorCount (as can be seen in my -now- commented code), as I do not set Affinity manually...

However, for the sake of completeness, here is the test proof, as promised:

http://www.7fgr.com/downloads/encog_test.png

And yes, it woiks.

@jeroldhaas
Copy link
Contributor

If you adjust affinity for Encog and another program (a single process app) to run on one of your cores while Encog consumes the rest, does Encog honor those settings using ProcessorCount in the concerned code?

@jeroldhaas
Copy link
Contributor

@XeonMan @vsalomaki The original Encog Java source uses AvailableProcessors, which appears to be equivalent to ProcessorCount. With that in mind, we can assume that the affinity-based code was written to take advantage of the .NET framework's capabilities to follow affinity settings, as the Java core cannot handle threads based on affinity without native calls to the operating system.

I'd really like to avoid removing this feature, and I'm going to assume that @jeffheaton feels similarly, as well. @vsalomaki I agree bitwise operations are indeed a better solution to this, however I haven't yet spent enough time experimenting with the IntPtr / NativeInt returned to obtain the affinity sum. Using Log(2) only somewhat works: it's a bit of a hack.

There are very likely other reasons to keep the use of affinity that I'm unaware of. I'm hopeful that @jeffheaton can shed some light on this matter.

@XeonMan
Copy link

XeonMan commented Feb 2, 2016

@jeroldhaas I don't think ProcessorCount would honor that, as I believe ProcessorCount will return the number of processors as known to the OS, regardless of affinity.

I do believe @jeffheaton really meant having affinity functionality in the code; I made the modification as a quick way to get Encog working and with my specific needs in mind; however, by using ProcessorAffinity, I see no neg impact on my specific needs, as by not setting affinity manually, I get the same functionality. Having said that, and putting aside my particular scenario, I do fully agree with using ProcessorAffinity as was originally intended, albeit the "small" (double) fix.

BTW, I just roasted my home mains powerline today at 6:18a.m., as I was running my DL580G7 and ML370G5 full blast (> 85% CPU) and a PE2950III as a DB server, servicing the former two, plus AC. A 2,000+ watt around-the-clock load plus the rest of the house (say another kWh) which finally toasted my mains powerline before the fuses blew. The fusebox literally went up in flames. I got my house running again, along with the PE2950III by 9:20a.m. I'm getting a new powerline installed during the week (I hope) which should get my full gear up to speed by the end of the week, should we need further testing on a 32 thread system, etc...

@jeffheaton
Copy link
Owner

Thank you very much for the pull request and investigation. Sorry for the slow response, and I will review/reply soon.

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

Successfully merging this pull request may close these issues.

4 participants