numa.py 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import os
  2. #
  3. def parse_range_list(range_list_str):
  4. """
  5. Take an input like '1-3,5,7-10' and return a list like [1,2,3,5,7,8,9,10].
  6. """
  7. #
  8. range_strings = range_list_str.split(',')
  9. all_items = []
  10. for range_str in range_strings:
  11. if '-' in range_str:
  12. # in the form '12-34'
  13. range_ends = [int(x) for x in range_str.split('-')]
  14. assert(len(range_ends) == 2)
  15. all_items.extend(range(range_ends[0], range_ends[1]+1))
  16. else:
  17. # just a number
  18. all_items.append(int(range_str))
  19. #
  20. #
  21. return all_items
  22. #
  23. def generate_range_list(l):
  24. """
  25. Take a list like [1,2,3,5,7,8,9,10] and return a string like '1-3,5,7-10'.
  26. """
  27. #
  28. l = list(set(sorted(l)))
  29. ranges = []
  30. current_range_start = None
  31. #
  32. for index in range(len(l)):
  33. if current_range_start is None:
  34. current_range_start = l[index]
  35. else:
  36. if l[index] != l[index-1]+1:
  37. ranges.append((current_range_start, l[index-1]))
  38. current_range_start = l[index]
  39. #
  40. #
  41. #
  42. ranges.append((current_range_start, l[-1]))
  43. #
  44. return ','.join(['-'.join([str(y) for y in x]) if x[0] != x[1] else str(x[0]) for x in ranges])
  45. #
  46. def check_path_traversal(path, base_path):
  47. # this is not guarenteed to be secure
  48. if os.path.commonprefix([os.path.realpath(path), base_path]) != base_path:
  49. raise Exception('The path \'{}\' is not in the base path \'{}.\''.format(os.path.realpath(path), base_path))
  50. #
  51. #
  52. def get_online_numa_nodes():
  53. with open('/sys/devices/system/node/online', 'r') as f:
  54. online_nodes = f.read().strip()
  55. return parse_range_list(online_nodes)
  56. #
  57. #
  58. def get_processors_in_numa_node(node_id):
  59. path = '/sys/devices/system/node/node{}/cpulist'.format(node_id)
  60. check_path_traversal(path, '/sys/devices/system/node')
  61. with open(path, 'r') as f:
  62. return parse_range_list(f.read().strip())
  63. #
  64. #
  65. def get_thread_siblings(processor_id):
  66. path = '/sys/devices/system/cpu/cpu{}/topology/thread_siblings_list'.format(processor_id)
  67. check_path_traversal(path, '/sys/devices/system/cpu')
  68. with open(path, 'r') as f:
  69. return parse_range_list(f.read().strip())
  70. #
  71. #
  72. def get_numa_overview():
  73. numa_nodes = {}
  74. #
  75. for node_id in get_online_numa_nodes():
  76. numa_nodes[node_id] = {}
  77. #
  78. for node_id in numa_nodes:
  79. processors = get_processors_in_numa_node(node_id)
  80. #
  81. numa_nodes[node_id]['physical_cores'] = []
  82. for processor_id in processors:
  83. thread_siblings = sorted(get_thread_siblings(processor_id))
  84. #
  85. if thread_siblings not in numa_nodes[node_id]['physical_cores']:
  86. numa_nodes[node_id]['physical_cores'].append(thread_siblings)
  87. #
  88. #
  89. #
  90. return numa_nodes
  91. #
  92. if __name__ == '__main__':
  93. print(get_numa_overview())
  94. #