├── .drone.yml
├── .github
└── workflows
│ └── CpuUsage-Tests-Matrix.yml
├── .gitignore
├── .idea
└── .idea.Universe.CpuUsage
│ └── .idea
│ └── runConfigurations
│ ├── Benchmarks_Core_2_2.xml
│ └── Benchmarks_NET_4_7.xml
├── .travis.yml
├── .vscode
├── extensions.json
├── launch.json
└── tasks.json
├── FULL-TIME-REPORT.sh
├── Info
├── Benchark-via-fio-on-AzurePipelines.txt
├── Benchmark.txt
├── TODO-on-Linux.txt
├── express.test.sh
└── show-platform.sh
├── LICENSE
├── README.md
├── Shared
├── Targeting.props
├── Universe.CpuUsage.snk
└── Version.props
├── Tests4Mono
├── On-Local-Mac-or-Linux.sh
├── Universe.CpuUsage.MonoTests.sln
├── Universe.CpuUsage.MonoTests
│ ├── App.config
│ ├── FirstTest.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Universe.CpuUsage.MonoTests.csproj
│ ├── ZeroTest.cs
│ └── packages.config
├── build-the-matrix.sh
├── install-dotnet.sh
├── nuget.config
└── say.include.sh
├── Universe.CpuUsage.Banchmark
├── BenchmarkProgram.cs
├── CpuUsageBenchmarks.cs
├── Universe.CpuUsage.Banchmark.csproj
└── run-llvm.sh
├── Universe.CpuUsage.Tests
├── AsyncSchedulerCases.cs
├── CpuLoader.cs
├── CpuUsageAsyncWatcher_AsyncStreamTests.cs
├── CpuUsageAsyncWatcher_Tests.cs
├── CrossInfo
│ └── CrossInfo.cs
├── CrossInfo_Tests.cs
├── Env.cs
├── LinuxKernelCacheFlusher.cs
├── LinuxResourcesUsage_Reader_Tests.cs
├── NUnitTestsBase.cs
├── PosixProcessPriority.cs
├── PosixResourcesUsage_Tests.cs
├── PrecisionTest.cs
├── RisingTest.cs
├── SmokeCpuUsage.cs
├── Statistica
│ ├── HistogramReport.cs
│ ├── PercentileCalc.cs
│ └── Statistica.cs
├── Test_IsSupported.cs
└── Universe.CpuUsage.Tests.csproj
├── Universe.CpuUsage.sln
├── Universe.CpuUsage
├── CpuUsage.cs
├── CpuUsageAsyncWatcher.cs
├── CpuUsageReader.cs
├── CrossInfo.cs
├── LegacyNetStandardInterop.cs
├── LinuxResourceUsageReader.cs
├── MacOsThreadInfo.cs
├── PosixResourceUsage.cs
├── Universe.CpuUsage.csproj
└── WindowsCpuUsage.cs
├── Using Net Standard.url
├── appveyor.yml
├── azure-pipelines.yml
├── azure-steps-nix.yml
├── azure-steps-nix.yml-Prev
├── benchmark.linux-2.sh
├── benchmark.linux.sh
├── benchmark.win.cmd
├── build-and-test-in-docker.sh
├── build.cmd
├── images
├── CpuUsage.Icon-V2.png
├── CpuUsage.Icon.png
├── Main.Icon.pdn
├── Universe.CpuUsage-benchmark.png
└── Universe.CpuUsage-implementation-table.png
├── nuget.config-ignored
└── test-on-mono-only-platforms.sh
/.drone.yml:
--------------------------------------------------------------------------------
1 | kind: pipeline
2 | type: docker
3 | name: 'ARM 64 bit: Build and test'
4 |
5 | platform:
6 | os: linux
7 | arch: arm64
8 |
9 | steps:
10 | - name: 'Build & Test on ARM 64 bit'
11 | image: 'debian:stretch'
12 | commands:
13 | - bash -e build-and-test-in-docker.sh
14 |
15 | ---
16 | kind: pipeline
17 | type: docker
18 | name: 'ARM 32 bit: Build and test on'
19 |
20 | platform:
21 | os: linux
22 | arch: arm
23 |
24 | steps:
25 | - name: 'Build & Test on ARM 32 bit'
26 | image: 'debian:stretch'
27 | commands:
28 | - bash -e build-and-test-in-docker.sh
29 |
30 | ---
31 | kind: pipeline
32 | type: docker
33 | name: 'x64_86: Build and test'
34 |
35 | platform:
36 | os: linux
37 | arch: amd64
38 |
39 | steps:
40 | - name: 'Build & Test on x64'
41 | image: 'debian:stretch'
42 | commands:
43 | - bash -e build-and-test-in-docker.sh
44 |
45 |
--------------------------------------------------------------------------------
/.github/workflows/CpuUsage-Tests-Matrix.yml:
--------------------------------------------------------------------------------
1 | name: CPU Usage Tests Matrix
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: [ "master" ]
7 |
8 | defaults:
9 | run:
10 | shell: bash
11 |
12 | jobs:
13 | TestCpuUsageOnVm:
14 | name: On VM
15 | strategy:
16 | fail-fast: false
17 | matrix:
18 | os:
19 | - 'x64-debian-10'
20 | - 'x64-debian-11'
21 | - 'x64-debian-12'
22 | - 'x64-debian-13'
23 | - 'x64-ubuntu-22.04'
24 | - 'x64-ubuntu-24.04'
25 | - 'arm64-debian-10'
26 | - 'arm64-debian-11'
27 | - 'arm64-debian-12'
28 | - 'arm64-debian-13'
29 | - 'arm64-ubuntu-14.04'
30 | - 'arm64-ubuntu-16.04'
31 | - 'arm64-ubuntu-18.04'
32 | - 'arm64-ubuntu-20.04'
33 | - 'arm64-ubuntu-22.04'
34 | - 'arm64-ubuntu-24.04'
35 | - 'armel-debian-8'
36 | - 'armel-debian-9'
37 | - 'armel-debian-10'
38 | - 'armel-debian-11'
39 | - 'armhf-debian-8'
40 | - 'armhf-debian-9'
41 | - 'armhf-debian-10'
42 | - 'armhf-debian-11'
43 | - 'armhf-debian-12'
44 | - 'armhf-ubuntu-14.04'
45 | - 'armhf-ubuntu-16.04'
46 | - 'armhf-ubuntu-18.04'
47 | - 'armhf-ubuntu-20.04'
48 | - 'armhf-ubuntu-22.04'
49 | - 'armhf-ubuntu-24.04'
50 | - 'i386-debian-10'
51 | - 'i386-debian-11'
52 | - 'i386-debian-12'
53 |
54 |
55 | runs-on: ubuntu-24.04
56 | timeout-minutes: 15
57 | steps:
58 | - name: Checkout
59 | uses: actions/checkout@v4
60 |
61 | - name: Bootstrap (timeout and try-and-retry)
62 | run: |
63 | script=https://raw.githubusercontent.com/devizer/test-and-build/master/install-build-tools-bundle.sh; (wget -q -nv --no-check-certificate -O - $script 2>/dev/null || curl -ksSL $script) | bash > /dev/null
64 | dotnet --info
65 | Say "Installing timeout"
66 | try-and-retry sudo apt-get update -qq;
67 | try-and-retry sudo apt-get install bsdutils -y -qq;
68 | timeout --version
69 |
70 | - name: Build Tests
71 | run: |
72 | set -ue; set -o pipefail
73 | cd Universe.CpuUsage.Tests
74 | Reset-Target-Framework -fw net48
75 | time try-and-retry dotnet build -c Release -f net48 -o "$HOME/Cpu-Usage-Tests" >/dev/null
76 |
77 | - name: Pull VM
78 | run: |
79 | set -ue; set -o pipefail
80 | docker version
81 | try-and-retry docker pull "devizervlad/crossplatform-pipeline:${{ matrix.os }}"
82 |
83 | - name: Run Tests
84 | uses: nick-fields/retry@v3
85 | with:
86 | timeout_minutes: 12
87 | max_attempts: 3
88 | shell: bash
89 | retry_wait_seconds: 1
90 | on_retry_command: 'docker rm -f VM 2>/dev/null || true'
91 | command: |
92 | set -ue; set -o pipefail
93 | cat << 'EOF' > "$HOME/Cpu-Usage-Tests/lets-rock.sh"
94 | Say "STARTING CPU USAGE TESTS";
95 | free -m;
96 | nunit3-console --inprocess --workers=1 Universe.CpuUsage.Tests.dll 2>&1 | tee /job/PRECISION.LOG;
97 | Say "TESTS COMPLETED"
98 | EOF
99 | cat "$HOME/Cpu-Usage-Tests/lets-rock.sh"
100 | timeout 666 docker run --privileged -e VM_MEM=1000M -e VM_CPUS=2 -e QEMU_TCG_ACCELERATOR=tcg --name VM --hostname VM --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined -t \
101 | -v "$HOME/Cpu-Usage-Tests":/job \
102 | "devizervlad/crossplatform-pipeline:${{ matrix.os }}" \
103 | bash -eu -o pipefail lets-rock.sh
104 |
105 | - name: PREPARE Artifact
106 | run: |
107 | # TRIM Show_Precision_Histogram (first) ... Show_Precision_Histogram (last) lines
108 | sudo chown -R $(whoami) $HOME/Cpu-Usage-Tests
109 | cp $HOME/Cpu-Usage-Tests/PRECISION.LOG /tmp/0
110 | # sed '0,/Show_Precision_Histogram/d' - it exclides matching line
111 | cat /tmp/0 | sed -n '/Show_Precision_Histogram/,$p' > /tmp/1
112 | tac /tmp/1 > /tmp/2
113 | cat /tmp/2 | sed -n '/Show_Precision_Histogram/,$p' > /tmp/3
114 | tac /tmp/3 > $HOME/Cpu-Usage-Tests/PRECISION.LOG
115 | cat $HOME/Cpu-Usage-Tests/PRECISION.LOG
116 |
117 | mkdir -p "${{ github.workspace }}/Artifact"
118 | cp -f $HOME/Cpu-Usage-Tests/PRECISION.LOG "${{ github.workspace }}/Artifact/PRECISION on ${{ matrix.os }}.LOG"
119 |
120 | - name: Upload artifacts
121 | uses: actions/upload-artifact@v4
122 | if: always()
123 | with:
124 | name: 'Precision ${{ matrix.os }}'
125 | path: '${{ github.workspace }}/Artifact'
126 |
127 | TestCpuUsageonHost:
128 | name: On Host
129 | strategy:
130 | fail-fast: false
131 | matrix:
132 | include:
133 | - os: windows-2025
134 | - os: windows-2022
135 | - os: windows-2019
136 | - os: ubuntu-20.04
137 | - os: ubuntu-22.04
138 | - os: ubuntu-24.04
139 | - os: macos-13
140 | - os: macos-14
141 | - os: macos-15
142 |
143 | runs-on: ${{ matrix.os }}
144 | steps:
145 | - name: Checkout
146 | uses: actions/checkout@v4
147 |
148 | - name: Environment
149 | run: 'printenv | sort'
150 |
151 | - name: Setup .NET Core
152 | uses: actions/setup-dotnet@v4
153 | with:
154 | dotnet-version: 8.0
155 |
156 | - name: .NET Info
157 | run: |
158 | script=https://raw.githubusercontent.com/devizer/test-and-build/master/install-build-tools-bundle.sh; (wget -q -nv --no-check-certificate -O - $script 2>/dev/null || curl -ksSL $script) | bash > /dev/null
159 | dotnet --info
160 |
161 | - name: TEST full (and build)
162 | run: |
163 | set -eu;
164 | Say ".NET SDK LIST"
165 | dotnet --list-sdks
166 | if [[ "$(uname -s)" == Linux ]]; then
167 | Say "Linux Kernel: [$(uname -r 2>/dev/null)]"
168 | fi
169 | TARGET_FRAMEWORKS_TEST="${TARGET_FRAMEWORKS_TEST:-net8.0}"
170 | cd Universe.CpuUsage.Tests
171 |
172 | Say "Building $TARGET_FRAMEWORKS_TEST"
173 | sed -i -E 's|.*|'$TARGET_FRAMEWORKS_TEST'|' *.csproj
174 | time try-and-retry dotnet build -c Release -f $TARGET_FRAMEWORKS_TEST >/dev/null 2>&1
175 | Say "Testing"
176 | dotnet test --no-build -c Release -f $TARGET_FRAMEWORKS_TEST
177 |
178 | - name: SHOW Precision Histogram
179 | run: |
180 | set -eu;
181 | Say "Linux Kernel: [$(uname -r 2>/dev/null)]"
182 | cd Universe.CpuUsage.Tests
183 | TARGET_FRAMEWORKS_TEST="${TARGET_FRAMEWORKS_TEST:-net8.0}"
184 | mkdir -p ../Artifact
185 | dotnet test --no-build -c Release -f $TARGET_FRAMEWORKS_TEST --filter "Name ~ Show_Precision_Histogram" | tee "../Artifact/PRECISION on ${{ matrix.os }}.LOG" &
186 |
187 | - name: Upload artifacts [${{ env.THEARTIFACTS_NATIVE }}]
188 | uses: actions/upload-artifact@v4
189 | if: always()
190 | with:
191 | name: 'Precision ${{ matrix.os }}'
192 | path: '${{ github.workspace }}/Artifact'
193 |
194 | Combine:
195 | name: Combine results in a single Artifact
196 | needs: [TestCpuUsageonHost, TestCpuUsageOnVm]
197 | if: always()
198 | runs-on: ubuntu-latest
199 | steps:
200 | - name: Download All Artifacts
201 | uses: actions/download-artifact@v4
202 | with:
203 | pattern: '**'
204 | path: "${{ runner.temp }}/Combined"
205 | merge-multiple: true
206 |
207 | - name: Show Download Structure
208 | run: 'sudo apt-get update -qq; sudo apt-get install tree -y -qq; tree $RUNNER_TEMP'
209 |
210 | - name: Upload Combined System Info
211 | uses: actions/upload-artifact@v4
212 | with:
213 | name: 'Combined CPU Usage Tests'
214 | path: "${{ runner.temp }}/Combined"
215 |
216 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | backup.cmd
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 | [Ll]og/
27 |
28 | # Visual Studio 2015/2017 cache/options directory
29 | .vs/
30 | # Uncomment if you have tasks that create the project's static files in wwwroot
31 | #wwwroot/
32 |
33 | # Visual Studio 2017 auto generated files
34 | Generated\ Files/
35 |
36 | # MSTest test Results
37 | [Tt]est[Rr]esult*/
38 | [Bb]uild[Ll]og.*
39 |
40 | # NUNIT
41 | *.VisualState.xml
42 | TestResult.xml
43 |
44 | # Build Results of an ATL Project
45 | [Dd]ebugPS/
46 | [Rr]eleasePS/
47 | dlldata.c
48 |
49 | # Benchmark Results
50 | BenchmarkDotNet.Artifacts/
51 |
52 | # .NET Core
53 | project.lock.json
54 | project.fragment.lock.json
55 | artifacts/
56 | **/Properties/launchSettings.json
57 |
58 | # StyleCop
59 | StyleCopReport.xml
60 |
61 | # Files built by Visual Studio
62 | *_i.c
63 | *_p.c
64 | *_i.h
65 | *.ilk
66 | *.meta
67 | *.obj
68 | *.iobj
69 | *.pch
70 | *.pdb
71 | *.ipdb
72 | *.pgc
73 | *.pgd
74 | *.rsp
75 | *.sbr
76 | *.tlb
77 | *.tli
78 | *.tlh
79 | *.tmp
80 | *.tmp_proj
81 | *.log
82 | *.vspscc
83 | *.vssscc
84 | .builds
85 | *.pidb
86 | *.svclog
87 | *.scc
88 |
89 | # Chutzpah Test files
90 | _Chutzpah*
91 |
92 | # Visual C++ cache files
93 | ipch/
94 | *.aps
95 | *.ncb
96 | *.opendb
97 | *.opensdf
98 | *.sdf
99 | *.cachefile
100 | *.VC.db
101 | *.VC.VC.opendb
102 |
103 | # Visual Studio profiler
104 | *.psess
105 | *.vsp
106 | *.vspx
107 | *.sap
108 |
109 | # Visual Studio Trace Files
110 | *.e2e
111 |
112 | # TFS 2012 Local Workspace
113 | $tf/
114 |
115 | # Guidance Automation Toolkit
116 | *.gpState
117 |
118 | # ReSharper is a .NET coding add-in
119 | _ReSharper*/
120 | *.[Rr]e[Ss]harper
121 | *.DotSettings.user
122 |
123 | # JustCode is a .NET coding add-in
124 | .JustCode
125 |
126 | # TeamCity is a build add-in
127 | _TeamCity*
128 |
129 | # DotCover is a Code Coverage Tool
130 | *.dotCover
131 |
132 | # AxoCover is a Code Coverage Tool
133 | .axoCover/*
134 | !.axoCover/settings.json
135 |
136 | # Visual Studio code coverage results
137 | *.coverage
138 | *.coveragexml
139 |
140 | # NCrunch
141 | _NCrunch_*
142 | .*crunch*.local.xml
143 | nCrunchTemp_*
144 |
145 | # MightyMoose
146 | *.mm.*
147 | AutoTest.Net/
148 |
149 | # Web workbench (sass)
150 | .sass-cache/
151 |
152 | # Installshield output folder
153 | [Ee]xpress/
154 |
155 | # DocProject is a documentation generator add-in
156 | DocProject/buildhelp/
157 | DocProject/Help/*.HxT
158 | DocProject/Help/*.HxC
159 | DocProject/Help/*.hhc
160 | DocProject/Help/*.hhk
161 | DocProject/Help/*.hhp
162 | DocProject/Help/Html2
163 | DocProject/Help/html
164 |
165 | # Click-Once directory
166 | publish/
167 |
168 | # Publish Web Output
169 | *.[Pp]ublish.xml
170 | *.azurePubxml
171 | # Note: Comment the next line if you want to checkin your web deploy settings,
172 | # but database connection strings (with potential passwords) will be unencrypted
173 | *.pubxml
174 | *.publishproj
175 |
176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
177 | # checkin your Azure Web App publish settings, but sensitive information contained
178 | # in these scripts will be unencrypted
179 | PublishScripts/
180 |
181 | # NuGet Packages
182 | *.nupkg
183 | # The packages folder can be ignored because of Package Restore
184 | **/[Pp]ackages/*
185 | # except build/, which is used as an MSBuild target.
186 | !**/[Pp]ackages/build/
187 | # Uncomment if necessary however generally it will be regenerated when needed
188 | #!**/[Pp]ackages/repositories.config
189 | # NuGet v3's project.json files produces more ignorable files
190 | *.nuget.props
191 | *.nuget.targets
192 |
193 | # Microsoft Azure Build Output
194 | csx/
195 | *.build.csdef
196 |
197 | # Microsoft Azure Emulator
198 | ecf/
199 | rcf/
200 |
201 | # Windows Store app package directories and files
202 | AppPackages/
203 | BundleArtifacts/
204 | Package.StoreAssociation.xml
205 | _pkginfo.txt
206 | *.appx
207 |
208 | # Visual Studio cache files
209 | # files ending in .cache can be ignored
210 | *.[Cc]ache
211 | # but keep track of directories ending in .cache
212 | !*.[Cc]ache/
213 |
214 | # Others
215 | ClientBin/
216 | ~$*
217 | *~
218 | *.dbmdl
219 | *.dbproj.schemaview
220 | *.jfm
221 | *.pfx
222 | *.publishsettings
223 | orleans.codegen.cs
224 |
225 | # Including strong name files can present a security risk
226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
227 | #*.snk
228 |
229 | # Since there are multiple workflows, uncomment next line to ignore bower_components
230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
231 | #bower_components/
232 |
233 | # RIA/Silverlight projects
234 | Generated_Code/
235 |
236 | # Backup & report files from converting an old project file
237 | # to a newer Visual Studio version. Backup files are not needed,
238 | # because we have git ;-)
239 | _UpgradeReport_Files/
240 | Backup*/
241 | UpgradeLog*.XML
242 | UpgradeLog*.htm
243 | ServiceFabricBackup/
244 | *.rptproj.bak
245 |
246 | # SQL Server files
247 | *.mdf
248 | *.ldf
249 | *.ndf
250 |
251 | # Business Intelligence projects
252 | *.rdl.data
253 | *.bim.layout
254 | *.bim_*.settings
255 | *.rptproj.rsuser
256 |
257 | # Microsoft Fakes
258 | FakesAssemblies/
259 |
260 | # GhostDoc plugin setting file
261 | *.GhostDoc.xml
262 |
263 | # Node.js Tools for Visual Studio
264 | .ntvs_analysis.dat
265 | node_modules/
266 |
267 | # Visual Studio 6 build log
268 | *.plg
269 |
270 | # Visual Studio 6 workspace options file
271 | *.opt
272 |
273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
274 | *.vbw
275 |
276 | # Visual Studio LightSwitch build output
277 | **/*.HTMLClient/GeneratedArtifacts
278 | **/*.DesktopClient/GeneratedArtifacts
279 | **/*.DesktopClient/ModelManifest.xml
280 | **/*.Server/GeneratedArtifacts
281 | **/*.Server/ModelManifest.xml
282 | _Pvt_Extensions
283 |
284 | # Paket dependency manager
285 | .paket/paket.exe
286 | paket-files/
287 |
288 | # FAKE - F# Make
289 | .fake/
290 |
291 | # JetBrains Rider
292 | .idea/
293 | *.sln.iml
294 |
295 | # CodeRush
296 | .cr/
297 |
298 | # Python Tools for Visual Studio (PTVS)
299 | __pycache__/
300 | *.pyc
301 |
302 | # Cake - Uncomment if you are using it
303 | # tools/**
304 | # !tools/packages.config
305 |
306 | # Tabs Studio
307 | *.tss
308 |
309 | # Telerik's JustMock configuration file
310 | *.jmconfig
311 |
312 | # BizTalk build output
313 | *.btp.cs
314 | *.btm.cs
315 | *.odx.cs
316 | *.xsd.cs
317 |
318 | # OpenCover UI analysis results
319 | OpenCover/
320 |
321 | # Azure Stream Analytics local run output
322 | ASALocalRun/
323 |
324 | # MSBuild Binary and Structured Log
325 | *.binlog
326 |
327 | # NVidia Nsight GPU debugger configuration file
328 | *.nvuser
329 |
330 | # MFractors (Xamarin productivity tool) working folder
331 | .mfractor/
332 |
--------------------------------------------------------------------------------
/.idea/.idea.Universe.CpuUsage/.idea/runConfigurations/Benchmarks_Core_2_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/.idea.Universe.CpuUsage/.idea/runConfigurations/Benchmarks_NET_4_7.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: shell
2 |
3 | git:
4 | depth: false
5 |
6 | # mono:
7 | # - latest
8 |
9 | matrix:
10 | include:
11 |
12 | # - name: Linux PowerPC 64 (mono)
13 | # os: linux
14 | # dist: xenial
15 | # env:
16 | # - MULTIARCH_IMAGE="multiarch/debian-debootstrap:ppc64el-stretch"
17 | # services:
18 | # - docker
19 |
20 | - name: OS X 10.14 (.net core & mono)
21 | os: osx
22 | osx_image: xcode10.2
23 |
24 | - name: Native ARM64 Ubuntu 18.04 (.net core & mono)
25 | os: linux
26 | dist: bionic
27 | arch: arm64
28 |
29 | - name: Linux ARM-64 (.net core & mono)
30 | os: linux
31 | dist: xenial
32 | env:
33 | - MULTIARCH_IMAGE="multiarch/debian-debootstrap:arm64-stretch"
34 | services:
35 | - docker
36 |
37 | - name: Linux ARM-v7 (mono)
38 | os: linux
39 | dist: xenial
40 | env:
41 | - MULTIARCH_IMAGE="multiarch/debian-debootstrap:armhf-buster"
42 | services:
43 | - docker
44 |
45 | - name: Linux i386 (mono)
46 | os: linux
47 | dist: xenial
48 | env:
49 | - MULTIARCH_IMAGE="multiarch/debian-debootstrap:i386-stretch"
50 | services:
51 | - docker
52 |
53 | - name: OS X 10.10 (mono)
54 | os: osx
55 | osx_image: xcode6.4
56 |
57 | # - name: OS X 10.12
58 | # os: osx
59 | # osx_image: xcode8.3
60 | # - name: OS X 10.13
61 | # os: osx
62 | # osx_image: xcode9.3
63 |
64 | # https://docs.travis-ci.com/user/caching/
65 | before_script:
66 | - export SKIP_POSIXRESOURCESUSAGE_ASSERTS=True
67 | - 'script=https://raw.githubusercontent.com/devizer/test-and-build/master/install-build-tools-bundle.sh; (wget -q -nv --no-check-certificate -O - $script 2>/dev/null || curl -ksSL $script) | bash'
68 | - |
69 | OS_X_VER=$(sw_vers 2>/dev/null | grep BuildVer | awk '{print $2}' | cut -c1-2 || true); OS_X_VER=$((OS_X_VER-4))
70 |
71 | - pushd Tests4Mono; . install-dotnet.sh; popd
72 | - dotnet --info || true
73 | - mono --version || true
74 | - 'mono --version; msbuild /version; echo ""; nuget 2>&1 | head -4 || true'
75 | - 'printenv | sort'
76 | - 'bash Info/show-platform.sh'
77 |
78 | script:
79 | - export DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0
80 | - export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1; export DOTNET_CLI_TELEMETRY_OPTOUT=1;
81 | - export CI=false
82 | - |
83 | # Restore
84 | Say "DOTNET RESTORE"
85 | dotnet restore || dotnet restore --disable-parallel || true
86 | Say "NUGET RESTORE"
87 | nuget restore -verbosity quiet || true
88 |
89 | - '[ $OS_X_VER -ge 13 ] && Say "DOTNET TEST (core 3.0, release)" && (dotnet test -f netcoreapp3.0 -c Release | cat) || true'
90 | - '[ $OS_X_VER -ge 13 ] && Say "MAC OS BENCHMARK (core 3.0, release)" && (time bash benchmark.linux.sh) || true'
91 |
92 | - |
93 | if [[ -n "$MULTIARCH_IMAGE" ]]; then
94 | set -e
95 |
96 | Say "Daemonizing ${MULTIARCH_IMAGE} image as 'tests' container"
97 | docker run --rm --privileged multiarch/qemu-user-static:register --reset
98 | docker run -d -h debian-multiarch-tests --name tests -t "${MULTIARCH_IMAGE}" bash -c 'sleep 424242'
99 |
100 | Say "Installing mono in the QEMU"
101 | docker cp Tests4Mono/install-dotnet.sh tests:/
102 | docker exec tests bash /install-dotnet.sh
103 |
104 | Say "BUILDING MATRIX"
105 | cd Tests4Mono; source build-the-matrix.sh; echo $matrix_run; cd ..
106 |
107 | Say "Runnig test matrix"
108 | pushd $matrix
109 | docker exec tests mkdir -p $matrix
110 | docker cp ./. tests:$(pwd)
111 | sudo docker exec tests bash -ec "export SKIP_POSIXRESOURCESUSAGE_ASSERTS=$SKIP_POSIXRESOURCESUSAGE_ASSERTS; $matrix_run"
112 |
113 | else
114 | # MAC OS (check renice without sudo), both 14th and 10th
115 | echo "Starting tests using Mono in [$(pwd)]"
116 | cd Tests4Mono; set +e; source build-the-matrix.sh; echo $matrix_run
117 | # need -e -c
118 | bash -e -c "$matrix_run"
119 | fi
120 |
121 | - 'Show-System-Stat || true'
122 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
4 |
5 | // List of extensions which should be recommended for users of this workspace.
6 | "recommendations": [
7 | "ms-dotnettools.csharp"
8 | ],
9 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
10 | "unwantedRecommendations": [
11 | ]
12 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to find out which attributes exist for C# debugging
3 | // Use hover for the description of the existing attributes
4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Benchmark (console)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build-release",
12 | // If you have changed target frameworks, make sure to update the program path.
13 | "program": "${workspaceFolder}/Universe.CpuUsage.Banchmark/bin/Release/netcoreapp3.0/Universe.CpuUsage.Banchmark.exe",
14 | "args": ["--dry"],
15 | "cwd": "${workspaceFolder}/Universe.CpuUsage.Banchmark/bin/Release/netcoreapp3.0",
16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
17 | "console": "internalConsole",
18 | "stopAtEntry": false,
19 | "logging": {"engineLogging": false}
20 | },
21 | {
22 | "name": ".NET Core Attach",
23 | "type": "coreclr",
24 | "request": "attach",
25 | "processId": "${command:pickProcess}"
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "Build using SDK 3.1 (release)",
6 | "command": "dotnet build -c Release -f netcoreapp3.1",
7 | "type": "shell",
8 | "problemMatcher": "$msCompile",
9 | "group": {
10 | "kind": "build",
11 | "isDefault": true
12 | }
13 | },
14 | {
15 | "label": "Test using SDK 3.1 (release)",
16 | "command": "dotnet test -c Debug -f netcoreapp3.1 Universe.CpuUsage.Tests\\Universe.CpuUsage.Tests.csproj",
17 | "type": "shell",
18 | "problemMatcher": "$msCompile",
19 | "group": {
20 | "kind": "test",
21 | "isDefault": true
22 | }
23 | }
24 | ]
25 | }
--------------------------------------------------------------------------------
/FULL-TIME-REPORT.sh:
--------------------------------------------------------------------------------
1 | export FULL_TIME="
2 | TIME:
3 | Elapsed real time %E
4 | Elapsed real time %e
5 | Total number of CPU-seconds that the process spent in kernel mode %S
6 | Total number of CPU-seconds that the process spent in user mode %U
7 | Percentage of the CPU that this job got %P
8 |
9 | MEMORY:
10 | Maximum resident set size of the process during its lifetime (in Kbytes) %M
11 | Average resident set size of the process (in Kbytes) %t
12 | Average total (data+stack+text) memory use of the process (in Kbytes) %K
13 | Average size of the process's unshared data area (in Kbytes) %D
14 | Average size of the process's unshared stack space (in Kbytes) %p
15 | Average size of the process's shared text space (in Kbytes) %X
16 | System's page size, in bytes. This is a per-system constant, but varies between systems. %Z
17 | Number of major page faults. %F
18 | Number of minor page faults. %R
19 | Number of times the process was swapped out of main memory. %W
20 | Number of times the process was context-switched involuntarily (because the time slice expired). %c
21 | Number of waits: times that the program was context-switched voluntarily, for instance while waiting for an I/O operation to complete. %w
22 |
23 | I/O:
24 | Number of file system inputs by the process. %I
25 | Number of file system outputs by the process. %O
26 | Number of socket messages received by the process. %r
27 | Number of socket messages sent by the process. %s
28 | Number of signals delivered to the process. %k
29 | Name and command-line arguments of the command being timed. [%C]
30 | Exit status of the command. %x
31 | "
32 | # sudo apt-get install time -y
33 |
--------------------------------------------------------------------------------
/Info/Benchmark.txt:
--------------------------------------------------------------------------------
1 |
2 | Linux 4.15
3 | | Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
4 | |------------ |----------:|---------:|---------:|-----:|-------:|------:|------:|----------:|
5 | | DateTimeNow | 150.24 ns | 2.346 ns | 2.194 ns | 2 | - | - | - | - |
6 | | Stopwatch | 77.27 ns | 0.473 ns | 0.419 ns | 1 | 0.0095 | - | - | 40 B |
7 | | ByProcess | 795.18 ns | 8.000 ns | 6.681 ns | 3 | - | - | - | - |
8 | | ByThread | 834.59 ns | 9.324 ns | 8.722 ns | 4 | - | - | - | - |
9 |
10 | Linux 32 bit @ ARM 3.4 (Orange PI, H3, 1200 MHz)
11 | | Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
12 | |------------ |---------:|----------:|----------:|-----:|-------:|------:|------:|----------:|
13 | | DateTimeNow | 2.788 us | 0.0595 us | 0.0944 us | 2 | - | - | - | - |
14 | | Stopwatch | 1.737 us | 0.0539 us | 0.0504 us | 1 | 0.1945 | - | - | 32 B |
15 | | ByProcess | 5.552 us | 0.1662 us | 0.4900 us | 3 | - | - | - | - |
16 | | ByThread | 5.664 us | 0.1136 us | 0.1960 us | 3 | - | - | - | - |
17 |
18 |
19 | OSX 10.13
20 | | Method | Mean | Error | StdDev | Median | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
21 | |------------ |------------:|----------:|----------:|------------:|-----:|-------:|------:|------:|----------:|
22 | | DateTimeNow | 186.70 ns | 1.233 ns | 1.093 ns | 186.79 ns | 2 | - | - | - | - |
23 | | Stopwatch | 72.02 ns | 1.474 ns | 3.560 ns | 70.45 ns | 1 | 0.0095 | - | - | 40 B |
24 | | ByProcess | 1,906.05 ns | 47.314 ns | 44.258 ns | 1,892.13 ns | 3 | - | - | - | - |
25 | | ByThread | 1,968.98 ns | 41.026 ns | 69.665 ns | 1,945.56 ns | 4 | - | - | - | - |
26 |
27 | OSX 10.13
28 | | Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
29 | |------------ |------------:|----------:|----------:|-----:|-------:|------:|------:|----------:|
30 | | DateTimeNow | 73.40 ns | 1.506 ns | 2.475 ns | 1 | - | - | - | - |
31 | | Stopwatch | 79.36 ns | 1.640 ns | 1.454 ns | 2 | 0.0025 | - | - | 40 B |
32 | | ByProcess | 1,979.10 ns | 29.771 ns | 26.391 ns | 4 | - | - | - | - |
33 | | ByThread | 1,921.54 ns | 35.869 ns | 33.552 ns | 3 | - | - | - | - |
34 |
35 | Windows
36 | | Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
37 | |------------ |----------:|---------:|---------:|-----:|-------:|------:|------:|----------:|
38 | | DateTimeNow | 217.08 ns | 1.667 ns | 1.559 ns | 4 | - | - | - | - |
39 | | Stopwatch | 31.12 ns | 0.203 ns | 0.169 ns | 1 | 0.0095 | - | - | 40 B |
40 | | ByProcess | 200.49 ns | 3.743 ns | 3.501 ns | 2 | - | - | - | - |
41 | | ByThread | 205.11 ns | 3.970 ns | 4.413 ns | 3 | - | - | - | - |
42 |
--------------------------------------------------------------------------------
/Info/TODO-on-Linux.txt:
--------------------------------------------------------------------------------
1 | http://man7.org/linux/man-pages/man2/getrusage.2.html
2 | https://unix.stackexchange.com/questions/442969/what-exactly-are-voluntary-context-switches
3 |
4 | x64:
5 |
6 | #5 {Show_Raw_Process_Usage} @ LinuxResourcesUsage_Tests starting...
7 | getrusage returns 0
8 | GetRawUsageResources(RUSAGE_SELF):
9 | u_sec 0:11, u_usec 1:612017, k_sec 2:1, k_usec 3:767126,
10 | maxrss 4:899992, ixrss 5:0, idrss 6:0, isrss 7:0,
11 | minflt 8:693381, majflt 9:1, nswap 10:0, inblock 11:25424,
12 | oublock 12:21512, msgsnd 13:0, msgrcv 14:0, nsignals 15:0,
13 | nvcsw 16:2074, nivcsw 17:3717
14 | #5 {Show_Raw_Process_Usage} >PASSED< in 00:00:00.0101869 (cpu: 79%, 8.045 = 8.045 [user] + 0.000 [kernel] milliseconds)
15 |
16 | #6 {Show_Raw_Thread_Usage} @ LinuxResourcesUsage_Tests starting...
17 | getrusage returns 0
18 | GetRawUsageResources(RUSAGE_THREAD):
19 | u_sec 0:10, u_usec 1:521780, k_sec 2:1, k_usec 3:486560,
20 | maxrss 4:900276, ixrss 5:0, idrss 6:0, isrss 7:0,
21 | minflt 8:650550, majflt 9:0, nswap 10:0, inblock 11:18384,
22 | oublock 12:17672, msgsnd 13:0, msgrcv 14:0, nsignals 15:0,
23 | nvcsw 16:372, nivcsw 17:2248
24 | #6 {Show_Raw_Thread_Usage} >PASSED< in 00:00:00.0009432 (cpu: 71%, 0.671 = 0.671 [user] + 0.000 [kernel] milliseconds)
--------------------------------------------------------------------------------
/Info/express.test.sh:
--------------------------------------------------------------------------------
1 | docker run -it --rm ubuntu bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update; apt-get install git curl sudo mc -y;
2 | git clone https://github.com/devizer/Universe.CpuUsage; cd Universe.CpuUsage;
3 | cd Universe.CpuUsage/Universe.CpuUsage.Tests;
4 | script=https://raw.githubusercontent.com/devizer/test-and-build/master/lab/install-DOTNET.sh; (wget -q -nv --no-check-certificate -O - $script 2>/dev/null || curl -ksSL $script) | bash;
5 | dotnet test -f netcoreapp3.1;
6 | bash'
7 |
8 |
--------------------------------------------------------------------------------
/Info/show-platform.sh:
--------------------------------------------------------------------------------
1 | files='
2 | /sys/devices/virtual/dmi/id/chassis_vendor
3 | /sys/devices/virtual/dmi/id/sys_vendor
4 | /sys/devices/virtual/dmi/id/board_vendor
5 |
6 | /sys/devices/virtual/dmi/id/product_name
7 | /sys/devices/virtual/dmi/id/board_name
8 |
9 | /sys/firmware/devicetree/base/model
10 | /proc/device-tree/model
11 | '
12 | for f in $files; do
13 | echo "$f: [$(sudo cat $f 2>/dev/null)]"
14 | done
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 devizer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Universe.CpuUsage [](https://www.nuget.org/packages/Universe.CpuUsage/)
2 | CPU Usage for .NET Core, .NET Framework and Mono on Linux, Windows and macOS
3 |
4 | ## Crossplatform CPU Usage metrics
5 | It receives the amount of time that the current **thread(s)/process** has executed in _**kernel**_ and _**user**_ mode.
6 |
7 | Works everywhere: Linux, OSX and Windows.
8 |
9 | Targets everywhere: Net Framework 2.0+, Net Standard 1.3+, Net Core 1.0+
10 |
11 | ## Coverage and supported OS
12 | Minimum OS requirements: Linux Kernel 2.6.26, Mac OS 10.9, Windows XP/2003
13 |
14 | CPU Usage for sub-tasks and sub-threads requires NET Core, NET Framework 4.6+
15 |
16 | ### Autotests using .NET Core and .NET Framework cover:
17 | - Linux running on X64, arm64, armhf, armv5 and i386, kernel varies 3.16 ... 6.8
18 | - Windows Server 2016 ... 2025
19 | - macOS on X64 and Apple Silicon
20 |
21 | ### Manually tested on:
22 | - Termux on non-rooted arm64 phone.
23 | - WSL v1 and v2
24 | - Windows 10 ARM64 (.NET Core)
25 | - FreeBSD 12 (both .NET Core and Mono).
26 |
27 |
28 | | appveyor | travis-ci |
29 | |----------------------------|-------------------------------------------------------------------------------------------|
30 | | .NET Core: **Linux** x64, **Windows** x64.
Mono: **Linux** x64. | .NET Core: **macOS** 10.14, **Linux** Arm 64.
Mono: **Linux** Arm 64, Arm-v7, i386, **macOS** 10.10. |
31 | |
[](https://ci.appveyor.com/project/devizer/universe-cpuusage)
|
[](https://travis-ci.org/devizer/Universe.CpuUsage)
|
32 |
33 | ### Integration tests on exotic platforms
34 | For ***mono only*** platforms (i386, ppc64, mips, arm v5/v6, etc) here is the script for integration tests: [test-on-mono-only-platforms.sh](https://raw.githubusercontent.com/devizer/Universe.CpuUsage/master/test-on-mono-only-platforms.sh)
35 | ```
36 | url=https://raw.githubusercontent.com/devizer/Universe.CpuUsage/master/test-on-mono-only-platforms.sh;
37 | (wget -q -nv --no-check-certificate -O - $url 2>/dev/null || curl -ksSL $url) | bash
38 | ```
39 |
40 | ## Implementation
41 | The implementation utilizes platform invocation (P/Invoke) of the corresponding system libraries depending on the OS:
42 |
43 | | OS | per thread implementation | per process implementation | library |
44 | |----------|--------------------------|------------------------|-----------------|
45 | | Linux | getrusage(RUSAGE_THREAD) | getrusage(RUSAGE_SELF) | libc.so |
46 | | Windows | GetThreadTimes() | GetProcessTimes() | kernel32.dll |
47 | | Mac OS X | thread_info() | getrusage(RUSAGE_SELF) | libSystem.dylib |
48 |
49 | ## Precision depends on
50 | Here is a summary of CPU usage precision. In general, it depends on OS and version and it does not depend on CPU performance except of FreeBSD
51 |
52 | | OS | Average Precision |
53 | |----------------------------------------------------------------------------|------------------:|
54 | | Windows Server 2025, AMD EPYC 7763 | _15,625 μs_ |
55 | | Windows Server 2019, Xeon E5-2697 v3 | _16,250 μs_ |
56 | | Linux, i5-10300, 4.0 GHz, kernel 6.8 | _1,000 μs_ |
57 | | Ubuntu, x86-64 QEMU, kernel 6.8 | _1,000 μs_ |
58 | | Debian, x86-64 QEMU, kernel 6.10 | _4,000 μs_ |
59 | | Ubuntu, arm64 QEMU, kernel 6.8 | _1,000 μs_ |
60 | | Debian, arm64 QEMU, kernel 6.10 | _4,000 μs_ |
61 | | Linux, Xeon E5-2697 v3 @ 2.60GHz, kernel 5.0 | _4,000 μs_ |
62 | | Linux, ARMv7 H3 CPU, 1.50GHz, kernel 3.4 | _10,000 μs_ |
63 | | Linux, ARMv7 H3 CPU, 1.10GHz, kernel 4.19 | _4,000 μs_ |
64 | | Linux, ARMv7 H3 CPU, 1.30GHz, kernel 6.13 | _4,000 μs_ |
65 | | Mac OS 10.14, Xeon E5-2697 v2 @ 2.70GHz | 14.0 μs |
66 | | Mac OS 15, Apple M1 | 3.0 μs |
67 | | FreeBSD 12, .NET Core, Xeon E3-1270 v2 @ 3.50GHz, pseudo kernel 2.6.32 | 3.0 μs |
68 | | FreeBSD 12, Mono 5.1, Xeon E3-1270 v2 @ 3.50GHz, native BSD 12 | 1.9 μs |
69 |
70 | Detailed histograms of precision are produced by PrecisionTest.cs
71 |
72 | ## Low level API: class CpuUsage
73 | ```csharp
74 | var onStart = CpuUsage.GetByThread();
75 | ...
76 | var onEnd = CpuUsage.GetByThread();
77 | Console.WriteLine("CPU Usage: " + (onEnd-onStart));
78 | ```
79 |
80 | ## High level API: class CpuUsageAsyncWatcher
81 | ```csharp
82 | public void Configure(IApplicationBuilder app)
83 | {
84 | // Here is a "middleware" that displays total CPU usage
85 | // of all the Tasks executed by ASP.NET Core pipeline during each http request
86 | app.Use(async (context, next) =>
87 | {
88 | CpuUsageAsyncWatcher watcher = new CpuUsageAsyncWatcher();
89 | await next.Invoke();
90 | watcher.Stop();
91 | Console.WriteLine($"Cpu Usage by http request is {watcher.GetSummaryCpuUsage()}");
92 | });
93 | }
94 | ```
95 |
96 | ## Benchmark
97 | Just for illustration here is a comparison to well known DateTime.Now and Stopwatch using benchmark.net. All of them are taken using .NET Core 3.0 runtime.
98 |
99 | #### Linux x64, kernel 4.15
100 | | Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
101 | |------------ |----------:|---------:|---------:|-----:|-------:|------:|------:|----------:|
102 | | DateTime.Now() | 150.24 ns | 2.346 ns | 2.194 ns | 2 | - | - | - | - |
103 | | Stopwatch | 77.27 ns | 0.473 ns | 0.419 ns | 1 | 0.0095 | - | - | 40 B |
104 | | Process CPU Usage | 795.18 ns | 8.000 ns | 6.681 ns | 3 | - | - | - | - |
105 | | Thread CPU Usage| 834.59 ns | 9.324 ns | 8.722 ns | 4 | - | - | - | - |
106 |
107 | #### Linux 32 bit @ ARM, kernel 3.4 (Orange PI, H3, 1500 MHz)
108 | | Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
109 | |------------ |---------:|----------:|----------:|-----:|-------:|------:|------:|----------:|
110 | | DateTime.Now | 2.788 μs | 0.0595 μs | 0.0944 μs | 2 | - | - | - | - |
111 | | Stopwatch | 1.737 μs | 0.0539 μs | 0.0504 μs | 1 | 0.1945 | - | - | 32 B |
112 | | Process CPU Usage | 5.552 μs | 0.1662 μs | 0.4900 μs | 3 | - | - | - | - |
113 | | Thread CPU Usage | 5.664 μs | 0.1136 μs | 0.1960 μs | 3 | - | - | - | - |
114 |
115 | #### macOS 10.14
116 | | Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
117 | |------------ |------------:|----------:|----------:|-----:|-------:|------:|------:|----------:|
118 | | DateTime.Now() | 73.40 ns | 1.506 ns | 2.475 ns | 1 | - | - | - | - |
119 | | Stopwatch | 79.36 ns | 1.640 ns | 1.454 ns | 2 | 0.0025 | - | - | 40 B |
120 | | Process CPU Usage | 1,979.10 ns | 29.771 ns | 26.391 ns | 4 | - | - | - | - |
121 | | Thread CPU Usage | 1,921.54 ns | 35.869 ns | 33.552 ns | 3 | - | - | - | - |
122 |
123 | #### Windows Server 2016
124 | | Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
125 | |------------ |----------:|---------:|---------:|-----:|-------:|------:|------:|----------:|
126 | | DateTime.Now() | 217.08 ns | 1.667 ns | 1.559 ns | 4 | - | - | - | - |
127 | | Stopwatch | 31.12 ns | 0.203 ns | 0.169 ns | 1 | 0.0095 | - | - | 40 B |
128 | | Process CPU Usage | 200.49 ns | 3.743 ns | 3.501 ns | 2 | - | - | - | - |
129 | | Thread CPU Usage | 205.11 ns | 3.970 ns | 4.413 ns | 3 | - | - | - | - |
130 |
131 | ##### Legend
132 | - Stopwatch: `var sw = new Stopwatch(); var ticks = sw.ElapsedTicks;`
133 | - Process CPU Usage: `CpuUsageReader.GetByProcess();`
134 | - Thread CPU Usage: `CpuUsageReader.GetByThread();`
135 | - ns - nanosecond, μs - microsecond
136 |
--------------------------------------------------------------------------------
/Shared/Universe.CpuUsage.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devizer/Universe.CpuUsage/5c5c67794ad0592460f15d972a8afe76362c4a9f/Shared/Universe.CpuUsage.snk
--------------------------------------------------------------------------------
/Shared/Version.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.42
5 | 1.42
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Tests4Mono/On-Local-Mac-or-Linux.sh:
--------------------------------------------------------------------------------
1 | work=$HOME/build/devizer; mkdir -p $work; cd $work; rm -rf $work/Universe.CpuUsage; git clone https://github.com/devizer/Universe.CpuUsage; cd Universe.CpuUsage/Tests4Mono;
2 | git pull; set +e; source build-the-matrix.sh; echo $matrix_run; bash -e -c "$matrix_run"
3 |
--------------------------------------------------------------------------------
/Tests4Mono/Universe.CpuUsage.MonoTests.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29326.143
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Universe.CpuUsage.MonoTests", "Universe.CpuUsage.MonoTests\Universe.CpuUsage.MonoTests.csproj", "{CCCAD110-24BE-4056-B1E0-F87F80280195}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {CCCAD110-24BE-4056-B1E0-F87F80280195}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {CCCAD110-24BE-4056-B1E0-F87F80280195}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {CCCAD110-24BE-4056-B1E0-F87F80280195}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {CCCAD110-24BE-4056-B1E0-F87F80280195}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {8A4C2E86-6430-4C7F-B905-69CD89BEF0D8}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/Tests4Mono/Universe.CpuUsage.MonoTests/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Tests4Mono/Universe.CpuUsage.MonoTests/FirstTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Mono.Cecil;
4 | using NUnit.Framework;
5 |
6 | namespace Universe.CpuUsage.MonoTests
7 | {
8 | [TestFixture]
9 | public class FirstTest
10 | {
11 | [Test]
12 | public void Smoke_Test()
13 | {
14 | Show_CpuUsage_Version();
15 | CpuUsage? byThread = CpuUsage.GetByThread();
16 | CpuUsage? byProcess = CpuUsage.GetByProcess();
17 | TestContext.Progress.WriteLine($"Process's CPU Usage : {byProcess}");
18 | TestContext.Progress.WriteLine($"Thread's CPU Usage : {byThread}");
19 | }
20 |
21 | [Test]
22 | public void Show_CpuUsage_Version()
23 | {
24 | var asm = typeof(CpuUsage).Assembly;
25 | TestContext.Progress.WriteLine($"CpuUsage Version : {asm.GetName().Version}");
26 | TestContext.Progress.WriteLine($"Its Target Framework : '{GetTargetFramework(asm.Location)}'");
27 | }
28 |
29 |
30 |
31 | [Test/*, Ignore("Experimental")*/]
32 | public void Show_TargetFramework_ByCecil()
33 | {
34 | var fileName = typeof(CpuUsage).Assembly.Location;
35 |
36 | TryAndForget("AssemblyDefinition.ReadAssembly()", () =>
37 | {
38 | var asmDef = AssemblyDefinition.ReadAssembly(fileName);
39 | ShowProperties(asmDef, 3);
40 | TestContext.Progress.WriteLine(" ");
41 |
42 | foreach (var attr in asmDef.CustomAttributes)
43 | {
44 | TestContext.Progress.WriteLine($"Attribute {attr.AttributeType}");
45 | ShowProperties(attr, 6);
46 | foreach (var prop in attr.Properties)
47 | {
48 | TestContext.Progress.WriteLine($" Property [{prop.Argument.Type}] {prop.Name}: '{prop.Argument.Value}'");
49 | }
50 | TestContext.Progress.WriteLine(" ");
51 | }
52 |
53 |
54 | });
55 | }
56 |
57 | [Test, Ignore("Experimental")]
58 | public void Show_Metatdata_ByCecil()
59 | {
60 | var fileName = typeof(CpuUsage).Assembly.Location;
61 | TestContext.Progress.WriteLine($"CpuUsage Location: {fileName}");
62 |
63 | TryAndForget("ModuleDefinition.ReadModule()", () =>
64 | {
65 | ModuleDefinition module = ModuleDefinition.ReadModule(fileName);
66 | ShowProperties(module, 4);
67 | TestContext.Progress.WriteLine(" ");
68 |
69 |
70 | foreach (CustomAttribute moduleCustomAttribute in module.CustomAttributes)
71 | {
72 | TestContext.Progress.WriteLine($"Attribute: {moduleCustomAttribute.GetType()}");
73 | ShowProperties(moduleCustomAttribute, 4);
74 | TestContext.Progress.WriteLine(" ");
75 | }
76 | });
77 | }
78 |
79 | void ShowProperties(object obj, int indent)
80 | {
81 | if (obj == null) return;
82 | string pre = indent > 0 ? new string(' ', indent) : "";
83 | var props = obj.GetType().GetProperties();
84 | foreach (var propertyInfo in props)
85 | {
86 | TryAndForget(null, () =>
87 | {
88 | TestContext.Progress.WriteLine($"{pre}{propertyInfo.Name}: {propertyInfo.GetValue(obj)}");
89 | });
90 | }
91 | }
92 |
93 | void TryAndForget(string caption, Action action)
94 | {
95 | try
96 | {
97 | action();
98 | }
99 | catch (Exception ex)
100 | {
101 | if (caption != null)
102 | Console.WriteLine($"Fail. {caption}. {ex.GetType().Name}: {ex.Message}");
103 | }
104 |
105 | }
106 |
107 | string GetTargetFramework(string fileName)
108 | {
109 | var asmDef = AssemblyDefinition.ReadAssembly(fileName);
110 | const string attrName = "System.Runtime.Versioning.TargetFrameworkAttribute";
111 | var tfa = asmDef.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == attrName);
112 | return tfa?.Properties.FirstOrDefault().Argument.Value?.ToString();
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/Tests4Mono/Universe.CpuUsage.MonoTests/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Console = System.Console;
7 |
8 | namespace Universe.CpuUsage.MonoTests
9 | {
10 | class Program
11 | {
12 | static void Main(string[] args)
13 | {
14 | Console.WriteLine($"The Platform: {CrossInfo.ThePlatform}");
15 | Console.WriteLine($"Process: {IntPtr.Size * 8} bits");
16 | Console.WriteLine($"CPU Usage by Process: {CpuUsage.GetByProcess()}");
17 | Console.WriteLine($"CPU Usage by Thread: {CpuUsage.GetByThread()}");
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Tests4Mono/Universe.CpuUsage.MonoTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Universe.CpuUsage.MonoTests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Universe.CpuUsage.MonoTests")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("cccad110-24be-4056-b1e0-f87f80280195")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Tests4Mono/Universe.CpuUsage.MonoTests/Universe.CpuUsage.MonoTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Debug
8 | AnyCPU
9 | {CCCAD110-24BE-4056-B1E0-F87F80280195}
10 | Exe
11 | Universe.CpuUsage.MonoTests
12 | Universe.CpuUsage.MonoTests
13 | v4.7
14 | 512
15 | true
16 | true
17 |
18 |
19 |
20 |
21 | AnyCPU
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 |
30 |
31 | AnyCPU
32 | pdbonly
33 | true
34 | bin\Release\
35 | TRACE
36 | prompt
37 | 4
38 |
39 |
40 |
41 | ..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.dll
42 |
43 |
44 | ..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.Mdb.dll
45 |
46 |
47 | ..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.Pdb.dll
48 |
49 |
50 | ..\packages\Mono.Cecil.0.11.1\lib\net40\Mono.Cecil.Rocks.dll
51 |
52 |
53 | ..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll
54 |
55 |
56 | ..\packages\ParallelExtensionsExtras.1.2.0.0\lib\net40\ParallelExtensionsExtras.dll
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | ..\bin\net46\Universe.CpuUsage.dll
68 |
69 |
70 |
71 |
72 | LinkedTests\AsyncSchedulerCases.cs
73 |
74 |
75 | LinkedTests\CpuLoader.cs
76 |
77 |
78 | LinkedTests\CpuUsageAsyncWatcher_AsyncStreamTests.cs
79 |
80 |
81 | LinkedTests\CpuUsageAsyncWatcher_Tests.cs
82 |
83 |
84 | FullCrossInfo\CrossInfo.cs
85 |
86 |
87 | LinkedTests\CrossInfo_Tests.cs
88 |
89 |
90 | LinkedTests\LinuxKernelCacheFlusher.cs
91 |
92 |
93 | LinkedTests\LinuxResourcesUsage_Reader_Tests.cs
94 |
95 |
96 | LinkedTests\NUnitTestsBase.cs
97 |
98 |
99 | LinkedTests\PosixProcessPriority.cs
100 |
101 |
102 | LinkedTests\PosixResourcesUsage_Tests.cs
103 |
104 |
105 | LinkedTests\PrecisionTest.cs
106 |
107 |
108 | LinkedTests\RisingTest.cs
109 |
110 |
111 | LinkedTests\SmokeCpuUsage.cs
112 |
113 |
114 | LinkedTests\Statistica\HistogramReport.cs
115 |
116 |
117 | LinkedTests\Statistica\PercentileCalc.cs
118 |
119 |
120 | LinkedTests\Statistica\Statistica.cs
121 |
122 |
123 | LinkedTests\Test_IsSupported.cs
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/Tests4Mono/Universe.CpuUsage.MonoTests/ZeroTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace Universe.CpuUsage.MonoTests
4 | {
5 |
6 | [TestFixture]
7 | public class ZeroTest
8 | {
9 | [Test]
10 | public void DoNothing()
11 | {
12 |
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Tests4Mono/Universe.CpuUsage.MonoTests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Tests4Mono/build-the-matrix.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # work=$HOME/build/devizer; mkdir -p $work; cd $work; rm -rf Universe.CpuUsage; git clone https://github.com/devizer/Universe.CpuUsage; cd Universe.CpuUsage/Tests4Mono; source build-the-matrix.sh; echo $matrix_run; bash -c "$matrix_run" | tee ~/cpu-usage-tests.log
3 |
4 | # set -e
5 |
6 | # pushd "$(dirname "$0")" >/dev/null; SCRIPT="$(pwd)"; popd >/dev/null
7 | SCRIPT="$(pwd)"
8 | SayScript="$(pwd)/say.include.sh"
9 | pushd ..; StartFrom=$(pwd); popd
10 | source "$SayScript"
11 |
12 | echo '
13 |
14 |
15 |
16 |
17 |
18 | ' > nuget.config
19 |
20 |
21 | matrix=~/build/devizer/MATRIX-Universe.CpuUsage
22 | mkdir -p bin $matrix
23 | bin_path=$(pwd)/bin
24 | restore_path=$(pwd)/obj/RESTORE
25 | mkdir -p $restore_path; rm -rf $restore_path/* || true
26 | Say "Matrix Path: $matrix"
27 | Say "Library path: $bin_path"
28 |
29 | cd $restore_path
30 | Say "Nuget Restore Universe.CpuUsage to [$(pwd)]"
31 | nuget install Universe.CpuUsage -verbosity quiet || nuget install Universe.CpuUsage -verbosity quiet || true
32 | pushd Universe.CpuUsage*/lib
33 | copyto=$(pwd)
34 | popd
35 | RAOT_VER=3.0.2
36 | Say "Nuget Restore Theraot.Core ${RAOT_VER} to $(pwd)"
37 | nuget install Theraot.Core -version $RAOT_VER -verbosity quiet || nuget install Theraot.Core -version $RAOT_VER -verbosity quiet || true
38 | cd Theraot.Core*/lib
39 | for subdir in $(ls -1); do
40 | # Say "TRY ${copyto}/${subdir}"
41 | if [[ -d "${copyto}/${subdir}" ]]; then
42 | echo -e "COPY [Theraot.Core $RAOT_VER] @ ${subdir} to\n [${copyto}/${subdir}]"
43 | cp -r ${subdir}/. "${copyto}/${subdir}"
44 | fi
45 | done
46 | # popd
47 |
48 | # Say "LIST OF THE [$(pwd)]"
49 | # find .
50 |
51 |
52 | # cd Universe.CpuUsage*/lib
53 | cd $copyto
54 | Say "CpuUsage libraries: {$(pwd)}"
55 | rm -rf net47 net472 net48 netcoreapp3.0 netstandard2.1
56 | # mkdir -p ../../../../Tests4Linux/bin
57 | cp -r ./. $bin_path/
58 |
59 | cd $SCRIPT
60 | Say "RESTORE for [$(pwd)] Universe.CpuUsage.MonoTests.sln"
61 | nuget restore Universe.CpuUsage.MonoTests.sln -verbosity quiet || nuget restore Universe.CpuUsage.MonoTests.sln -verbosity quiet
62 | # pushd ~/build/devizer/Universe.CpuUsage; find . ; popd
63 | msbuild /t:Rebuild /p:Configuration=Release /v:q
64 |
65 | errors=0;
66 | proj=Universe.CpuUsage.MonoTests/Universe.CpuUsage.MonoTests.csproj
67 | cp -f ${proj} ${proj}-bak
68 | cat "$SayScript" > $matrix/run.sh
69 | echo 'success=0; errors=0; Say "RUNNING MATRIX. current is [$(pwd)]. Machine is [$(uname -m)-$(uname -s)]"' >> $matrix/run.sh
70 | matrix_run="cd $matrix && bash run.sh"
71 | for target_dir in $(ls -d bin/*/); do
72 | target=$(basename $target_dir)
73 | echo "pushd job-${target} >/dev/null" >> $matrix/run.sh
74 | echo 'echo ""; Say "JOB ['${target}'] for [$(uname -m)-$(uname -s)] in [$(pwd)]"' >> $matrix/run.sh
75 |
76 |
77 | Say "Mono Tests: msbuild rebuild for [$target]"
78 | sed_cmd="s/\.\.\\bin\\net46\\Universe/\.\.\\bin\\${target}\\Universe/g"
79 | sed_cmd="s/net46/${target}/g"
80 |
81 | cp -f ${proj}-bak ${proj}
82 | sed "$sed_cmd" ${proj}-bak > ${proj}
83 | echo "REF: {$(cat $proj | grep $target | grep HintPath)}"
84 |
85 | cfg=Debug
86 | msbuild /noLogo /t:Rebuild /p:Configuration=$cfg /v:q
87 | echo "mono ./Universe.CpuUsage.MonoTests/bin/$cfg/Universe.CpuUsage.MonoTests.exe" >> $matrix/run.sh
88 |
89 | # Say "Mono Tests: Run Tests for [$target]"
90 | echo '
91 | pushd packages/NUnit.ConsoleRunner*/tools >/dev/null; runner=$(pwd)/nunit3-console.exe; popd >/dev/null
92 | echo "Runner for the '$target' target is [$runner]"
93 | pushd Universe.CpuUsage.MonoTests/bin/'$cfg' >/dev/null
94 | mono $runner --workers=1 Universe.CpuUsage.MonoTests.exe && { Say "Success ['$target'] for [$(uname -m)-$(uname -s)]"; success=$((success+1)); } || { Say "ERROR: TESTING ['$target'] for [$(uname -m)-$(uname -s)]"; errors=$((errors+1)); }
95 | popd >/dev/null
96 | ' >> $matrix/run.sh
97 |
98 | mkdir -p $matrix/job-${target}
99 | cp -r ./. $matrix/job-${target}
100 |
101 | printf 'echo ""; popd >/dev/null\n\n' >> $matrix/run.sh
102 | done
103 |
104 | echo 'Say "Target Summary. Success: $success, Errors: $errors"; exit $errors' >> $matrix/run.sh
105 | chmod +x $matrix/run.sh
106 |
107 |
108 | has_dot_net="no"; command -v dotnet >/dev/null 2>&1 && { has_dot_net=yes; } || true
109 | if [[ $has_dot_net == yes && "$(uname -m)" == "aarch64" ]]; then
110 | pushd $StartFrom;
111 | cd ..
112 | Say "dotnet RESTORE for [$(pwd)]"
113 | time dotnet restore --disable-parallel || true
114 | pushd Universe.CpuUsage.Tests
115 | Say "dotnet TEST -f netcoreapp2.2 for [$(pwd)]"
116 | (dotnet test -f netcoreapp2.2 -c Release || exit 1) | cat
117 | popd
118 | popd
119 | fi
120 |
121 | # set +e
--------------------------------------------------------------------------------
/Tests4Mono/install-dotnet.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # script=https://raw.githubusercontent.com/devizer/Universe.CpuUsage/master/Tests4Mono/install-dotnet.sh; (wget -q -nv --no-check-certificate -O - $script 2>/dev/null || curl -ksSL $script) | bash
3 | set -e
4 | OS_X_VER=$(sw_vers 2>/dev/null | grep BuildVer | awk '{print $2}' | cut -c1-2 || true); OS_X_VER=$((OS_X_VER-4))
5 |
6 | script=https://raw.githubusercontent.com/devizer/test-and-build/master/install-build-tools.sh; (wget -q -nv --no-check-certificate -O - $script 2>/dev/null || curl -ksSL $script) | bash
7 | Say "Hello. Installing mono on Linux/OSX. Non-x64 arch is fully supported"
8 |
9 | # OSX?
10 | if [[ "$(uname -s)" == "Darwin" ]]; then
11 | MONO_Url=https://download.mono-project.com/archive/6.4.0/macos-10-universal/MonoFramework-MDK-6.4.0.198.macos10.xamarin.universal.pkg
12 | MONO_Url=https://download.mono-project.com/archive/6.8.0/macos-10-universal/MonoFramework-MDK-6.8.0.123.macos10.xamarin.universal.pkg
13 | curl -o ~/mono.pkg $MONO_Url
14 | sudo installer -verbose -pkg ~/mono.pkg -target /
15 | rm ~/mono.pkg
16 | export PATH="/Library/Frameworks/Mono.framework/Versions/Current/Commands:$PATH"
17 | else
18 |
19 | cmd1="try-and-retry apt-get -qq update >/dev/null"
20 | cmd2="try-and-retry apt-get install -y -qq git sudo jq tar bzip2 gzip curl lsb-release procps gnupg apt-transport-https dirmngr ca-certificates >/dev/null"
21 | for cmd in "$cmd1" "$cmd2"; do
22 | echo "eval [$cmd]"
23 | sudo true >/dev/null 2>&1 && time eval "sudo $cmd" || time eval "$cmd"
24 | done
25 |
26 | Say "Configure apt for missed signatures"
27 | echo 'Acquire::Check-Valid-Until "0";' | sudo tee /etc/apt/apt.conf.d/10no--check-valid-until
28 | echo 'APT::Get::Assume-Yes "true";' | sudo tee /etc/apt/apt.conf.d/11assume-yes
29 | echo 'APT::Get::AllowUnauthenticated "true";' | sudo tee /etc/apt/apt.conf.d/12allow-unauth
30 |
31 | # echo net.ipv6.conf.all.disable_ipv6=1 | sudo tee /etc/sysctl.d/disableipv6.conf || true
32 |
33 | if [[ "$(command -v mono)" == "" ]]; then
34 | # export MONO_ENV_OPTIONS=-O=-aot
35 | export MONO_USE_LLVM=0
36 |
37 | try-and-retry sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys A6A19B38D3D831EF
38 | # sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
39 | source /etc/os-release
40 | def="deb https://download.mono-project.com/repo/$ID stable-$(lsb_release -s -c) main"
41 | if [[ "$ID" == "raspbian" ]]; then def="deb https://download.mono-project.com/repo/debian stable-raspbian$(lsb_release -cs) main"; fi
42 | echo "$def" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list >/dev/null
43 | echo "Official mono repo: /etc/apt/sources.list.d/mono-official-stable.list"
44 | echo $def
45 | try-and-retry sudo apt-get -qq update;
46 | Say "Final mono installation"
47 | time try-and-retry sudo apt-get -qq install mono-complete nuget msbuild -y
48 | # --allow-unauthenticated ?
49 | fi
50 | set -e
51 | mono --version
52 |
53 | fi
54 |
55 | echo "uname -m: $(uname -m)"
56 |
57 | need_core=false
58 | if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "x86_64" ]] || [ $OS_X_VER -ge 13 ]; then need_core=true; fi
59 | if [[ $OS_X_VER -le 12 && $OS_X_VER -gt 0 ]] ; then need_core=false; fi
60 | if [[ $need_core == true ]]; then
61 | DOTNET_Url=https://dot.net/v1/dotnet-install.sh
62 | cmd_curl_dotnet_install="curl -ksSL -o /tmp/dotnet-install.sh $DOTNET_Url"
63 | cmd_dotnet_22="bash /tmp/dotnet-install.sh -c 2.2 -i ~/.dotnet"
64 | cmd_dotnet_30="bash /tmp/dotnet-install.sh -c 3.0 -i ~/.dotnet"
65 | cmd_dotnet_31="bash /tmp/dotnet-install.sh -c 3.1 -i ~/.dotnet"
66 | for cmd in "$cmd_curl_dotnet_install" "$cmd_dotnet_22" "$cmd_dotnet_30" "$cmd_dotnet_31"; do
67 | echo "|# $cmd"
68 | time (eval "$cmd" || eval "$cmd" || eval "$cmd")
69 | done
70 |
71 | echo '#!/usr/bin/env bash
72 | mkdir -p $HOME/.dotnet/tools $HOME/.dotnet
73 | export PATH="$HOME/.dotnet:$PATH:$HOME/.dotnet/tools"
74 | export DOTNET_ROOT="$HOME/.dotnet"
75 | ' >> ~/.dotnet-env
76 | chmod +x ~/.dotnet-env
77 | . ~/.dotnet-env
78 | echo '
79 | . ~/.dotnet-env
80 | ' >> ~/.bashrc
81 | dotnet --info || true
82 | dotnet tool install -g BenchmarkDotNet.Tool || true
83 | fi
84 |
85 | set +e
86 |
--------------------------------------------------------------------------------
/Tests4Mono/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Tests4Mono/say.include.sh:
--------------------------------------------------------------------------------
1 | counter=0;
2 | function header() {
3 | startAt=${startAt:-$(date +%s)}; elapsed=$(date +%s); elapsed=$((elapsed-startAt));
4 | if [[ $(uname -s) != Darwin ]]; then
5 | elapsed=$(TZ=UTC date -d "@${elapsed}" "+%_H:%M:%S" 2>/dev/null);
6 | else
7 | elapsed=$(TZ=UTC date -r "${elapsed}" "+%_H:%M:%S" 2>/dev/null);
8 | fi
9 | LightGreen='\033[1;32m'; Yellow='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'; LightGray='\033[1;2m';
10 | printf "${LightGray}${elapsed:-}${NC} ${LightGreen}$1${NC} ${Yellow}$2${NC}\n";
11 | }
12 | function Say() {
13 | echo "";
14 | counter=$((counter+1));
15 | header "STEP $counter" "$1";
16 | }
17 | Say "" >/dev/null;
18 | counter=0;
19 |
--------------------------------------------------------------------------------
/Universe.CpuUsage.Banchmark/BenchmarkProgram.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Configs;
2 | using BenchmarkDotNet.Environments;
3 | using BenchmarkDotNet.Jobs;
4 | using BenchmarkDotNet.Reports;
5 | using BenchmarkDotNet.Running;
6 |
7 | namespace Universe.CpuUsage.Banchmark
8 | {
9 | class BenchmarkProgram
10 | {
11 | public static void Main(string[] args)
12 | {
13 | // var run = Job.ShortRun;
14 | var run = Job.MediumRun;
15 | IConfig config = ManualConfig.Create(DefaultConfig.Instance);
16 | // Job jobLlvm = Job.InProcess;
17 | Job jobLlvm = run.With(Jit.Llvm).With(MonoRuntime.Default).WithId("LLVM-ON").WithWarmupCount(3);
18 | Job jobNoLlvm = run.With(MonoRuntime.Default).WithId("LLVM-OFF").WithWarmupCount(3);
19 | Job jobCore21 = run.With(CoreRuntime.Core21).WithId("NET-CORE").WithWarmupCount(3);
20 | Job jobCore50 = run.With(CoreRuntime.Core50).WithId("NET-CORE").WithWarmupCount(3);
21 | Job jobFW47 = run.With(ClrRuntime.Net47).WithId("NETFW-47").WithWarmupCount(3);
22 | config = config.With(new[] { jobLlvm, jobNoLlvm, jobCore21, jobCore50});
23 | Summary summary = BenchmarkRunner.Run(config);
24 | }
25 |
26 |
27 | }
28 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Banchmark/CpuUsageBenchmarks.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using BenchmarkDotNet.Attributes;
6 | using BenchmarkDotNet.Jobs;
7 |
8 | namespace Universe.CpuUsage.Banchmark
9 | {
10 | /*[SimpleJob(RuntimeMoniker.NetCoreApp30)]*/
11 | [RankColumn]
12 | [MemoryDiagnoser]
13 | public class CpuUsageBenchmarks
14 | {
15 | [Benchmark]
16 | public int ManagedThreadId()
17 | {
18 | return Thread.CurrentThread.ManagedThreadId;
19 | }
20 |
21 | [Benchmark]
22 | public long CustomThreadId()
23 | {
24 | return LegacyNetStandardInterop.GetThreadId();
25 | }
26 |
27 | [Benchmark]
28 | public void DateTimeNow()
29 | {
30 | var now = DateTime.Now;
31 | }
32 |
33 | [Benchmark]
34 | public void Stopwatch()
35 | {
36 | System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
37 | var ticks = sw.ElapsedTicks;
38 | }
39 |
40 | [Benchmark]
41 | public void ByProcess()
42 | {
43 | CpuUsage.GetByProcess();
44 | }
45 |
46 | [Benchmark]
47 | public void ByThread()
48 | {
49 | CpuUsage.GetByProcess();
50 | }
51 |
52 | [Benchmark]
53 | public async Task CpuUsageAsyncMinimal()
54 | {
55 | CpuUsageAsyncWatcher watcher = new CpuUsageAsyncWatcher();
56 | await Task.Run(() => "nothing to do");
57 | var totals = watcher.Totals.GetSummaryCpuUsage();
58 | }
59 |
60 | }
61 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Banchmark/Universe.CpuUsage.Banchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net47;netcoreapp3.0;netcoreapp2.2;netcoreapp3.1;net5.0
6 |
7 |
8 |
9 | 1701;1702;CS0162;CS0414;NU1701
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Universe.CpuUsage.Banchmark/run-llvm.sh:
--------------------------------------------------------------------------------
1 | # work=$HOME/build/devizer; mkdir -p $work; cd $work; git clone https://github.com/devizer/Universe.CpuUsage; cd Universe.CpuUsage; git pull; cd Universe.CpuUsage.Banchmark; bash run-llvm.sh
2 |
3 | pushd ..
4 | nuget restore || true; dotnet restore || true
5 | popd
6 | msbuild *.csproj /t:rebuild /p:Configuration=Release /v:q
7 | cd bin/Release/net47
8 | mono --llvm Universe.CpuUsage.Banchmark.exe
9 | cd ..
10 | cd ..
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Universe.CpuUsage.Tests/AsyncSchedulerCases.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 | using System.Threading.Tasks.Schedulers;
4 |
5 | namespace Universe.CpuUsage.Tests
6 | {
7 | public class AsyncSchedulerCases
8 | {
9 | public static IEnumerable Schedulers
10 | {
11 | get
12 | {
13 | var defaultScheduler = TaskScheduler.Default;
14 | yield return new AsyncSchedulerCase("Default .NET Scheduler", new TaskFactory(defaultScheduler), defaultScheduler);
15 |
16 | // works on OSX 10.10 on mono and windows, may not work on dotnet except of ConcurrentTest
17 | // May fail CpuUsageAsyncWatcher_Tests.ParallelTests and CpuUsageAsyncWatcher_Tests.SimpleTest on Linux
18 | ThreadPerTaskScheduler tpt = new ThreadPerTaskScheduler();
19 | yield return new AsyncSchedulerCase("Thread Per Task Scheduler", new TaskFactory(tpt), tpt);
20 |
21 | LimitedConcurrencyLevelTaskScheduler lc = new LimitedConcurrencyLevelTaskScheduler(16);
22 | yield return new AsyncSchedulerCase("Limited Concurrency Scheduler, up to 16 threads", new TaskFactory(lc), lc);
23 |
24 | LimitedConcurrencyLevelTaskScheduler lc1 = new LimitedConcurrencyLevelTaskScheduler(1);
25 | yield return new AsyncSchedulerCase("Limited Concurrency Scheduler. Single Thread", new TaskFactory(lc1), lc1);
26 |
27 | if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows)
28 | {
29 | QueuedTaskScheduler s = new QueuedTaskScheduler(1);
30 | yield return new AsyncSchedulerCase("QueuedTaskScheduler, Single Thread", new TaskFactory(s), s);
31 |
32 | // May fail CpuUsageAsyncWatcher_Tests.ParallelTests only
33 | QueuedTaskScheduler s2 = new QueuedTaskScheduler();
34 | yield return new AsyncSchedulerCase("QueuedTaskScheduler, Unlimited Threads", new TaskFactory(s2), s2);
35 | }
36 |
37 | }
38 | }
39 | }
40 |
41 | public class AsyncSchedulerCase
42 | {
43 | public string Title;
44 | public TaskFactory Factory;
45 | public TaskScheduler Scheduler;
46 |
47 | public AsyncSchedulerCase(string title, TaskFactory factory, TaskScheduler scheduler)
48 | {
49 | Title = title;
50 | Factory = factory;
51 | Scheduler = scheduler;
52 | }
53 |
54 | public override string ToString()
55 | {
56 | return Title;
57 | }
58 | }
59 |
60 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Tests/CpuLoader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace Universe.CpuUsage.Tests
6 | {
7 | public class CpuLoader
8 | {
9 | // contains a value of each CpuUsage increment
10 | public readonly List Population = new List(64000/8);
11 |
12 | public int IncrementsCount => Population.Count;
13 |
14 | // MILLI seconds
15 | public static CpuLoader Run(int minDuration = 1, int minCpuUsage = 1, bool needKernelLoad = true)
16 | {
17 | CpuLoader ret = new CpuLoader();
18 | ret.LoadCpu(minDuration, minCpuUsage, needKernelLoad);
19 | return ret;
20 | }
21 |
22 | // MILLI seconds
23 | private void LoadCpu(int minDuration, int minCpuUsage, bool needKernelLoad)
24 | {
25 | Stopwatch sw = Stopwatch.StartNew();
26 | CpuUsage firstUsage = CpuUsage.GetByThread().Value;
27 | CpuUsage prev = firstUsage;
28 | bool isFirstChange = true;
29 | bool isDone = false;
30 | while (!isDone)
31 | {
32 | if (needKernelLoad)
33 | {
34 | var ptr = Marshal.AllocHGlobal(2*1024);
35 | Marshal.FreeHGlobal(ptr);
36 | }
37 |
38 | CpuUsage nextUsage = CpuUsage.GetByThread().Value;
39 | if (nextUsage.TotalMicroSeconds != prev.TotalMicroSeconds)
40 | {
41 | var increment = CpuUsage.Substruct(nextUsage, prev).TotalMicroSeconds;
42 | if (!isFirstChange) Population.Add(increment);
43 | isFirstChange = false;
44 | }
45 |
46 | prev = nextUsage;
47 |
48 | isDone =
49 | sw.ElapsedMilliseconds >= minDuration
50 | && (nextUsage - firstUsage).TotalMicroSeconds >= minCpuUsage * 1000L;
51 | }
52 | }
53 |
54 | }
55 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Tests/CpuUsageAsyncWatcher_AsyncStreamTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.CompilerServices;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using NUnit.Framework;
7 | using Tests;
8 |
9 | namespace Universe.CpuUsage.Tests
10 | {
11 | #if NETCOREAPP3_0 || NETCOREAPP3_1 || NETCOREAPP5_0
12 | [TestFixture]
13 | public class CpuUsageAsyncWatcher_AsyncStreamTests : NUnitTestsBase
14 | {
15 | // https://blog.jetbrains.com/dotnet/2019/09/16/async-streams-look-new-language-features-c-8/
16 | // https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/generate-consume-asynchronous-stream
17 | // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/async-streams
18 | [Test, TestCaseSource(typeof(AsyncSchedulerCases), nameof(AsyncSchedulerCases.Schedulers))]
19 | public async Task AwaitForEachTests(AsyncSchedulerCase testEnvironment)
20 | {
21 | // Pre JIT
22 | await testEnvironment.Factory.StartNew(() => CpuLoader.Run(minDuration: 1, minCpuUsage: 1, needKernelLoad: true));
23 | // Act (durations are for debugging)
24 | CpuUsageAsyncWatcher watcher = new CpuUsageAsyncWatcher();
25 | long expectedMicroseconds = 0;
26 | await foreach (var milliseconds in GetLoadings())
27 | {
28 | await testEnvironment.Factory.StartNew(() => CpuLoader.Run(minDuration: milliseconds, minCpuUsage: milliseconds, needKernelLoad: true));
29 | expectedMicroseconds += milliseconds * 1000L;
30 | }
31 |
32 | var totals = watcher.Totals;
33 | long actualMicroseconds = totals.GetSummaryCpuUsage().TotalMicroSeconds;
34 | Console.WriteLine($"Expected usage: {(expectedMicroseconds/1000d):n3}, Actual usage: {(actualMicroseconds/1000d):n3} milliseconds");
35 | Console.WriteLine(totals.ToHumanString(taskDescription:"ParallelTests()"));
36 |
37 | // Assert
38 | Assert.GreaterOrEqual(totals.Count, 8, "Number of context switches should be 8 at least");
39 | // 0.15 for qemu armhf, less for rest
40 | Assert.AreEqual(expectedMicroseconds, actualMicroseconds, Env.CpuUsagePrecision * expectedMicroseconds, "Actual CPU Usage should be about as expected.");
41 | }
42 |
43 | static async IAsyncEnumerable GetLoadings([EnumeratorCancellation] CancellationToken token = default)
44 | {
45 | foreach (var ret in new[] {250, 450, 650, 850})
46 | {
47 | yield return ret;
48 | await Task.Delay(10);
49 | }
50 | }
51 |
52 | }
53 | #endif
54 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Tests/CpuUsageAsyncWatcher_Tests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using System.Threading.Tasks.Schedulers;
7 | using NUnit.Framework;
8 | using Tests;
9 |
10 | namespace Universe.CpuUsage.Tests
11 | {
12 | [TestFixture]
13 | public class CpuUsageAsyncWatcher_Tests : NUnitTestsBase
14 | {
15 | [OneTimeSetUp]
16 | public void SetUp()
17 | {
18 | PreJit().Wait();
19 | }
20 |
21 | [Test, TestCaseSource(typeof(AsyncSchedulerCases), nameof(AsyncSchedulerCases.Schedulers))]
22 | public async Task SimpleTests(AsyncSchedulerCase testEnvironment)
23 | {
24 | if (!IsSupported()) return;
25 |
26 | // Act (durations are for debugging)
27 | CpuUsageAsyncWatcher watch = new CpuUsageAsyncWatcher();
28 | await testEnvironment.Factory.StartNew(() => LoadCpu(milliseconds: 200));
29 | await testEnvironment.Factory.StartNew(() => LoadCpu(milliseconds: 500));
30 | await testEnvironment.Factory.StartNew(() => LoadCpu(milliseconds: 800));
31 | await NotifyFinishedTasks();
32 | watch.Stop();
33 | var totals = watch.Totals;
34 |
35 | // Assert
36 | long actualMicroseconds = totals.GetSummaryCpuUsage().TotalMicroSeconds;
37 | long expectedMicroseconds = 1000L * (200 + 500 + 800);
38 | Console.WriteLine($"Expected usage: {(expectedMicroseconds/1000d):n3}, Actual usage: {(actualMicroseconds/1000d):n3} milliseconds");
39 | Console.WriteLine(watch.ToHumanString(taskDescription:"SimpleTests()"));
40 |
41 | Assert.GreaterOrEqual(totals.Count, 6, "Number of context switches should be 6 at least");
42 | Assert.AreEqual(expectedMicroseconds, actualMicroseconds, Env.CpuUsagePrecision * expectedMicroseconds, "Actual CPU Usage should be about as expected.");
43 | }
44 |
45 | [Test, TestCaseSource(typeof(AsyncSchedulerCases), nameof(AsyncSchedulerCases.Schedulers))]
46 | public async Task ParallelTests(AsyncSchedulerCase testEnvironment)
47 | {
48 | if (!IsSupported()) return;
49 |
50 | // Act (durations are for debugging)
51 | CpuUsageAsyncWatcher watcher = new CpuUsageAsyncWatcher();
52 | TaskFactory tf = new TaskFactory();
53 | var task4 = testEnvironment.Factory.StartNew(() => LoadCpu(milliseconds: 2400));
54 | var task3 = testEnvironment.Factory.StartNew(() => LoadCpu(milliseconds: 2100));
55 | var task2 = testEnvironment.Factory.StartNew(() => LoadCpu(milliseconds: 1800));
56 | var task1 = testEnvironment.Factory.StartNew(() => LoadCpu(milliseconds: 1500));
57 | await Task.WhenAll(task1, task2, task3, task4);
58 | await NotifyFinishedTasks();
59 | watcher.Stop();
60 | var totals = watcher.Totals;
61 |
62 | // Assert
63 | long actualMicroseconds = totals.GetSummaryCpuUsage().TotalMicroSeconds;
64 | long expectedMicroseconds = 1000L * (2400 + 2100 + 1800 + 1500);
65 | Console.WriteLine($"Expected usage: {(expectedMicroseconds/1000d):n3}, Actual usage: {(actualMicroseconds/1000d):n3} milliseconds");
66 | Console.WriteLine(totals.ToHumanString(taskDescription:"ParallelTests()"));
67 | // 7 for windows 8 cores, rarely 6 for slow 2 core machine, but 7 for single core armv5
68 | Assert.GreaterOrEqual(totals.Count, 6, "Number of context switches should be 6 at least");
69 | Assert.AreEqual(expectedMicroseconds, actualMicroseconds, Env.CpuUsagePrecision * expectedMicroseconds, "Actual CPU Usage should be about as expected.");
70 | }
71 |
72 | [Test, TestCaseSource(typeof(AsyncSchedulerCases), nameof(AsyncSchedulerCases.Schedulers))]
73 | public async Task ConcurrentTest(AsyncSchedulerCase testEnvironment)
74 | {
75 | if (!IsSupported()) return;
76 | // second context switch is lost for ThreadPerTaskScheduler
77 | if (testEnvironment.Scheduler is ThreadPerTaskScheduler) return;
78 |
79 | IList tasks = new List();
80 | int errors = 0;
81 | int maxThreads = Math.Min(Environment.ProcessorCount + 9, 16);
82 | // hypothesis: each thread load should be exponential
83 | for (int i = 1; i <= maxThreads; i++)
84 | {
85 | var iCopy = i;
86 | tasks.Add( testEnvironment.Factory.StartNew(async () =>
87 | {
88 | var expectedMilliseconds = iCopy * 400;
89 | if (!await CheckExpectedCpuUsage(expectedMilliseconds, testEnvironment.Factory))
90 | Interlocked.Increment(ref errors);
91 |
92 | }, TaskCreationOptions.LongRunning).Unwrap());
93 | }
94 |
95 | await Task.WhenAll(tasks);
96 | Assert.IsTrue(errors == 0, "Concurrent CpuUsageAsyncWatchers should not infer on each other. See details above");
97 | }
98 |
99 | async Task CheckExpectedCpuUsage(int expectedMilliseconds, TaskFactory factory)
100 | {
101 | // Act
102 | CpuUsageAsyncWatcher watcher = new CpuUsageAsyncWatcher();
103 | await factory.StartNew(() => LoadCpu(expectedMilliseconds));
104 | watcher.Stop();
105 | Console.WriteLine(watcher.ToHumanString(taskDescription: $"'Expected CPU Load is {expectedMilliseconds} milli-seconds'"));
106 |
107 | Assert.AreEqual(2, watcher.Totals.Count, "The CheckExpectedCpuUsage should produce exact 2 context switches");
108 | // Assert: CpuUsage should be expectedMilliseconds
109 | var actualSeconds = watcher.GetSummaryCpuUsage().TotalMicroSeconds / 1000000d;
110 | bool isOk = actualSeconds >= expectedMilliseconds / 1000d;
111 | return isOk;
112 | }
113 |
114 | // Load CPU by number of milliseconds
115 | private void LoadCpu(int milliseconds = 42) => CpuLoader.Run(minDuration: milliseconds, minCpuUsage: milliseconds, needKernelLoad: true);
116 |
117 | private async Task PreJit()
118 | {
119 | Console.WriteLine("Pre-Jitting CpuUsageAsyncWatcher class");
120 | CpuUsageAsyncWatcher watch = new CpuUsageAsyncWatcher();
121 | await Task.Run(() => LoadCpu(1));
122 | await NotifyFinishedTasks();
123 | watch.Stop();
124 | watch.ToHumanString();
125 | Console.WriteLine("Pre-Jitted CpuUsageAsyncWatcher class");
126 | }
127 |
128 | async Task NotifyFinishedTasks()
129 | {
130 | // v1
131 | // await Task.Run(() => "Nothing to do");
132 |
133 | // v2
134 | // await Task.Delay(0);
135 |
136 | // v3
137 | for (int i = 0; i < 7; i++)
138 | {
139 | await Task.Run(() => Thread.Sleep(1));
140 | await Task.Delay(1);
141 | }
142 | }
143 |
144 | bool IsSupported()
145 | {
146 | if (!CpuUsageAsyncWatcher.IsSupported)
147 | {
148 | Console.WriteLine("Skipping. The CpuUsageAsyncWatcher class requires NET Framework 4.6+, Net Core 1.0+ or NetStandard 2.0+");
149 | }
150 |
151 | return CpuUsageAsyncWatcher.IsSupported;
152 | }
153 |
154 |
155 | }
156 |
157 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Tests/CrossInfo_Tests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using NUnit.Framework;
3 | using Tests;
4 |
5 | namespace Universe.CpuUsage.Tests
6 | {
7 | [TestFixture]
8 | public class CrossInfo_Tests : NUnitTestsBase
9 | {
10 |
11 | [Test]
12 | public void Show_The_Platform()
13 | {
14 | Console.WriteLine($"The Platform: {CrossInfo.ThePlatform}");
15 | Console.WriteLine($"Process: {IntPtr.Size * 8} bits");
16 | Console.WriteLine($"Environment.OSVersion.Platform: {Environment.OSVersion.Platform}");
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Tests/Env.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace Universe.CpuUsage.Tests
5 | {
6 | public class Env
7 | {
8 | // qemu emulator: 0.2, otherwise 0.1
9 | public static double CpuUsagePrecision => _CpuUsagePrecision.Value;
10 |
11 | private static Lazy _CpuUsagePrecision = new Lazy(() =>
12 | {
13 | double ret = 0.1d;
14 | var raw = Environment.ExpandEnvironmentVariables("%CpuUsagePrecisionForAssert%");
15 | var enUS = new CultureInfo("en-US");
16 | if (!string.IsNullOrEmpty(raw))
17 | if (double.TryParse(raw, NumberStyles.AllowDecimalPoint, enUS, out ret))
18 | {
19 | }
20 |
21 | ret = ret > 0 ? ret : 0.1d;
22 | Console.WriteLine($"CpuUsagePrecisionForAssert: {ret}, raw: [{raw}]");
23 | return ret;
24 | });
25 | }
26 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Tests/LinuxKernelCacheFlusher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 |
5 | namespace KernelManagementJam.Benchmarks
6 | {
7 |
8 | public class LinuxKernelCacheFlusher
9 | {
10 | // 60 seconds is an acceptable timeout for a build server
11 | private static readonly int WriteBuffersFlushingTimout = 60000;
12 | private static readonly int ReadBuffersFlushTimeout = 60000;
13 |
14 | // skipping is used for integration tests only
15 | private static bool SkipFlushing => Environment.GetEnvironmentVariable("SKIP_FLUSHING") == "true";
16 | public static void Sync()
17 | {
18 | if (SkipFlushing) return;
19 | SyncWriteBuffer();
20 | FlushReadBuffers();
21 | }
22 |
23 | public static void SyncWriteBuffer()
24 | {
25 | StartAndIgnore("sync", "", WriteBuffersFlushingTimout);
26 | }
27 |
28 | public static void FlushReadBuffers()
29 | {
30 | bool isDropOk = false;
31 | try
32 | {
33 | File.WriteAllText("/proc/sys/vm/drop_caches", "3");
34 | // sudo on Linux: ok
35 | // non-sudo on Linux: fail
36 | // macOS - fail
37 | isDropOk = true;
38 | }
39 | catch
40 | {
41 | }
42 |
43 | if (!isDropOk)
44 | {
45 | StartAndIgnore("sudo", "sh -c \"echo 1 > /proc/sys/vm/drop_caches; purge;\"", ReadBuffersFlushTimeout);
46 | }
47 | }
48 |
49 | static int StartAndIgnore(string executableName, string args, int timeoutMilliseconds)
50 | {
51 | Stopwatch sw = Stopwatch.StartNew();
52 | string info = executableName == "sudo" ? args : executableName;
53 |
54 | try
55 | {
56 | ProcessStartInfo si = new ProcessStartInfo(executableName, args);
57 | if (string.IsNullOrEmpty(args)) si = new ProcessStartInfo(executableName);
58 | si.RedirectStandardError = true;
59 | si.RedirectStandardOutput = true;
60 | si.UseShellExecute = false;
61 | using (Process p = Process.Start(si))
62 | {
63 | p.Start();
64 | bool isOk = p.WaitForExit(timeoutMilliseconds);
65 | if (!isOk)
66 | {
67 | Console.WriteLine($"Warning! Flush command [{info}] is not completed in {sw.ElapsedMilliseconds:n0} milliseconds");
68 | }
69 | #if DEBUG
70 | if (isOk)
71 | {
72 | Console.WriteLine($"Info: Flush command [{info}] successfully finished in {sw.ElapsedMilliseconds:n0} milliseconds");
73 | }
74 | #endif
75 | return p.ExitCode;
76 | }
77 | }
78 | catch(Exception ex)
79 | {
80 | #if DEBUG
81 | Console.WriteLine($"Info: Process [{info}] failed in {sw.ElapsedMilliseconds:n0} milliseconds. {ex.GetType().Name} {ex.Message}");
82 | #endif
83 | return -1;
84 | }
85 | }
86 |
87 | }
88 | }
--------------------------------------------------------------------------------
/Universe.CpuUsage.Tests/LinuxResourcesUsage_Reader_Tests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Linq;
4 | using System.Text;
5 | using NUnit.Framework;
6 | using Tests;
7 | using Universe.CpuUsage.Interop;
8 |
9 | namespace Universe.CpuUsage.Tests
10 | {
11 | // git pull; time dotnet test --filter LinuxResourcesUsage
12 | [TestFixture]
13 | public class LinuxResourcesUsage_Reader_Tests : NUnitTestsBase
14 | {
15 | [Test]
16 | public void Is_Supported()
17 | {
18 | Console.WriteLine($"LinuxResourceUsageReader.IsSupported: {LinuxResourceUsageReader.IsSupported}");
19 |
20 | bool expectedSupported = CrossInfo.ThePlatform == CrossInfo.Platform.Linux ||
21 | CrossInfo.ThePlatform == CrossInfo.Platform.MacOSX;
22 |
23 | if (expectedSupported && !LinuxResourceUsageReader.IsSupported)
24 | Assert.Fail("On Linux 2.6.26+ the value of LinuxResourceUsageReader.IsSupported should be true");
25 | }
26 |
27 | [Test]
28 | public void Grow_Usage()
29 | {
30 | if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows) return;
31 |
32 | CpuLoader.Run(1, 1, true);
33 | CpuUsageScope scope = CrossInfo.ThePlatform == CrossInfo.Platform.MacOSX
34 | ? CpuUsageScope.Process
35 | : CpuUsageScope.Thread;
36 |
37 | Console.WriteLine($"Supported scope: {scope}");
38 | LinuxResourceUsageReader.GetByScope(scope);
39 | var prev = LinuxResourceUsageReader.GetByScope(scope);
40 | for (int i = 0; i < 10; i++)
41 | {
42 | CpuLoader.Run(1, 1, true);
43 | CpuUsage? next = LinuxResourceUsageReader.GetByScope(scope);
44 | Console.WriteLine($" {i} -> {next}");
45 | Assert.GreaterOrEqual(next.Value.KernelUsage.TotalMicroSeconds, prev.Value.KernelUsage.TotalMicroSeconds);
46 | Assert.GreaterOrEqual(next.Value.UserUsage.TotalMicroSeconds, prev.Value.UserUsage.TotalMicroSeconds);
47 | prev = next;
48 | }
49 | }
50 |
51 | [Test]
52 | public void Get_Process_Usage()
53 | {
54 | if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows)
55 | {
56 | Console.WriteLine($"LinuxResourceUsageReader is not supported on platform {CrossInfo.ThePlatform}");
57 | return;
58 | }
59 |
60 | var usage = LinuxResourceUsageReader.GetByProcess();
61 | Console.WriteLine($"LinuxResourceUsageReader.GetByProcess(): {usage}");
62 | }
63 |
64 |
65 | [Test]
66 | public void Get_Thread_Usage()
67 | {
68 | if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows)
69 | {
70 | Console.WriteLine($"LinuxResourceUsageReader is not supported on platform {CrossInfo.ThePlatform}");
71 | return;
72 | }
73 |
74 | var usage = LinuxResourceUsageReader.GetByThread();
75 | Console.WriteLine($"LinuxResourceUsageReader.GetByThread(): {usage}");
76 | }
77 |
78 | [Test]
79 | public void Show_Raw_Thread_Usage()
80 | {
81 | if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows)
82 | {
83 | Console.WriteLine($"LinuxResourceUsageReader is not supported on platform {CrossInfo.ThePlatform}");
84 | return;
85 | }
86 |
87 | var resources = LinuxResourceUsageInterop.GetRawUsageResources(LinuxResourceUsageInterop.RUSAGE_THREAD);
88 | Console.WriteLine($"GetRawUsageResources(RUSAGE_THREAD):{Environment.NewLine}{AsString(resources)}");
89 | }
90 |
91 | [Test]
92 | public void Show_Raw_Process_Usage()
93 | {
94 | if (CrossInfo.ThePlatform == CrossInfo.Platform.Windows)
95 | {
96 | Console.WriteLine($"LinuxResourceUsageReader is not supported on platform {CrossInfo.ThePlatform}");
97 | return;
98 | }
99 |
100 | var resources = LinuxResourceUsageInterop.GetRawUsageResources(LinuxResourceUsageInterop.RUSAGE_SELF);
101 | Console.WriteLine($"GetRawUsageResources(RUSAGE_SELF):{Environment.NewLine}{AsString(resources)}");
102 | }
103 |
104 | static string AsString(IEnumerable arr)
105 | {
106 | if (arr == null) return "";
107 |
108 | if (IntPtr.Size == 8 && CrossInfo.ThePlatform == CrossInfo.Platform.MacOSX)
109 | {
110 | var copy = arr.OfType