|
56 | 56 | from mypy.cache import ( |
57 | 57 | CACHE_VERSION, |
58 | 58 | DICT_STR_GEN, |
| 59 | + LIST_GEN, |
59 | 60 | LITERAL_NONE, |
60 | 61 | CacheMeta, |
61 | 62 | ReadBuffer, |
62 | 63 | SerializedError, |
63 | 64 | Tag, |
64 | 65 | WriteBuffer, |
| 66 | + read_bytes, |
65 | 67 | read_int, |
66 | 68 | read_int_list, |
67 | 69 | read_int_opt, |
68 | 70 | read_str, |
69 | 71 | read_str_list, |
70 | 72 | read_str_opt, |
| 73 | + write_bytes, |
71 | 74 | write_int, |
72 | 75 | write_int_list, |
73 | 76 | write_int_opt, |
@@ -2391,6 +2394,93 @@ def __init__( |
2391 | 2394 | self.add_ancestors() |
2392 | 2395 | self.size_hint = size_hint |
2393 | 2396 |
|
| 2397 | + def write(self, buf: WriteBuffer) -> None: |
| 2398 | + """Serialize State for sending to build worker. |
| 2399 | +
|
| 2400 | + Note that unlike write() methods for most other classes, this one is |
| 2401 | + not idempotent. We erase some bulky values that should either be not needed |
| 2402 | + for processing by the worker, or can be re-created from other data relatively |
| 2403 | + quickly. These are: |
| 2404 | + * self.meta: workers will call self.reload_meta() anyway. |
| 2405 | + * self.options: can be restored with Options.clone_for_module(). |
| 2406 | + * self.error_lines: fresh errors are handled by the coordinator. |
| 2407 | + """ |
| 2408 | + write_int(buf, self.order) |
| 2409 | + write_str(buf, self.id) |
| 2410 | + write_str_opt(buf, self.path) |
| 2411 | + write_str_opt(buf, self.source) # mostly for mypy -c '<some code>' |
| 2412 | + write_bool(buf, self.ignore_all) |
| 2413 | + write_int(buf, self.caller_line) |
| 2414 | + write_tag(buf, LIST_GEN) |
| 2415 | + write_int_bare(buf, len(self.import_context)) |
| 2416 | + for path, line in self.import_context: |
| 2417 | + write_str(buf, path) |
| 2418 | + write_int(buf, line) |
| 2419 | + write_bytes(buf, self.interface_hash) |
| 2420 | + write_str_opt(buf, self.meta_source_hash) |
| 2421 | + write_str_list(buf, self.dependencies) |
| 2422 | + write_str_list(buf, self.suppressed) |
| 2423 | + # TODO: we can possibly serialize these dictionaries in a more compact way. |
| 2424 | + # Most keys in the dictionaries should be the same, so we can write them once. |
| 2425 | + write_tag(buf, DICT_STR_GEN) |
| 2426 | + write_int_bare(buf, len(self.priorities)) |
| 2427 | + for mod_id, prio in self.priorities.items(): |
| 2428 | + write_str_bare(buf, mod_id) |
| 2429 | + write_int(buf, prio) |
| 2430 | + write_tag(buf, DICT_STR_GEN) |
| 2431 | + write_int_bare(buf, len(self.dep_line_map)) |
| 2432 | + for mod_id, line in self.dep_line_map.items(): |
| 2433 | + write_str_bare(buf, mod_id) |
| 2434 | + write_int(buf, line) |
| 2435 | + write_tag(buf, DICT_STR_GEN) |
| 2436 | + write_int_bare(buf, len(self.dep_hashes)) |
| 2437 | + for mod_id, dep_hash in self.dep_hashes.items(): |
| 2438 | + write_str_bare(buf, mod_id) |
| 2439 | + write_bytes(buf, dep_hash) |
| 2440 | + write_int(buf, self.size_hint) |
| 2441 | + |
| 2442 | + @classmethod |
| 2443 | + def read(cls, buf: ReadBuffer, manager: BuildManager) -> State: |
| 2444 | + order = read_int(buf) |
| 2445 | + id = read_str(buf) |
| 2446 | + path = read_str_opt(buf) |
| 2447 | + source = read_str_opt(buf) |
| 2448 | + ignore_all = read_bool(buf) |
| 2449 | + caller_line = read_int(buf) |
| 2450 | + assert read_tag(buf) == LIST_GEN |
| 2451 | + import_context = [(read_str(buf), read_int(buf)) for _ in range(read_int_bare(buf))] |
| 2452 | + interface_hash = read_bytes(buf) |
| 2453 | + meta_source_hash = read_str_opt(buf) |
| 2454 | + dependencies = read_str_list(buf) |
| 2455 | + suppressed = read_str_list(buf) |
| 2456 | + assert read_tag(buf) == DICT_STR_GEN |
| 2457 | + priorities = {read_str_bare(buf): read_int(buf) for _ in range(read_int_bare(buf))} |
| 2458 | + assert read_tag(buf) == DICT_STR_GEN |
| 2459 | + dep_line_map = {read_str_bare(buf): read_int(buf) for _ in range(read_int_bare(buf))} |
| 2460 | + assert read_tag(buf) == DICT_STR_GEN |
| 2461 | + dep_hashes = {read_str_bare(buf): read_bytes(buf) for _ in range(read_int_bare(buf))} |
| 2462 | + return cls( |
| 2463 | + manager=manager, |
| 2464 | + order=order, |
| 2465 | + id=id, |
| 2466 | + path=path, |
| 2467 | + source=source, |
| 2468 | + options=manager.options.clone_for_module(id), |
| 2469 | + ignore_all=ignore_all, |
| 2470 | + caller_line=caller_line, |
| 2471 | + import_context=import_context, |
| 2472 | + meta=None, |
| 2473 | + interface_hash=interface_hash, |
| 2474 | + meta_source_hash=meta_source_hash, |
| 2475 | + dependencies=dependencies, |
| 2476 | + suppressed=suppressed, |
| 2477 | + priorities=priorities, |
| 2478 | + dep_line_map=dep_line_map, |
| 2479 | + dep_hashes=dep_hashes, |
| 2480 | + error_lines=[], |
| 2481 | + size_hint=read_int(buf), |
| 2482 | + ) |
| 2483 | + |
2394 | 2484 | def reload_meta(self) -> None: |
2395 | 2485 | """Force reload of cache meta. |
2396 | 2486 |
|
@@ -3727,11 +3817,19 @@ def find_stale_sccs( |
3727 | 3817 |
|
3728 | 3818 | def process_graph(graph: Graph, manager: BuildManager) -> None: |
3729 | 3819 | """Process everything in dependency order.""" |
| 3820 | + # Broadcast graph to workers before computing SCCs to save a bit of time. |
| 3821 | + graph_message = GraphMessage(graph=graph) |
| 3822 | + buf = WriteBuffer() |
| 3823 | + graph_message.write(buf) |
| 3824 | + graph_data = buf.getvalue() |
| 3825 | + for worker in manager.workers: |
| 3826 | + AckMessage.read(receive(worker.conn)) |
| 3827 | + worker.conn.write_bytes(graph_data) |
| 3828 | + |
3730 | 3829 | sccs = sorted_components(graph) |
3731 | 3830 | manager.log( |
3732 | 3831 | "Found %d SCCs; largest has %d nodes" % (len(sccs), max(len(scc.mod_ids) for scc in sccs)) |
3733 | 3832 | ) |
3734 | | - |
3735 | 3833 | scc_by_id = {scc.id: scc for scc in sccs} |
3736 | 3834 | manager.scc_by_id = scc_by_id |
3737 | 3835 | manager.top_order = [scc.id for scc in sccs] |
@@ -4186,6 +4284,7 @@ def deserialize_codes(errs: list[SerializedError]) -> list[ErrorTupleRaw]: |
4186 | 4284 | SCC_RESPONSE_MESSAGE: Final[Tag] = 103 |
4187 | 4285 | SOURCES_DATA_MESSAGE: Final[Tag] = 104 |
4188 | 4286 | SCCS_DATA_MESSAGE: Final[Tag] = 105 |
| 4287 | +GRAPH_MESSAGE: Final[Tag] = 106 |
4189 | 4288 |
|
4190 | 4289 |
|
4191 | 4290 | class AckMessage(IPCMessage): |
@@ -4336,3 +4435,24 @@ def write(self, buf: WriteBuffer) -> None: |
4336 | 4435 | write_str_list(buf, sorted(scc.mod_ids)) |
4337 | 4436 | write_int(buf, scc.id) |
4338 | 4437 | write_int_list(buf, sorted(scc.deps)) |
| 4438 | + |
| 4439 | + |
| 4440 | +class GraphMessage(IPCMessage): |
| 4441 | + """A message wrapping the build graph computed by the coordinator.""" |
| 4442 | + |
| 4443 | + def __init__(self, *, graph: Graph) -> None: |
| 4444 | + self.graph = graph |
| 4445 | + |
| 4446 | + @classmethod |
| 4447 | + def read(cls, buf: ReadBuffer, manager: BuildManager | None = None) -> GraphMessage: |
| 4448 | + assert manager is not None |
| 4449 | + assert read_tag(buf) == GRAPH_MESSAGE |
| 4450 | + graph = {read_str_bare(buf): State.read(buf, manager) for _ in range(read_int_bare(buf))} |
| 4451 | + return GraphMessage(graph=graph) |
| 4452 | + |
| 4453 | + def write(self, buf: WriteBuffer) -> None: |
| 4454 | + write_tag(buf, GRAPH_MESSAGE) |
| 4455 | + write_int_bare(buf, len(self.graph)) |
| 4456 | + for mod_id, state in self.graph.items(): |
| 4457 | + write_str_bare(buf, mod_id) |
| 4458 | + state.write(buf) |
0 commit comments