gpu_linux.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. package discover
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. "reflect"
  8. "regexp"
  9. "sort"
  10. "strings"
  11. "github.com/ollama/ollama/format"
  12. )
  13. var CudartGlobs = []string{
  14. "/usr/local/cuda/lib64/libcudart.so*",
  15. "/usr/lib/x86_64-linux-gnu/nvidia/current/libcudart.so*",
  16. "/usr/lib/x86_64-linux-gnu/libcudart.so*",
  17. "/usr/lib/wsl/lib/libcudart.so*",
  18. "/usr/lib/wsl/drivers/*/libcudart.so*",
  19. "/opt/cuda/lib64/libcudart.so*",
  20. "/usr/local/cuda*/targets/aarch64-linux/lib/libcudart.so*",
  21. "/usr/lib/aarch64-linux-gnu/nvidia/current/libcudart.so*",
  22. "/usr/lib/aarch64-linux-gnu/libcudart.so*",
  23. "/usr/local/cuda/lib*/libcudart.so*",
  24. "/usr/lib*/libcudart.so*",
  25. "/usr/local/lib*/libcudart.so*",
  26. }
  27. var NvmlGlobs = []string{}
  28. var NvcudaGlobs = []string{
  29. "/usr/local/cuda*/targets/*/lib/libcuda.so*",
  30. "/usr/lib/*-linux-gnu/nvidia/current/libcuda.so*",
  31. "/usr/lib/*-linux-gnu/libcuda.so*",
  32. "/usr/lib/wsl/lib/libcuda.so*",
  33. "/usr/lib/wsl/drivers/*/libcuda.so*",
  34. "/opt/cuda/lib*/libcuda.so*",
  35. "/usr/local/cuda/lib*/libcuda.so*",
  36. "/usr/lib*/libcuda.so*",
  37. "/usr/local/lib*/libcuda.so*",
  38. }
  39. var OneapiGlobs = []string{
  40. "/usr/lib/x86_64-linux-gnu/libze_intel_gpu.so*",
  41. "/usr/lib*/libze_intel_gpu.so*",
  42. }
  43. var (
  44. CudartMgmtName = "libcudart.so*"
  45. NvcudaMgmtName = "libcuda.so*"
  46. NvmlMgmtName = "" // not currently wired on linux
  47. OneapiMgmtName = "libze_intel_gpu.so*"
  48. )
  49. func GetCPUMem() (memInfo, error) {
  50. var mem memInfo
  51. var total, available, free, buffers, cached, freeSwap uint64
  52. f, err := os.Open("/proc/meminfo")
  53. if err != nil {
  54. return mem, err
  55. }
  56. defer f.Close()
  57. s := bufio.NewScanner(f)
  58. for s.Scan() {
  59. line := s.Text()
  60. switch {
  61. case strings.HasPrefix(line, "MemTotal:"):
  62. _, err = fmt.Sscanf(line, "MemTotal:%d", &total)
  63. case strings.HasPrefix(line, "MemAvailable:"):
  64. _, err = fmt.Sscanf(line, "MemAvailable:%d", &available)
  65. case strings.HasPrefix(line, "MemFree:"):
  66. _, err = fmt.Sscanf(line, "MemFree:%d", &free)
  67. case strings.HasPrefix(line, "Buffers:"):
  68. _, err = fmt.Sscanf(line, "Buffers:%d", &buffers)
  69. case strings.HasPrefix(line, "Cached:"):
  70. _, err = fmt.Sscanf(line, "Cached:%d", &cached)
  71. case strings.HasPrefix(line, "SwapFree:"):
  72. _, err = fmt.Sscanf(line, "SwapFree:%d", &freeSwap)
  73. default:
  74. continue
  75. }
  76. if err != nil {
  77. return mem, err
  78. }
  79. }
  80. mem.TotalMemory = total * format.KibiByte
  81. mem.FreeSwap = freeSwap * format.KibiByte
  82. if available > 0 {
  83. mem.FreeMemory = available * format.KibiByte
  84. } else {
  85. mem.FreeMemory = (free + buffers + cached) * format.KibiByte
  86. }
  87. return mem, nil
  88. }
  89. const CpuInfoFilename = "/proc/cpuinfo"
  90. type linuxCpuInfo struct {
  91. ID string `cpuinfo:"processor"`
  92. VendorID string `cpuinfo:"vendor_id"`
  93. ModelName string `cpuinfo:"model name"`
  94. PhysicalID string `cpuinfo:"physical id"`
  95. Siblings string `cpuinfo:"siblings"`
  96. CoreID string `cpuinfo:"core id"`
  97. }
  98. func GetCPUDetails() ([]CPU, error) {
  99. file, err := os.Open(CpuInfoFilename)
  100. if err != nil {
  101. return nil, err
  102. }
  103. return linuxCPUDetails(file)
  104. }
  105. func linuxCPUDetails(file io.Reader) ([]CPU, error) {
  106. reColumns := regexp.MustCompile("\t+: ")
  107. scanner := bufio.NewScanner(file)
  108. cpuInfos := []linuxCpuInfo{}
  109. cpu := &linuxCpuInfo{}
  110. for scanner.Scan() {
  111. line := scanner.Text()
  112. if sl := reColumns.Split(line, 2); len(sl) > 1 {
  113. t := reflect.TypeOf(cpu).Elem()
  114. s := reflect.ValueOf(cpu).Elem()
  115. for i := range t.NumField() {
  116. field := t.Field(i)
  117. tag := field.Tag.Get("cpuinfo")
  118. if tag == sl[0] {
  119. s.FieldByName(field.Name).SetString(sl[1])
  120. break
  121. }
  122. }
  123. } else if strings.TrimSpace(line) == "" && cpu.ID != "" {
  124. cpuInfos = append(cpuInfos, *cpu)
  125. cpu = &linuxCpuInfo{}
  126. }
  127. }
  128. if cpu.ID != "" {
  129. cpuInfos = append(cpuInfos, *cpu)
  130. }
  131. // Process the sockets/cores/threads
  132. socketByID := map[string]*CPU{}
  133. coreBySocket := map[string]map[string]struct{}{}
  134. threadsByCoreBySocket := map[string]map[string]int{}
  135. for _, c := range cpuInfos {
  136. if _, found := socketByID[c.PhysicalID]; !found {
  137. socketByID[c.PhysicalID] = &CPU{
  138. ID: c.PhysicalID,
  139. VendorID: c.VendorID,
  140. ModelName: c.ModelName,
  141. }
  142. coreBySocket[c.PhysicalID] = map[string]struct{}{}
  143. threadsByCoreBySocket[c.PhysicalID] = map[string]int{}
  144. }
  145. if c.CoreID != "" {
  146. coreBySocket[c.PhysicalID][c.PhysicalID+":"+c.CoreID] = struct{}{}
  147. threadsByCoreBySocket[c.PhysicalID][c.PhysicalID+":"+c.CoreID]++
  148. } else {
  149. coreBySocket[c.PhysicalID][c.PhysicalID+":"+c.ID] = struct{}{}
  150. threadsByCoreBySocket[c.PhysicalID][c.PhysicalID+":"+c.ID]++
  151. }
  152. }
  153. // Tally up the values from the tracking maps
  154. for id, s := range socketByID {
  155. s.CoreCount = len(coreBySocket[id])
  156. s.ThreadCount = 0
  157. for _, tc := range threadsByCoreBySocket[id] {
  158. s.ThreadCount += tc
  159. }
  160. // This only works if HT is enabled, consider a more reliable model, maybe cache size comparisons?
  161. efficiencyCoreCount := 0
  162. for _, threads := range threadsByCoreBySocket[id] {
  163. if threads == 1 {
  164. efficiencyCoreCount++
  165. }
  166. }
  167. if efficiencyCoreCount == s.CoreCount {
  168. // 1:1 mapping means they're not actually efficiency cores, but regular cores
  169. s.EfficiencyCoreCount = 0
  170. } else {
  171. s.EfficiencyCoreCount = efficiencyCoreCount
  172. }
  173. }
  174. keys := make([]string, 0, len(socketByID))
  175. result := make([]CPU, 0, len(socketByID))
  176. for k := range socketByID {
  177. keys = append(keys, k)
  178. }
  179. sort.Strings(keys)
  180. for _, k := range keys {
  181. result = append(result, *socketByID[k])
  182. }
  183. return result, nil
  184. }