| ... | ... | @@ -39,6 +39,21 @@ wip_zcu: std.ArrayListUnmanaged(ZcuTask), |
| 39 | 39 | /// index into `wip_zcu` which we have reached. |
| 40 | 40 | wip_zcu_idx: usize, |
| 41 | 41 | |
| 42 | /// The sum of all `air_bytes` for all currently-queued `ZcuTask.link_func` tasks. Because |
| 43 | /// MIR bytes are approximately proportional to AIR bytes, this acts to limit the amount of |
| 44 | /// AIR and MIR which is queued for codegen and link respectively, to prevent excessive |
| 45 | /// memory usage if analysis produces AIR faster than it can be processed by codegen/link. |
| 46 | /// The cap is `max_air_bytes_in_flight`. |
| 47 | /// Guarded by `mutex`. |
| 48 | air_bytes_in_flight: u32, |
| 49 | /// If nonzero, then a call to `enqueueZcu` is blocked waiting to add a `link_func` task, but |
| 50 | /// cannot until `air_bytes_in_flight` is no greater than this value. |
| 51 | /// Guarded by `mutex`. |
| 52 | air_bytes_waiting: u32, |
| 53 | /// After setting `air_bytes_waiting`, `enqueueZcu` will wait on this condition (with `mutex`). |
| 54 | /// When `air_bytes_waiting` many bytes can be queued, this condition should be signaled. |
| 55 | air_bytes_cond: std.Thread.Condition, |
| 56 | |
| 42 | 57 | /// Guarded by `mutex`. |
| 43 | 58 | state: union(enum) { |
| 44 | 59 | /// The link thread is currently running or queued to run. |
| ... | ... | @@ -52,6 +67,11 @@ state: union(enum) { |
| 52 | 67 | wait_for_mir: *ZcuTask.LinkFunc.SharedMir, |
| 53 | 68 | }, |
| 54 | 69 | |
| 70 | /// In the worst observed case, MIR is around 50 times as large as AIR. More typically, the ratio is |
| 71 | /// around 20. Going by that 50x multiplier, and assuming we want to consume no more than 500 MiB of |
| 72 | /// memory on AIR/MIR, we see a limit of around 10 MiB of AIR in-flight. |
| 73 | const max_air_bytes_in_flight = 10 * 1024 * 1024; |
| 74 | |
| 55 | 75 | /// The initial `Queue` state, containing no tasks, expecting no prelink tasks, and with no running worker thread. |
| 56 | 76 | /// The `pending_prelink_tasks` and `queued_prelink` fields may be modified as needed before calling `start`. |
| 57 | 77 | pub const empty: Queue = .{ |
| ... | ... | @@ -64,6 +84,9 @@ pub const empty: Queue = .{ |
| 64 | 84 | .wip_zcu = .empty, |
| 65 | 85 | .wip_zcu_idx = 0, |
| 66 | 86 | .state = .finished, |
| 87 | .air_bytes_in_flight = 0, |
| 88 | .air_bytes_waiting = 0, |
| 89 | .air_bytes_cond = .{}, |
| 67 | 90 | }; |
| 68 | 91 | /// `lf` is needed to correctly deinit any pending `ZcuTask`s. |
| 69 | 92 | pub fn deinit(q: *Queue, comp: *Compilation) void { |
| ... | ... | @@ -131,6 +154,16 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error! |
| 131 | 154 | { |
| 132 | 155 | q.mutex.lock(); |
| 133 | 156 | defer q.mutex.unlock(); |
| 157 | // If this is a `link_func` task, we might need to wait for `air_bytes_in_flight` to fall. |
| 158 | if (task == .link_func) { |
| 159 | const max_in_flight = max_air_bytes_in_flight -| task.link_func.air_bytes; |
| 160 | while (q.air_bytes_in_flight > max_in_flight) { |
| 161 | q.air_bytes_waiting = task.link_func.air_bytes; |
| 162 | q.air_bytes_cond.wait(&q.mutex); |
| 163 | q.air_bytes_waiting = 0; |
| 164 | } |
| 165 | q.air_bytes_in_flight += task.link_func.air_bytes; |
| 166 | } |
| 134 | 167 | try q.queued_zcu.append(comp.gpa, task); |
| 135 | 168 | switch (q.state) { |
| 136 | 169 | .running, .wait_for_mir => return, |
| ... | ... | @@ -221,6 +254,17 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { |
| 221 | 254 | } |
| 222 | 255 | link.doZcuTask(comp, tid, task); |
| 223 | 256 | task.deinit(comp.zcu.?); |
| 257 | if (task == .link_func) { |
| 258 | // Decrease `air_bytes_in_flight`, since we've finished processing this MIR. |
| 259 | q.mutex.lock(); |
| 260 | defer q.mutex.unlock(); |
| 261 | q.air_bytes_in_flight -= task.link_func.air_bytes; |
| 262 | if (q.air_bytes_waiting != 0 and |
| 263 | q.air_bytes_in_flight <= max_air_bytes_in_flight -| q.air_bytes_waiting) |
| 264 | { |
| 265 | q.air_bytes_cond.signal(); |
| 266 | } |
| 267 | } |
| 224 | 268 | q.wip_zcu_idx += 1; |
| 225 | 269 | } |
| 226 | 270 | } |