summary refs log tree commit diff stats
path: root/lib/pure/concurrency/cpuload.nim
blob: 1f3f540560ef165531a76d4de07863e45d635b1c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#
#
#            Nim's Runtime Library
#        (c) Copyright 2015 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module implements a helper for a thread pool to determine whether
## creating a thread is a good idea.

when defined(windows):
  import winlean, os, strutils, math

  proc `-`(a, b: TFILETIME): int64 = a.rdFileTime - b.rdFileTime
elif defined(linux):
  from cpuinfo import countProcessors

type
  ThreadPoolAdvice* = enum
    doNothing,
    doCreateThread,  # create additional thread for throughput
    doShutdownThread # too many threads are busy, shutdown one

  ThreadPoolState* = object
    when defined(windows):
      prevSysKernel, prevSysUser, prevProcKernel, prevProcUser: TFILETIME
    calls*: int

proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
  when defined(windows):
    var
      sysIdle, sysKernel, sysUser,
        procCreation, procExit, procKernel, procUser: TFILETIME
    if getSystemTimes(sysIdle, sysKernel, sysUser) == 0 or
        getProcessTimes(Handle(-1), procCreation, procExit, 
                        procKernel, procUser) == 0:
      return doNothing
    if s.calls > 0:
      let
        sysKernelDiff = sysKernel - s.prevSysKernel
        sysUserDiff = sysUser - s.prevSysUser

        procKernelDiff = procKernel - s.prevProcKernel
        procUserDiff = procUser - s.prevProcUser

        sysTotal = int(sysKernelDiff + sysUserDiff)
        procTotal = int(procKernelDiff + procUserDiff)
      # total CPU usage < 85% --> create a new worker thread.
      # Measurements show that 100% and often even 90% is not reached even
      # if all my cores are busy.
      if sysTotal == 0 or procTotal / sysTotal < 0.85:
        result = doCreateThread
    s.prevSysKernel = sysKernel
    s.prevSysUser = sysUser
    s.prevProcKernel = procKernel
    s.prevProcUser = procUser
  elif defined(linux):
    proc fscanf(c: File, frmt: cstring) {.varargs, importc, 
      header: "<stdio.h>".}

    var f = open("/proc/loadavg")
    var b: float
    var busy, total: int
    fscanf(f,"%lf %lf %lf %ld/%ld",
           addr b, addr b, addr b, addr busy, addr total)
    f.close()
    let cpus = countProcessors()
    if busy-1 < cpus:
      result = doCreateThread
    elif busy-1 >= cpus*2:
      result = doShutdownThread
    else:
      result = doNothing
  else:
    # XXX implement this for other OSes
    result = doNothing
  inc s.calls

when not defined(testing) and isMainModule:
  proc busyLoop() =
    while true:
      discard random(80)
      os.sleep(100)

  spawn busyLoop()
  spawn busyLoop()
  spawn busyLoop()
  spawn busyLoop()

  var s: ThreadPoolState

  for i in 1 .. 70:
    echo advice(s)
    os.sleep(1000)
