Track Expiring GPU Credits
Company: OpenAI
Role: Machine Learning Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates event-replay and time-window accounting skills, including temporal data structures, priority-based consumption, out-of-order event processing, and resource/debt tracking within the Coding & Algorithms domain for a Machine Learning Engineer role.
Part 1: GPU Credits I - Basic Addition and Inclusive Expiration
Constraints
- 0 <= len(add_events), len(queries) <= 200000
- 1 <= amount <= 10^9
- 0 <= timestamp, duration <= 10^9
- Queries may be unsorted and may repeat
Examples
Input: ([], [0, 10])
Expected Output: [0, 0]
Explanation: Edge case: no credits were ever added.
Input: ([(5, 10, 2), (3, 4, 0), (7, 8, 5)], [3, 4, 10, 12, 14])
Expected Output: [0, 3, 12, 12, 0]
Explanation: Events arrive out of order; query results depend only on timestamps.
Input: ([(4, 2, 3)], [1, 2, 5, 6])
Expected Output: [0, 4, 4, 0]
Explanation: The credit is active at times 2, 3, 4, 5 and expires before time 6.
Input: ([(2, 0, 0), (3, 0, 2)], [0, 1, 2, 3])
Expected Output: [5, 3, 3, 0]
Explanation: Two grants start at the same time; one lasts only for a single timestamp.
Hints
- A credit active on [start, end] can be represented as +amount at start and -amount at end + 1.
- Sort query times with their original indices, then sweep once through all balance changes.
Part 2: GPU Credits II - Subtraction with Expire-Soonest Priority
Constraints
- 0 <= len(events), len(queries) <= 2000
- 1 <= amount <= 10^9
- 0 <= timestamp, duration <= 10^9
- Correctness is more important than optimization in this baseline version
Examples
Input: ([('add', 10, 0, 10), ('add', 5, 0, 2), ('sub', 8, 1)], [1, 3])
Expected Output: [7, 7]
Explanation: At time 1, the 5-credit grant expires sooner, so it is consumed first.
Input: ([('add', 5, 2, 3), ('sub', 5, 1)], [1, 2, 5, 6])
Expected Output: [None, 0, 0, None]
Explanation: The subtraction creates debt at time 1. The later add pays it off exactly, leaving an active zero-remaining grant until it expires.
Input: ([('add', 4, 1, 10), ('add', 2, 2, 10), ('sub', 7, 3)], [3, 4])
Expected Output: [None, None]
Explanation: Only 6 active credits exist at time 3, so debt remains positive afterward.
Input: ([('add', 5, 1, 1)], [0, 1, 2, 3])
Expected Output: [None, 5, 5, None]
Explanation: Inclusive expiration means the credit is still active at time 2.
Input: ([], [0])
Expected Output: [None]
Explanation: Edge case: no grants means no active credit window.
Hints
- For each query, simulate events in timestamp order rather than input order.
- Keep each grant's remaining amount, and when subtracting, always consume the active grant with the smallest expiration time first.
Part 3: GPU Credits III - Audit Trail for Grant Consumption
Constraints
- 0 <= len(events) <= 50000
- Each add event has a unique grant_id
- 1 <= amount <= 10^9
- 0 <= timestamp, duration <= 10^9
Examples
Input: ([('add', 'A', 5, 0, 5), ('add', 'B', 4, 1, 4), ('sub', 6, 2)],)
Expected Output: [(2, 6, [('A', 5), ('B', 1)], 0)]
Explanation: Both grants expire at time 5, so the earlier-starting grant A is consumed first.
Input: ([('sub', 7, 3), ('add', 'g1', 4, 1, 10), ('add', 'g2', 2, 2, 10)],)
Expected Output: [(3, 7, [('g1', 4), ('g2', 2)], 1)]
Explanation: Only 6 credits are active at time 3, so the audit row shows uncovered amount 1.
Input: ([('sub', 5, 1), ('add', 'x', 3, 2, 5), ('add', 'y', 4, 3, 5), ('sub', 2, 3)],)
Expected Output: [(1, 5, [], 5), (3, 2, [('y', 2)], 0)]
Explanation: The first subtraction creates debt. Later adds pay debt first, leaving only 2 usable credits in grant y by time 3.
Input: ([('sub', 4, 5), ('add', 'g1', 10, 5, 5)],)
Expected Output: [(5, 4, [('g1', 4)], 0)]
Explanation: Same-timestamp ordering matters: adds happen before subtracts.
Input: ([],)
Expected Output: []
Explanation: Edge case: no events means no audit rows.
Hints
- A min-heap is a natural fit for 'consume the active grant that expires soonest'.
- You only need to log what a subtraction consumed right then; debt can be carried as a single number.
Part 4: GPU Credits IV - High-Volume Offline Queries
Constraints
- 0 <= len(events), len(queries) <= 100000
- 1 <= amount <= 10^9
- 0 <= timestamp, duration <= 10^9
- An O(len(events) * len(queries)) approach will time out
Examples
Input: ([('add', 10, 0, 10), ('add', 5, 0, 2), ('sub', 8, 1)], [0, 1, 2, 3, 11])
Expected Output: [15, 7, 7, 7, None]
Explanation: This checks overlapping grants, expire-soonest subtraction, and expiry after the window ends.
Input: ([('sub', 5, 1), ('add', 2, 2, 3), ('add', 3, 3, 3)], [1, 2, 3, 4, 7])
Expected Output: [None, None, 0, 0, None]
Explanation: Later adds exactly repay earlier debt, so the balance becomes 0 while grant windows are still active.
Input: ([('sub', 4, 5), ('add', 10, 5, 5), ('add', 3, 1, 10)], [5, 6])
Expected Output: [9, 9]
Explanation: Queries at time 5 must observe both same-timestamp events, with the add processed before the subtract.
Input: ([], [0, 10])
Expected Output: [None, None]
Explanation: Edge case: no active grants at any time.
Hints
- Sort queries offline with their original indices, and sweep forward in time only once.
- You may want one structure for grant-window expiration and another for choosing the next spendable grant.