@@ -175,6 +175,7 @@ type cpuInfo struct {
175175 family int // CPU family number
176176 model int // CPU model number
177177 cacheline int // Cache line size in bytes. Will be 0 if undetectable.
178+ hz int64 // Clock speed, if known
178179 cache struct {
179180 l1i int // L1 Instruction Cache (per core or shared). Will be -1 if undetected
180181 l1d int // L1 Data Cache (per core or shared). Will be -1 if undetected
@@ -222,6 +223,7 @@ func detect() {
222223 cpu .logicalcores = logicalCores ()
223224 cpu .physicalcores = physicalCores ()
224225 cpu .vendorid = vendorID ()
226+ cpu .hz = hertz (cpu .brandname )
225227 cpu .cacheSize ()
226228}
227229
@@ -598,6 +600,65 @@ func (c cpuInfo) logicalcpu() int {
598600 return int (ebx >> 24 )
599601}
600602
603+ // hertz tries to compute the clock speed of the CPU. If leaf 15 is
604+ // supported, use it, otherwise parse the brand string. Yes, really.
605+ func hertz (model string ) int64 {
606+ mfi := maxFunctionID ()
607+ if mfi >= 0x15 {
608+ eax , ebx , ecx , _ := cpuid (0x15 )
609+ if eax != 0 && ebx != 0 && ecx != 0 {
610+ return int64 ((int64 (ecx ) * int64 (ebx )) / int64 (eax ))
611+ }
612+ }
613+ // computeHz determines the official rated speed of a CPU from its brand
614+ // string. This insanity is *actually the official documented way to do
615+ // this according to Intel*, prior to leaf 0x15 existing. The official
616+ // documentation only shows this working for exactly `x.xx` or `xxxx`
617+ // cases, e.g., `2.50GHz` or `1300MHz`; this parser will accept other
618+ // sizes.
619+ hz := strings .LastIndex (model , "Hz" )
620+ if hz < 3 {
621+ return - 1
622+ }
623+ var multiplier int64
624+ switch model [hz - 1 ] {
625+ case 'M' :
626+ multiplier = 1000 * 1000
627+ case 'G' :
628+ multiplier = 1000 * 1000 * 1000
629+ case 'T' :
630+ multiplier = 1000 * 1000 * 1000 * 1000
631+ }
632+ if multiplier == 0 {
633+ return - 1
634+ }
635+ freq := int64 (0 )
636+ divisor := int64 (0 )
637+ decimalShift := int64 (1 )
638+ var i int
639+ for i = hz - 2 ; i >= 0 && model [i ] != ' ' ; i -- {
640+ if model [i ] >= '0' && model [i ] <= '9' {
641+ freq += int64 (model [i ]- '0' ) * decimalShift
642+ decimalShift *= 10
643+ } else if model [i ] == '.' {
644+ if divisor != 0 {
645+ return - 1
646+ }
647+ divisor = decimalShift
648+ } else {
649+ return - 1
650+ }
651+ }
652+ // we didn't find a space
653+ if i < 0 {
654+ return - 1
655+ }
656+ if divisor != 0 {
657+ return (freq * multiplier ) / divisor
658+ }
659+ return freq * multiplier
660+ }
661+
601662// VM Will return true if the cpu id indicates we are in
602663// a virtual machine. This is only a hint, and will very likely
603664// have many false negatives.
@@ -849,7 +910,7 @@ func (c *cpuInfo) cacheSize() {
849910 if maxExtendedFunction () < 0x8000001D {
850911 return
851912 }
852- for i := uint32 (0 ); i < math .maxuint32 ; i ++ {
913+ for i := uint32 (0 ); i < math .MaxUint32 ; i ++ {
853914 eax , ebx , ecx , _ := cpuidex (0x8000001D , i )
854915
855916 level := (eax >> 5 ) & 7
0 commit comments