xref: /aosp_15_r20/external/angle/build/toolchain/get_concurrent_links.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*8975f5c5SAndroid Build Coastguard Worker# Copyright 2014 The Chromium Authors
3*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker
6*8975f5c5SAndroid Build Coastguard Worker# This script computs the number of concurrent links we want to run in the build
7*8975f5c5SAndroid Build Coastguard Worker# as a function of machine spec. It's based on GetDefaultConcurrentLinks in GYP.
8*8975f5c5SAndroid Build Coastguard Worker
9*8975f5c5SAndroid Build Coastguard Workerimport argparse
10*8975f5c5SAndroid Build Coastguard Workerimport multiprocessing
11*8975f5c5SAndroid Build Coastguard Workerimport os
12*8975f5c5SAndroid Build Coastguard Workerimport re
13*8975f5c5SAndroid Build Coastguard Workerimport subprocess
14*8975f5c5SAndroid Build Coastguard Workerimport sys
15*8975f5c5SAndroid Build Coastguard Worker
16*8975f5c5SAndroid Build Coastguard Workersys.path.insert(1, os.path.join(os.path.dirname(__file__), '..'))
17*8975f5c5SAndroid Build Coastguard Workerimport gn_helpers
18*8975f5c5SAndroid Build Coastguard Worker
19*8975f5c5SAndroid Build Coastguard Worker
20*8975f5c5SAndroid Build Coastguard Workerdef _GetTotalMemoryInBytes():
21*8975f5c5SAndroid Build Coastguard Worker  if sys.platform in ('win32', 'cygwin'):
22*8975f5c5SAndroid Build Coastguard Worker    import ctypes
23*8975f5c5SAndroid Build Coastguard Worker
24*8975f5c5SAndroid Build Coastguard Worker    class MEMORYSTATUSEX(ctypes.Structure):
25*8975f5c5SAndroid Build Coastguard Worker      _fields_ = [
26*8975f5c5SAndroid Build Coastguard Worker          ("dwLength", ctypes.c_ulong),
27*8975f5c5SAndroid Build Coastguard Worker          ("dwMemoryLoad", ctypes.c_ulong),
28*8975f5c5SAndroid Build Coastguard Worker          ("ullTotalPhys", ctypes.c_ulonglong),
29*8975f5c5SAndroid Build Coastguard Worker          ("ullAvailPhys", ctypes.c_ulonglong),
30*8975f5c5SAndroid Build Coastguard Worker          ("ullTotalPageFile", ctypes.c_ulonglong),
31*8975f5c5SAndroid Build Coastguard Worker          ("ullAvailPageFile", ctypes.c_ulonglong),
32*8975f5c5SAndroid Build Coastguard Worker          ("ullTotalVirtual", ctypes.c_ulonglong),
33*8975f5c5SAndroid Build Coastguard Worker          ("ullAvailVirtual", ctypes.c_ulonglong),
34*8975f5c5SAndroid Build Coastguard Worker          ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
35*8975f5c5SAndroid Build Coastguard Worker      ]
36*8975f5c5SAndroid Build Coastguard Worker
37*8975f5c5SAndroid Build Coastguard Worker    stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX))
38*8975f5c5SAndroid Build Coastguard Worker    ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
39*8975f5c5SAndroid Build Coastguard Worker    return stat.ullTotalPhys
40*8975f5c5SAndroid Build Coastguard Worker  elif sys.platform.startswith('linux'):
41*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists("/proc/meminfo"):
42*8975f5c5SAndroid Build Coastguard Worker      with open("/proc/meminfo") as meminfo:
43*8975f5c5SAndroid Build Coastguard Worker        memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
44*8975f5c5SAndroid Build Coastguard Worker        for line in meminfo:
45*8975f5c5SAndroid Build Coastguard Worker          match = memtotal_re.match(line)
46*8975f5c5SAndroid Build Coastguard Worker          if not match:
47*8975f5c5SAndroid Build Coastguard Worker            continue
48*8975f5c5SAndroid Build Coastguard Worker          return float(match.group(1)) * 2**10
49*8975f5c5SAndroid Build Coastguard Worker  elif sys.platform == 'darwin':
50*8975f5c5SAndroid Build Coastguard Worker    try:
51*8975f5c5SAndroid Build Coastguard Worker      return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
52*8975f5c5SAndroid Build Coastguard Worker    except Exception:
53*8975f5c5SAndroid Build Coastguard Worker      return 0
54*8975f5c5SAndroid Build Coastguard Worker  # TODO(scottmg): Implement this for other platforms.
55*8975f5c5SAndroid Build Coastguard Worker  return 0
56*8975f5c5SAndroid Build Coastguard Worker
57*8975f5c5SAndroid Build Coastguard Worker
58*8975f5c5SAndroid Build Coastguard Workerdef _GetDefaultConcurrentLinks(per_link_gb, reserve_gb, thin_lto_type,
59*8975f5c5SAndroid Build Coastguard Worker                               secondary_per_link_gb, override_ram_in_gb):
60*8975f5c5SAndroid Build Coastguard Worker  explanation = []
61*8975f5c5SAndroid Build Coastguard Worker  explanation.append(
62*8975f5c5SAndroid Build Coastguard Worker      'per_link_gb={} reserve_gb={} secondary_per_link_gb={}'.format(
63*8975f5c5SAndroid Build Coastguard Worker          per_link_gb, reserve_gb, secondary_per_link_gb))
64*8975f5c5SAndroid Build Coastguard Worker  if override_ram_in_gb:
65*8975f5c5SAndroid Build Coastguard Worker    mem_total_gb = override_ram_in_gb
66*8975f5c5SAndroid Build Coastguard Worker  else:
67*8975f5c5SAndroid Build Coastguard Worker    mem_total_gb = float(_GetTotalMemoryInBytes()) / 2**30
68*8975f5c5SAndroid Build Coastguard Worker  adjusted_mem_total_gb = max(0, mem_total_gb - reserve_gb)
69*8975f5c5SAndroid Build Coastguard Worker
70*8975f5c5SAndroid Build Coastguard Worker  # Ensure that there is at least as many links allocated for the secondary as
71*8975f5c5SAndroid Build Coastguard Worker  # there is for the primary. The secondary link usually uses fewer gbs.
72*8975f5c5SAndroid Build Coastguard Worker  mem_cap = int(
73*8975f5c5SAndroid Build Coastguard Worker      max(1, adjusted_mem_total_gb / (per_link_gb + secondary_per_link_gb)))
74*8975f5c5SAndroid Build Coastguard Worker
75*8975f5c5SAndroid Build Coastguard Worker  try:
76*8975f5c5SAndroid Build Coastguard Worker    cpu_count = multiprocessing.cpu_count()
77*8975f5c5SAndroid Build Coastguard Worker  except:
78*8975f5c5SAndroid Build Coastguard Worker    cpu_count = 1
79*8975f5c5SAndroid Build Coastguard Worker
80*8975f5c5SAndroid Build Coastguard Worker  # A local LTO links saturate all cores, but only for some amount of the link.
81*8975f5c5SAndroid Build Coastguard Worker  cpu_cap = cpu_count
82*8975f5c5SAndroid Build Coastguard Worker  if thin_lto_type is not None:
83*8975f5c5SAndroid Build Coastguard Worker    assert thin_lto_type == 'local'
84*8975f5c5SAndroid Build Coastguard Worker    cpu_cap = min(cpu_count, 6)
85*8975f5c5SAndroid Build Coastguard Worker
86*8975f5c5SAndroid Build Coastguard Worker  explanation.append(
87*8975f5c5SAndroid Build Coastguard Worker      'cpu_count={} cpu_cap={} mem_total_gb={:.1f}GiB adjusted_mem_total_gb={:.1f}GiB'
88*8975f5c5SAndroid Build Coastguard Worker      .format(cpu_count, cpu_cap, mem_total_gb, adjusted_mem_total_gb))
89*8975f5c5SAndroid Build Coastguard Worker
90*8975f5c5SAndroid Build Coastguard Worker  num_links = min(mem_cap, cpu_cap)
91*8975f5c5SAndroid Build Coastguard Worker  if num_links == cpu_cap:
92*8975f5c5SAndroid Build Coastguard Worker    if cpu_cap == cpu_count:
93*8975f5c5SAndroid Build Coastguard Worker      reason = 'cpu_count'
94*8975f5c5SAndroid Build Coastguard Worker    else:
95*8975f5c5SAndroid Build Coastguard Worker      reason = 'cpu_cap (thinlto)'
96*8975f5c5SAndroid Build Coastguard Worker  else:
97*8975f5c5SAndroid Build Coastguard Worker    reason = 'RAM'
98*8975f5c5SAndroid Build Coastguard Worker
99*8975f5c5SAndroid Build Coastguard Worker  # static link see too many open files if we have many concurrent links.
100*8975f5c5SAndroid Build Coastguard Worker  # ref: http://b/233068481
101*8975f5c5SAndroid Build Coastguard Worker  if num_links > 30:
102*8975f5c5SAndroid Build Coastguard Worker    num_links = 30
103*8975f5c5SAndroid Build Coastguard Worker    reason = 'nofile'
104*8975f5c5SAndroid Build Coastguard Worker
105*8975f5c5SAndroid Build Coastguard Worker  explanation.append('concurrent_links={}  (reason: {})'.format(
106*8975f5c5SAndroid Build Coastguard Worker      num_links, reason))
107*8975f5c5SAndroid Build Coastguard Worker
108*8975f5c5SAndroid Build Coastguard Worker  # Use remaining RAM for a secondary pool if needed.
109*8975f5c5SAndroid Build Coastguard Worker  if secondary_per_link_gb:
110*8975f5c5SAndroid Build Coastguard Worker    mem_remaining = adjusted_mem_total_gb - num_links * per_link_gb
111*8975f5c5SAndroid Build Coastguard Worker    secondary_size = int(max(0, mem_remaining / secondary_per_link_gb))
112*8975f5c5SAndroid Build Coastguard Worker    if secondary_size > cpu_count:
113*8975f5c5SAndroid Build Coastguard Worker      secondary_size = cpu_count
114*8975f5c5SAndroid Build Coastguard Worker      reason = 'cpu_count'
115*8975f5c5SAndroid Build Coastguard Worker    else:
116*8975f5c5SAndroid Build Coastguard Worker      reason = 'mem_remaining={:.1f}GiB'.format(mem_remaining)
117*8975f5c5SAndroid Build Coastguard Worker    explanation.append('secondary_size={} (reason: {})'.format(
118*8975f5c5SAndroid Build Coastguard Worker        secondary_size, reason))
119*8975f5c5SAndroid Build Coastguard Worker  else:
120*8975f5c5SAndroid Build Coastguard Worker    secondary_size = 0
121*8975f5c5SAndroid Build Coastguard Worker
122*8975f5c5SAndroid Build Coastguard Worker  return num_links, secondary_size, explanation
123*8975f5c5SAndroid Build Coastguard Worker
124*8975f5c5SAndroid Build Coastguard Worker
125*8975f5c5SAndroid Build Coastguard Workerdef main():
126*8975f5c5SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
127*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('--mem_per_link_gb', type=int, default=8)
128*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('--reserve_mem_gb', type=int, default=0)
129*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('--secondary_mem_per_link', type=int, default=0)
130*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('--override-ram-in-gb-for-testing', type=float, default=0)
131*8975f5c5SAndroid Build Coastguard Worker  parser.add_argument('--thin-lto')
132*8975f5c5SAndroid Build Coastguard Worker  options = parser.parse_args()
133*8975f5c5SAndroid Build Coastguard Worker
134*8975f5c5SAndroid Build Coastguard Worker  primary_pool_size, secondary_pool_size, explanation = (
135*8975f5c5SAndroid Build Coastguard Worker      _GetDefaultConcurrentLinks(options.mem_per_link_gb,
136*8975f5c5SAndroid Build Coastguard Worker                                 options.reserve_mem_gb, options.thin_lto,
137*8975f5c5SAndroid Build Coastguard Worker                                 options.secondary_mem_per_link,
138*8975f5c5SAndroid Build Coastguard Worker                                 options.override_ram_in_gb_for_testing))
139*8975f5c5SAndroid Build Coastguard Worker  if options.override_ram_in_gb_for_testing:
140*8975f5c5SAndroid Build Coastguard Worker    print('primary={} secondary={} explanation={}'.format(
141*8975f5c5SAndroid Build Coastguard Worker        primary_pool_size, secondary_pool_size, explanation))
142*8975f5c5SAndroid Build Coastguard Worker  else:
143*8975f5c5SAndroid Build Coastguard Worker    sys.stdout.write(
144*8975f5c5SAndroid Build Coastguard Worker        gn_helpers.ToGNString({
145*8975f5c5SAndroid Build Coastguard Worker            'primary_pool_size': primary_pool_size,
146*8975f5c5SAndroid Build Coastguard Worker            'secondary_pool_size': secondary_pool_size,
147*8975f5c5SAndroid Build Coastguard Worker            'explanation': explanation,
148*8975f5c5SAndroid Build Coastguard Worker        }))
149*8975f5c5SAndroid Build Coastguard Worker  return 0
150*8975f5c5SAndroid Build Coastguard Worker
151*8975f5c5SAndroid Build Coastguard Worker
152*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__':
153*8975f5c5SAndroid Build Coastguard Worker  sys.exit(main())
154