FFMG / myoddweb.directorywatcher

A fast and reliable, (non blocking!), .NET/C++ File/Directory watcher, complete rewrite of FileSystemWatcher to ensure speed/accuracy/reliability/suppress duplicate events.
MIT License
49 stars 12 forks source link

High thread count #8

Closed milos12345 closed 4 years ago

milos12345 commented 4 years ago

When running the example console exe the computer becomes slow. In Process Hacker I have noticed that it uses around 1200 threads, and Process.GetCurrentProcess().Threads.Count shows the same (I have 5 disks, all SSDs but one). Can this be avoided?

EDIT: I cannot reproduce performance issues from the test before, but high thread count is still there, amounting to constant ~2% of CPU use on 24-core machine

FFMG commented 4 years ago

Hi, Sorry for the long delay in replying ...

I ran a couple of tests, (and created a branch for load tests)

When monitoring an entire system, (3 disks and so on), I get about 1300 threads, so it is fairly similar to your numbers.

My tests indicate that there is no leak, (memory and/or resources). You are welcome to run the same test as me using the app in the sample folder, and the params myoddweb.directorywatcher.load.exe --iterations 100 --q true --d true

I will keep updating that app as it is a useful testing tool for me.

Looking over the web, there is no clear consensus as to what a "good" number of threads might be. Some, (like myself), believe it is up to the OS to manage what a good number is. Others believe that 100 is about the maximum you should have.

In the end, it is not my call to make but up to the developer, I will make some changes to allow the app to limit the thread count.

milos12345 commented 4 years ago

Will it be possible to control the number of threads, and if yes, would it miss events if the number of threads is too low or have other side-effects? I have redesigned my app to use only one watcher per drive (instead of having one watcher per panel). It is working well for the few manual tests I have been doing (still on older version of watcher) but I am still catching up on other functionality so I will have to continue testing the integration later.

I tried to run the test but I am not at the level to understand what is happening and if how it is supposed to be ran, so I don't know if this is useful at all. I have opened myoddweb.directorywatcher.ref.sln and run the .load test after adding those arguments in the project properties and received this in console

[02:18:40.1646]:Number of threads: 71 [02:18:50.3467]:Number of threads: 99 [02:19:00.5047]:Number of threads: 118 [02:19:10.6648]:Number of threads: 121 [02:19:20.8293]:Number of threads: 121

D:\Temp2020\myoddweb.directorywatcher-feature-test2\myoddweb.directorywatcher-feature-test\samples\bin\Debug\netcoreapp3.1\myoddweb.directorywatcher.load.exe (process 4752) exited with code -1073741819.

and in output

The program '[4752] myoddweb.directorywatcher.load.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'.

I could not run the Release build as it shows shows

Error C3861 'MYODDWEB_OUT': identifier not found
CS1566 Error reading resource 'x86.directorywatcher.win' -- 'Could not find file 'D:\Temp2020\myoddweb.directorywatcher-feature-test2\myoddweb.directorywatcher-feature-test\samples\bin\Release\Win32\myoddweb.directorywatcher.win.x86.dll'.'

I tried also running the myoddweb.directorywatcher.win.test.exe from the main branch and got this result

[----------] 16 tests from MonitorsManagerAdd/ValidateNumberOfItemAdded (15038 ms total)

[----------] Global test environment tear-down [==========] 88 tests from 9 test suites ran. (30172 ms total) [ PASSED ] 73 tests. [ FAILED ] 15 tests, listed below: [ FAILED ] MonitorsManagerEdge/RecursiveAndNonRecursive.TwoWatchersOnTheSameFolder/0, where GetParam() = true [ FAILED ] MonitorsManagerEdge/RecursiveAndNonRecursive.TwoWatchersOnTheSameFolder/1, where GetParam() = false [ FAILED ] MonitorsManagerEdge/RecursiveAndNonRecursive.TwoWatchersOnTwoSeparateFolders/1, where GetParam() = false [ FAILED ] MonitorsManagerDelete/ValidateNumberOfItemDeleted.CallbackWhenFolderIsDeleted/2, where GetParam() = (1, true) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFileIsAdded/2, where GetParam() = (1, true) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFileIsAdded/3, where GetParam() = (1, false) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFileIsAdded/4, where GetParam() = (17, true) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFileIsAdded/5, where GetParam() = (17, false) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFileIsAdded/6, where GetParam() = (42, true) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFolderIsAdded/2, where GetParam() = (1, true) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFolderIsAdded/3, where GetParam() = (1, false) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFolderIsAdded/4, where GetParam() = (17, true) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFolderIsAdded/5, where GetParam() = (17, false) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFolderIsAdded/6, where GetParam() = (42, true) [ FAILED ] MonitorsManagerAdd/ValidateNumberOfItemAdded.CallbackWhenFolderIsAdded/7, where GetParam() = (42, false)

FFMG commented 4 years ago

Hi,

I am currently working in the branch feature/thread and feature/test.

In both branches I am running tests on thousands of folders and making sure that no issues are picked up.

I am not sure what branch/version you ran your tests, but only the version I pushed this morning are stable, (well kind of).

I will keep writing/fixing unit tests and so on to make sure that everything is still running fine and will let you know as soon as I will do a proper release.

But you are more than welcome to play with the feature/thread branch but until I push to master I suspect both those branches will be incomplete.

milos12345 commented 4 years ago

Hi, I have not had a chance to do more tests being stuck at some other parts, but I am reading now book "CLR via C#" and here is a relevant excerpt on threads I just got to:

Because customers have had starvation and deadlock issues, the CLR team has steadily increased the default maximum number of threads that the thread pool can have. The default maximum is now about 1,000 threads, which is effectively limitless because a 32-bit process has at most 2 GB of usable address space within it. After a bunch of Win32 DLLs load, the CLR DLLs load, the native heap and the managed heap is allocated, there is approximately 1.5 GB of address space left over. Because each thread requires more than 1 MB of memory for its user-mode stack and thread environment block (TEB), the most threads you can get in a 32-bit process is about 1,360. Attempting to create more threads than this will result in an OutOfMemoryException being thrown. Of course, a 64-bit process offers 8 terabytes of address space, so you could theoretically create hundreds of thousands of threads.

Page 724 if you are interested

This might have changed as this is written 2012. They also talk about thread pool scheduling, 30ms context switching, but without understanding how directorywatcher is using them and not knowing much on threads I cannot offer anything new on that.

FFMG commented 4 years ago

Sorry, but the number of thread was never an issue. In any case, I did some reworking and changed some of the architecture to not use as many threads. Not because the number of threads itself was a problem, but because architecturally it made more sense to use less. And the added benefit was to reduce the cpu usage, (ultimately a bigger issue).

In any case, I have finished the development, and I am in the final tests before releasing it.

Sorry, I should have added that I still have 2 inconclusive tests that I am working on and a load test case I would like to add.

FFMG commented 4 years ago

The number of thread is now limited under 0.1.8