cpuinfo (empty) → 0.1.0.0
raw patch · 6 files changed
+381/−0 lines, 6 filesdep +attoparsecdep +basedep +bytestringsetup-changed
Dependencies added: attoparsec, base, bytestring, deepseq
Files
- ChangeLog.md +2/−0
- LICENSE +21/−0
- README.md +2/−0
- Setup.hs +2/−0
- cpuinfo.cabal +32/−0
- src/System/CPU.hs +322/−0
+ ChangeLog.md view
@@ -0,0 +1,2 @@+# 0.1.0.0+Initial release.
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2016 Travis Whitaker++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,2 @@+# cpuinfo+Haskell Library for Checking CPU Information
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cpuinfo.cabal view
@@ -0,0 +1,32 @@+name: cpuinfo+version: 0.1.0.0+synopsis: Haskell Library for Checking CPU Information+description: Haskell Library for Checking CPU Information+homepage: https://github.com/TravisWhitaker/cpuinfo+license: MIT+license-file: LICENSE+author: Travis Whitaker+maintainer: pi.boy.travis@gmail.com+copyright: Travis Whitaker 2016+category: System+build-type: Simple+extra-source-files: ChangeLog.md, README.md+cabal-version: >=1.10++library+ exposed-modules: System.CPU+ -- other-modules: + -- other-extensions: + build-depends: base >=4.8 && <4.9+ , attoparsec >=0.13 && <0.14+ , bytestring >=0.10 && <0.11+ , deepseq >=1.4 && <1.5+ hs-source-dirs: src+ ghc-options: -Wall+ -fwarn-identities+ -fwarn-missing-signatures+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/TravisWhitaker/cpuinfo.git
+ src/System/CPU.hs view
@@ -0,0 +1,322 @@+{-|+Module : System.CPU+Description : Haskell Library for Checking CPU Information+Copyright : Travis Whitaker 2016+License : MIT+Maintainer : pi.boy.travis@gmail.com+Stability : Provisional+Portability : Linux >=2.6++This module provides information about the processors available on a system.+Modern hardware provides not only multiple physical processors and physical+cores, but logical cores which may not have dedicated execution resources.+Intel's Hyper-Threading is an example of such a technology, capable of providing+two logical cores for every physical core present on a supported physical+processor.++These additional logical cores increase the performance of some, but not all+workloads. Indeed, some parallel workloads may suffer a performance decrease if+all logical cores presented by the operating system do not have dedicated+physical resources. This is because technologies providing supernumerary logical+cores typically work by scheduling multiple threads in a shared pool of+execution resources, e.g. ALUs and FPUs. If threads sharing a pool of execution+resources are doing the same sort of work there will be scheduling contention+for a single type of execution resource on the physical core.++It is common for threaded Haskell programs to be run with @+RTS -N@, causing the+RTS to simply multiplex Haskell threads or sparks over the number of logical+cores available. However, if each logical core does not have dedicated physical+resources and the thread/spark workloads are similar, then this might be slower+than multiplexing over fewer cores.++This package allows a program to use information about the physical and logical+features of the available processors as a heuristic for selecting the number of+worker OS threads to use (e.g. via 'setNumCapabilities'). Some workloads may+benefit from, for example, using half the number of logical cores available if+there are in fact two logical cores for each physical core. This is typically+true of numerical workloads, but as always benchmarking should be employed to+evaluate the impact of different heuristics.++In its current state this module can only collect information from Linux systems+with a kernel from the 2.6 branch or later by reading @\/proc\/cpuinfo@. If this+module is unable to provide information on your system please file a bug+including your @\/proc\/cpuinfo@. Help providing Windows support would be+greatly appreciated!+-}++{-# LANGUAGE DeriveAnyClass+ , DeriveDataTypeable+ , DeriveGeneric+ , OverloadedStrings+ #-}++module System.CPU (+ CPU(..)+ -- * Retrieving CPU Information+ , getCPUs+ , tryGetCPUs+ -- * Physical Features+ , physicalProcessors+ , physicalCores+ , logicalCores+ , hyperthreadingFactor+ , hyperthreadingInUse+ ) where++import Control.Applicative++import Control.Arrow++import Control.DeepSeq++import qualified Data.Attoparsec.ByteString.Char8 as A++import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC++import Data.Data++import Data.Foldable++import Data.List++import Data.Maybe++import Data.Word++import GHC.Generics++-- | Representation of a logical processor and its features.+data CPU = CPU {+ -- | Logical Processor Index+ processorID :: !Word32+ -- | CPU Vendor+ , vendor :: !(Maybe B.ByteString)+ -- | CPU Model Number+ , model :: !(Maybe Word32)+ -- | CPU Model Name+ , modelName :: !(Maybe B.ByteString)+ -- | CPU Model Revision+ , revision :: !(Maybe Word32)+ -- | CPU Microcode Revision+ , microcode :: !(Maybe Word32)+ -- | Processor Frequency+ , freq :: !Double+ -- | CPU Cache Size. (TODO figure out how to get the cache topology)+ , cache :: !(Maybe Word32)+ -- | Physical Processor Index+ , physicalID :: !Word32+ -- | Number of Physical Cores on this Physical Processor.+ , siblings :: !Word32+ -- | Physical Core Index+ , coreID :: !Word32+ -- | CPU APIC Index+ , apicID :: !(Maybe Word32)+ -- | Whether or not the Physical Core provides a floating point unit.+ , fpu :: !(Maybe Bool)+ -- | Whether or not the Physical Core provides a floating point exception+ -- unit.+ , fpuExcept :: !(Maybe Bool)+ -- | Vendor-specific CPU flags.+ , flags :: !(Maybe [B.ByteString])+ -- | MIPS approximation computed by the Linux kernel on boot.+ , bogoMIPS :: !Double+ -- | Cache line size in bytes.+ , cacheAlignment :: !(Maybe Word32)+ -- | Physical address width.+ , physicalAddress :: !(Maybe Word32)+ -- | Virtual address width.+ , virtualAddress :: !(Maybe Word32)+ } deriving ( Eq+ , Ord+ , Read+ , Show+ , Data+ , Typeable+ , Generic+ , NFData+ )++parsePair :: B.ByteString -> A.Parser a -> A.Parser a+parsePair k vp = A.string k+ *> A.skipSpace+ *> A.char ':'+ *> A.skipSpace+ *> vp++parseProcessor :: A.Parser Word32+parseProcessor = parsePair "processor" A.decimal++parseVendor :: A.Parser B.ByteString+parseVendor = parsePair "vendor_id" A.takeByteString++parseModel :: A.Parser Word32+parseModel = parsePair "model" A.decimal++parseModelName :: A.Parser B.ByteString+parseModelName = parsePair "model name" A.takeByteString++parseRevision :: A.Parser Word32+parseRevision = parsePair "stepping" A.decimal++parseMicrocode :: A.Parser Word32+parseMicrocode = parsePair "microcode" (A.string "0x" *> A.hexadecimal)++parseFreq :: A.Parser Double+parseFreq = parsePair "cpu MHz" A.double++parseCache :: A.Parser Word32+parseCache = parsePair "cache size"+ (A.decimal <* (A.skipSpace *> A.string "KB"))++parsePhysicalID :: A.Parser Word32+parsePhysicalID = parsePair "physical id" A.decimal++parseSiblings :: A.Parser Word32+parseSiblings = parsePair "siblings" A.decimal++parseCoreID :: A.Parser Word32+parseCoreID = parsePair "core id" A.decimal++parseApicID :: A.Parser Word32+parseApicID = parsePair "apicid" A.decimal++parseFpu :: A.Parser Bool+parseFpu = parsePair "fpu" parseBool++parseFpuExcept :: A.Parser Bool+parseFpuExcept = parsePair "fpu_exception" parseBool++parseFlags :: A.Parser [B.ByteString]+parseFlags = parsePair "flags" parseWords++parseBogoMIPS :: A.Parser Double+parseBogoMIPS = parsePair "bogomips" A.double++parseCacheAlignment :: A.Parser Word32+parseCacheAlignment = parsePair "cache_alignment" A.decimal++parseAddresses :: A.Parser (Word32, Word32)+parseAddresses = parsePair "address sizes"+ ((,) <$> parsePhysicalAddress+ <*> parseVirtualAddress)++parsePhysicalAddress :: A.Parser Word32+parsePhysicalAddress = A.decimal <* A.string " bits physical, "++parseVirtualAddress :: A.Parser Word32+parseVirtualAddress = A.decimal <* A.string " bits virtual"++parseBool :: A.Parser Bool+parseBool = (A.string "yes" *> pure True)+ <|> (A.string "no" *> pure False)++parseWords :: A.Parser [B.ByteString]+parseWords = A.sepBy (A.takeWhile1 (/= ' ')) (A.char ' ')++parseMaybe :: A.Parser a -> B.ByteString -> Maybe a+parseMaybe = (check .) . A.parseOnly+ where check (Left _) = Nothing+ check (Right x) = Just x++keepTrying :: [B.ByteString] -> A.Parser a -> Maybe a+keepTrying bs p = asum (map (parseMaybe p) bs)++splitCPULines :: B.ByteString -> [[B.ByteString]]+splitCPULines = splitCPUs . BC.lines+ where splitCPUs [] = []+ splitCPUs ls = let (cs, lss) = break (== "") ls+ in cs : case lss of [] -> []+ (_:ls') -> splitCPUs ls'++tryCPU :: [B.ByteString] -> Maybe CPU+tryCPU bs = do+ proc <- keepTrying bs parseProcessor+ vend <- pure (keepTrying bs parseVendor)+ modl <- pure (keepTrying bs parseModel)+ modn <- pure (keepTrying bs parseModelName)+ rev <- pure (keepTrying bs parseRevision)+ mcode <- pure (keepTrying bs parseMicrocode)+ frq <- keepTrying bs parseFreq+ cch <- pure (keepTrying bs parseCache)+ pid <- keepTrying bs parsePhysicalID+ sib <- keepTrying bs parseSiblings+ cid <- keepTrying bs parseCoreID+ aid <- pure (keepTrying bs parseApicID)+ flpu <- pure (keepTrying bs parseFpu)+ flpex <- pure (keepTrying bs parseFpuExcept)+ flg <- pure (keepTrying bs parseFlags)+ bgm <- keepTrying bs parseBogoMIPS+ ca <- pure (keepTrying bs parseCacheAlignment)+ (pa, va) <- pure $ case keepTrying bs parseAddresses+ of Nothing -> (Nothing, Nothing)+ Just (p, v) -> (Just p, Just v)+ pure $ CPU proc+ vend+ modl+ modn+ rev+ mcode+ frq+ cch+ pid+ sib+ cid+ aid+ flpu+ flpex+ flg+ bgm+ ca+ pa+ va++-- | Read @\/proc\/cpuinfo@ and try to parse the output. If this function+-- returns 'Nothing' on your system, please file a bug report with your+-- @\/proc\/cpuinfo@ contents and CPU specifications.+tryGetCPUs :: IO (Maybe [CPU])+tryGetCPUs = (mapM tryCPU . splitCPULines)+ <$> B.readFile "/proc/cpuinfo"++-- | Read @\/proc\/cpuinfo@ and try to parse the output. If this function throws+-- an error on your system, please file a bug report with your+-- @\/proc\/cpuinfo@ contents and CPU specifications.+getCPUs :: IO [CPU]+getCPUs = fromMaybe (error e) <$> tryGetCPUs+ where e = unlines [ "Couldn't parse your /proc/cpuinfo contents."+ , "Please file a bug including your /proc/cpuinfo here:"+ , "https://github.com/traviswhitaker/cpuinfo/issues"+ ]++-- | Counts the number of physical processors in the system. A physical+-- processor corresponds to a single CPU unit in a single socket, i.e. unless+-- you have a multi-socket motherboard, this number will be one.+physicalProcessors :: [CPU] -> Int+physicalProcessors = length . nub . map physicalID++-- | Counts the number of physical cores in the system. A physical core is an+-- independent processing unit that reads and executes instructions on its+-- own, but potentially shares its die (and other resources) with other cores.+physicalCores :: [CPU] -> Int+physicalCores = length . nub . map (physicalID &&& coreID)++-- | Counts the number of logical cores in the system. A logical core is a+-- virtual processing unit exposed to the operating system, that may or may+-- not directly correspond with an independent physical processing unit, e.g.+-- a hyperthread appears as an independent processing unit to the operating+-- system, but has no physically dedicated execution resources.+logicalCores :: [CPU] -> Int+logicalCores = length++-- | The hyperthreading factor is the number of logical cores divided by the+-- number of physical cores. This quantity indicates the degree to which+-- physical execution resources are shared among logical processors, and may+-- be used to tune parallel applications.+hyperthreadingFactor :: [CPU] -> Rational+hyperthreadingFactor cpus = fromIntegral (logicalCores cpus)+ / fromIntegral (physicalCores cpus)++-- | If hyperthreading is in use, the 'hyperthreadingFactor' will be greater+-- than 1.+hyperthreadingInUse :: [CPU] -> Bool+hyperthreadingInUse = (/= 1) . hyperthreadingFactor