data !== []) { $this->flushSelectedData(); return; } if ($this->lastEpochData === [] || $this->shouldBroadcastEpoch) { return; } $this->waitForBroadcastInterval(); event(new PerceptronTrainingIteration($this->lastEpochData, $this->sessionId, $this->trainingId)); $this->lastBroadcastAt = microtime(true); $this->lastEpochData = []; } public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void { $newData = [ 'epoch' => $epoch, 'exampleIndex' => $exampleIndex, 'error' => $error, 'weights' => $synaptic_weights, ]; if ($this->activeEpoch !== $epoch) { $this->flushSelectedData(); $this->lastEpochData = []; $this->activeEpoch = $epoch; $this->shouldBroadcastEpoch = $epoch === 1 || $epoch % $this->epochInterval === 0; } if (! $this->shouldBroadcastEpoch) { $this->lastEpochData[] = $newData; return; } $this->data[] = $newData; if ($this->payloadExceedsLimit() || count($this->data) >= config('perceptron.broadcast_iteration_size')) { $this->flushSelectedData(); } } private function flushSelectedData(): void { if ($this->data === []) { return; } $this->waitForBroadcastInterval(); event(new PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId)); $this->lastBroadcastAt = microtime(true); $this->data = []; } private function payloadExceedsLimit(): bool { return strlen(json_encode([ 'iterations' => PerceptronTrainingIteration::normalizeForJson($this->data), 'trainingId' => $this->trainingId, ], JSON_THROW_ON_ERROR)) > config('broadcasting.broadcast_max_payload_size'); } private function waitForBroadcastInterval(): void { if ($this->lastBroadcastAt === null) { return; } $minimumInterval = config('perceptron.broadcast_minimum_interval_ms') / 1000; $remainingInterval = $minimumInterval - (microtime(true) - $this->lastBroadcastAt); if ($remainingInterval > 0) { usleep((int) ceil($remainingInterval * 1_000_000)); } } }