       // Core ML can't keep up with the camera.
       semaphore.wait()
 
+      // For better throughput, we want to schedule multiple prediction requests
+      // in parallel. These need to be separate instances, and inflightBuffer is
+      // the index of the current request.
+      let inflightIndex = inflightBuffer
+      inflightBuffer += 1
+      if inflightBuffer >= ViewController.maxInflightBuffers {
+        inflightBuffer = 0
+      }
+
       if useVision {
         // This method should always be called from the same thread!
         // Ain't nobody likes race conditions and crashes.
-        self.predictUsingVision(pixelBuffer: pixelBuffer)
+        self.predictUsingVision(pixelBuffer: pixelBuffer, inflightIndex: inflightIndex)
       } else {
         // For better throughput, perform the prediction on a concurrent
         // background queue instead of on the serial VideoCapture queue.
         DispatchQueue.global().async {
-          self.predict(pixelBuffer: pixelBuffer)
+          self.predict(pixelBuffer: pixelBuffer, inflightIndex: inflightIndex)
         }
       }
     }

  var colors: [UIColor] = []

  let ciContext = CIContext()
  var resizedPixelBuffer: CVPixelBuffer?

  var framesDone = 0
  var frameCapturingStartTime = CACurrentMediaTime()

  }

  func setUpCoreImage() {
    // Since we might be running several requests in parallel, we also need
    // to do the resizing in different pixel buffers or we might overwrite a
    // pixel buffer that's already in use.
    for _ in 0..<YOLO.maxBoundingBoxes {
      var resizedPixelBuffer: CVPixelBuffer?
      let status = CVPixelBufferCreate(nil, YOLO.inputWidth, YOLO.inputHeight,
                                       kCVPixelFormatType_32BGRA, nil,
                                       &resizedPixelBuffer)

      if status != kCVReturnSuccess {
        print("Error: could not create resized pixel buffer", status)
      }
      resizedPixelBuffers.append(resizedPixelBuffer)
    }
  }