lass="n">cbos listDirs: listDirs(a, {pcDir}) cbos removeDir: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: os.removeDir getString(a, 0) cbos removeFile: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: os.removeFile getString(a, 0) cbos createDir: os.createDir getString(a, 0) result.registerCallback "stdlib.system.getError", proc (a: VmArgs) = setResult(a, errorMsg) cbos setCurrentDir: os.setCurrentDir getString(a, 0) cbos getCurrentDir: setResult(a, os.getCurrentDir()) cbos moveFile: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: os.moveFile(getString(a, 0), getString(a, 1)) cbos moveDir: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: os.moveDir(getString(a, 0), getString(a, 1)) cbos copyFile: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: os.copyFile(getString(a, 0), getString(a, 1)) cbos copyDir: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: os.copyDir(getString(a, 0), getString(a, 1)) cbos getLastModificationTime: setResult(a, getLastModificationTime(getString(a, 0)).toUnix) cbos findExe: setResult(a, os.findExe(getString(a, 0))) cbos rawExec: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: setResult(a, osproc.execCmd getString(a, 0)) cbconf getEnv: setResult(a, os.getEnv(a.getString 0, a.getString 1)) cbconf existsEnv: setResult(a, os.existsEnv(a.getString 0)) cbconf putEnv: os.putEnv(a.getString 0, a.getString 1) cbconf delEnv: os.delEnv(a.getString 0) cbconf dirExists: setResult(a, os.dirExists(a.getString 0)) cbconf fileExists: setResult(a, os.fileExists(a.getString 0)) cbconf projectName: setResult(a, conf.projectName) cbconf projectDir: setResult(a, conf.projectPath.string) cbconf projectPath: setResult(a, conf.projectFull.string) cbconf thisDir: setResult(a, vthisDir) cbconf put: options.setConfigVar(conf, getString(a, 0), getString(a, 1)) cbconf get: setResult(a, options.getConfigVar(conf, a.getString 0)) cbconf exists: setResult(a, options.existsConfigVar(conf, a.getString 0)) cbconf nimcacheDir: setResult(a, options.getNimcacheDir(conf).string) cbconf paramStr: setResult(a, os.paramStr(int a.getInt 0)) cbconf paramCount: setResult(a, os.paramCount()) cbconf cmpIgnoreStyle: setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1)) cbconf cmpIgnoreCase: setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) cbconf setCommand: conf.command = a.getString 0 let arg = a.getString 1 incl(conf.globalOptions, optWasNimscript) if arg.len > 0: conf.projectName = arg let path = if conf.projectName.isAbsolute: AbsoluteFile(conf.projectName) else: conf.projectPath / RelativeFile(conf.projectName) try: conf.projectFull = canonicalizePath(conf, path) except OSError: conf.projectFull = path cbconf getCommand: setResult(a, conf.command) cbconf switch: processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf) cbconf hintImpl: processSpecificNote(a.getString 0, wHint, passPP, module.info, a.getString 1, conf) cbconf warningImpl: processSpecificNote(a.getString 0, wWarning, passPP, module.info, a.getString 1, conf) cbconf patchFile: let key = a.getString(0) & "_" & a.getString(1) var val = a.getString(2).addFileExt(NimExt) if {'$', '~'} in val: val = pathSubs(conf, val, vthisDir) elif not isAbsolute(val): val = vthisDir / val conf.moduleOverrides[key] = val cbconf selfExe: setResult(a, os.getAppFilename()) cbconf cppDefine: options.cppDefine(conf, a.getString(0)) cbexc stdinReadLine, EOFError: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: setResult(a, "") setResult(a, stdin.readLine()) cbexc stdinReadAll, EOFError: if defined(nimsuggest) or graph.config.cmd == cmdCheck: discard else: setResult(a, "") setResult(a, stdin.readAll()) proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; freshDefines=true; conf: ConfigRef) = rawMessage(conf, hintConf, scriptName.string) let oldSymbolFiles = conf.symbolFiles conf.symbolFiles = disabledSf let graph = newModuleGraph(cache, conf) connectCallbacks(graph) if freshDefines: initDefines(conf.symbols) defineSymbol(conf.symbols, "nimscript") defineSymbol(conf.symbols, "nimconfig") registerPass(graph, semPass) registerPass(graph, evalPass) conf.searchPaths.add(conf.libpath) let oldGlobalOptions = conf.globalOptions let oldSelectedGC = conf.selectedGC undefSymbol(conf.symbols, "nimv2") conf.globalOptions.excl optNimV2 conf.selectedGC = gcUnselected var m = graph.makeModule(scriptName) incl(m.flags, sfMainModule) graph.vm = setupVM(m, cache, scriptName.string, graph) graph.compileSystemModule() # TODO: see why this unsets hintConf in conf.notes discard graph.processModule(m, llStreamOpen(scriptName, fmRead)) # watch out, "newruntime" can be set within NimScript itself and then we need # to remember this: if optNimV2 in oldGlobalOptions: conf.globalOptions.incl optNimV2 defineSymbol(conf.symbols, "nimv2") if conf.selectedGC == gcUnselected: conf.selectedGC = oldSelectedGC # ensure we load 'system.nim' again for the real non-config stuff! resetSystemArtifacts(graph) # do not remove the defined symbols #initDefines() undefSymbol(conf.symbols, "nimscript") undefSymbol(conf.symbols, "nimconfig") conf.symbolFiles = oldSymbolFiles