cpuset.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cpuset
  14. import (
  15. "bytes"
  16. "fmt"
  17. "k8s.io/klog"
  18. "reflect"
  19. "sort"
  20. "strconv"
  21. "strings"
  22. )
  23. // Builder is a mutable builder for CPUSet. Functions that mutate instances
  24. // of this type are not thread-safe.
  25. type Builder struct {
  26. result CPUSet
  27. done bool
  28. }
  29. // NewBuilder returns a mutable CPUSet builder.
  30. func NewBuilder() Builder {
  31. return Builder{
  32. result: CPUSet{
  33. elems: map[int]struct{}{},
  34. },
  35. }
  36. }
  37. // Add adds the supplied elements to the result. Calling Add after calling
  38. // Result has no effect.
  39. func (b Builder) Add(elems ...int) {
  40. if b.done {
  41. return
  42. }
  43. for _, elem := range elems {
  44. b.result.elems[elem] = struct{}{}
  45. }
  46. }
  47. // Result returns the result CPUSet containing all elements that were
  48. // previously added to this builder. Subsequent calls to Add have no effect.
  49. func (b Builder) Result() CPUSet {
  50. b.done = true
  51. return b.result
  52. }
  53. // CPUSet is a thread-safe, immutable set-like data structure for CPU IDs.
  54. type CPUSet struct {
  55. elems map[int]struct{}
  56. }
  57. // NewCPUSet returns a new CPUSet containing the supplied elements.
  58. func NewCPUSet(cpus ...int) CPUSet {
  59. b := NewBuilder()
  60. for _, c := range cpus {
  61. b.Add(c)
  62. }
  63. return b.Result()
  64. }
  65. // Size returns the number of elements in this set.
  66. func (s CPUSet) Size() int {
  67. return len(s.elems)
  68. }
  69. // IsEmpty returns true if there are zero elements in this set.
  70. func (s CPUSet) IsEmpty() bool {
  71. return s.Size() == 0
  72. }
  73. // Contains returns true if the supplied element is present in this set.
  74. func (s CPUSet) Contains(cpu int) bool {
  75. _, found := s.elems[cpu]
  76. return found
  77. }
  78. // Equals returns true if the supplied set contains exactly the same elements
  79. // as this set (s IsSubsetOf s2 and s2 IsSubsetOf s).
  80. func (s CPUSet) Equals(s2 CPUSet) bool {
  81. return reflect.DeepEqual(s.elems, s2.elems)
  82. }
  83. // Filter returns a new CPU set that contains all of the elements from this
  84. // set that match the supplied predicate, without mutating the source set.
  85. func (s CPUSet) Filter(predicate func(int) bool) CPUSet {
  86. b := NewBuilder()
  87. for cpu := range s.elems {
  88. if predicate(cpu) {
  89. b.Add(cpu)
  90. }
  91. }
  92. return b.Result()
  93. }
  94. // FilterNot returns a new CPU set that contains all of the elements from this
  95. // set that do not match the supplied predicate, without mutating the source
  96. // set.
  97. func (s CPUSet) FilterNot(predicate func(int) bool) CPUSet {
  98. b := NewBuilder()
  99. for cpu := range s.elems {
  100. if !predicate(cpu) {
  101. b.Add(cpu)
  102. }
  103. }
  104. return b.Result()
  105. }
  106. // IsSubsetOf returns true if the supplied set contains all the elements
  107. func (s CPUSet) IsSubsetOf(s2 CPUSet) bool {
  108. result := true
  109. for cpu := range s.elems {
  110. if !s2.Contains(cpu) {
  111. result = false
  112. break
  113. }
  114. }
  115. return result
  116. }
  117. // Union returns a new CPU set that contains all of the elements from this
  118. // set and all of the elements from the supplied set, without mutating
  119. // either source set.
  120. func (s CPUSet) Union(s2 CPUSet) CPUSet {
  121. b := NewBuilder()
  122. for cpu := range s.elems {
  123. b.Add(cpu)
  124. }
  125. for cpu := range s2.elems {
  126. b.Add(cpu)
  127. }
  128. return b.Result()
  129. }
  130. // UnionAll returns a new CPU set that contains all of the elements from this
  131. // set and all of the elements from the supplied sets, without mutating
  132. // either source set.
  133. func (s CPUSet) UnionAll(s2 []CPUSet) CPUSet {
  134. b := NewBuilder()
  135. for cpu := range s.elems {
  136. b.Add(cpu)
  137. }
  138. for _, cs := range s2 {
  139. for cpu := range cs.elems {
  140. b.Add(cpu)
  141. }
  142. }
  143. return b.Result()
  144. }
  145. // Intersection returns a new CPU set that contains all of the elements
  146. // that are present in both this set and the supplied set, without mutating
  147. // either source set.
  148. func (s CPUSet) Intersection(s2 CPUSet) CPUSet {
  149. return s.Filter(func(cpu int) bool { return s2.Contains(cpu) })
  150. }
  151. // Difference returns a new CPU set that contains all of the elements that
  152. // are present in this set and not the supplied set, without mutating either
  153. // source set.
  154. func (s CPUSet) Difference(s2 CPUSet) CPUSet {
  155. return s.FilterNot(func(cpu int) bool { return s2.Contains(cpu) })
  156. }
  157. // ToSlice returns a slice of integers that contains all elements from
  158. // this set.
  159. func (s CPUSet) ToSlice() []int {
  160. result := []int{}
  161. for cpu := range s.elems {
  162. result = append(result, cpu)
  163. }
  164. sort.Ints(result)
  165. return result
  166. }
  167. // ToSliceNoSort returns a slice of integers that contains all elements from
  168. // this set.
  169. func (s CPUSet) ToSliceNoSort() []int {
  170. result := []int{}
  171. for cpu := range s.elems {
  172. result = append(result, cpu)
  173. }
  174. return result
  175. }
  176. // String returns a new string representation of the elements in this CPU set
  177. // in canonical linux CPU list format.
  178. //
  179. // See: http://man7.org/linux/man-pages/man7/cpuset.7.html#FORMATS
  180. func (s CPUSet) String() string {
  181. if s.IsEmpty() {
  182. return ""
  183. }
  184. elems := s.ToSlice()
  185. type rng struct {
  186. start int
  187. end int
  188. }
  189. ranges := []rng{{elems[0], elems[0]}}
  190. for i := 1; i < len(elems); i++ {
  191. lastRange := &ranges[len(ranges)-1]
  192. // if this element is adjacent to the high end of the last range
  193. if elems[i] == lastRange.end+1 {
  194. // then extend the last range to include this element
  195. lastRange.end = elems[i]
  196. continue
  197. }
  198. // otherwise, start a new range beginning with this element
  199. ranges = append(ranges, rng{elems[i], elems[i]})
  200. }
  201. // construct string from ranges
  202. var result bytes.Buffer
  203. for _, r := range ranges {
  204. if r.start == r.end {
  205. result.WriteString(strconv.Itoa(r.start))
  206. } else {
  207. result.WriteString(fmt.Sprintf("%d-%d", r.start, r.end))
  208. }
  209. result.WriteString(",")
  210. }
  211. return strings.TrimRight(result.String(), ",")
  212. }
  213. // MustParse CPUSet constructs a new CPU set from a Linux CPU list formatted
  214. // string. Unlike Parse, it does not return an error but rather panics if the
  215. // input cannot be used to construct a CPU set.
  216. func MustParse(s string) CPUSet {
  217. res, err := Parse(s)
  218. if err != nil {
  219. klog.Fatalf("unable to parse [%s] as CPUSet: %v", s, err)
  220. }
  221. return res
  222. }
  223. // Parse CPUSet constructs a new CPU set from a Linux CPU list formatted string.
  224. //
  225. // See: http://man7.org/linux/man-pages/man7/cpuset.7.html#FORMATS
  226. func Parse(s string) (CPUSet, error) {
  227. b := NewBuilder()
  228. // Handle empty string.
  229. if s == "" {
  230. return b.Result(), nil
  231. }
  232. // Split CPU list string:
  233. // "0-5,34,46-48 => ["0-5", "34", "46-48"]
  234. ranges := strings.Split(s, ",")
  235. for _, r := range ranges {
  236. boundaries := strings.Split(r, "-")
  237. if len(boundaries) == 1 {
  238. // Handle ranges that consist of only one element like "34".
  239. elem, err := strconv.Atoi(boundaries[0])
  240. if err != nil {
  241. return NewCPUSet(), err
  242. }
  243. b.Add(elem)
  244. } else if len(boundaries) == 2 {
  245. // Handle multi-element ranges like "0-5".
  246. start, err := strconv.Atoi(boundaries[0])
  247. if err != nil {
  248. return NewCPUSet(), err
  249. }
  250. end, err := strconv.Atoi(boundaries[1])
  251. if err != nil {
  252. return NewCPUSet(), err
  253. }
  254. // Add all elements to the result.
  255. // e.g. "0-5", "46-48" => [0, 1, 2, 3, 4, 5, 46, 47, 48].
  256. for e := start; e <= end; e++ {
  257. b.Add(e)
  258. }
  259. }
  260. }
  261. return b.Result(), nil
  262. }
  263. // Clone returns a copy of this CPU set.
  264. func (s CPUSet) Clone() CPUSet {
  265. b := NewBuilder()
  266. for elem := range s.elems {
  267. b.Add(elem)
  268. }
  269. return b.Result()
  270. }