Я пишу проект во Flink, который включает в себя потоковое множество точек запроса над пакетными данными и выполнение полного последовательного сканирования, чтобы найти ближайших соседей. То, что должно быть простой сортировкой для одного значения Float, вызывает нарушение общей ошибки контракта. Основной метод определяется как:
object StreamingDeCP{
var points: Vector[Point] = _
def main(args: Array[String]): Unit = {
val queryPointsVec: Vector[Point] = ... // Read from file
val pointsVec: Vector[Point] = ... // Read from file
val streamEnv: StreamExecutionEnvironment =
StreamExecutionEnvironment.getExecutionEnvironment
val queryPoints = streamEnv.fromCollection(queryPointsVec)
points = pointsVec
queryPoints.map(new StreamingSequentialScan)
streamEnv.execute("StreamingDeCP")
}
final class StreamingSequentialScan
extends MapFunction[Point, (Point, Vector[Point])] {
def map(queryPoint: Point): (Point, Vector[Point]) = {
val nn = points
.map{ _.eucDist(queryPoint) }
.sorted
(queryPoint, nn)
}
}
}
Класс Point
и объект-компаньон:
case class Point(pointID: Long,
descriptor: Vector[Float]) extends Serializable {
var distance: Float = Float.MaxValue
def eucDist(that: Point): Point = {
// Simple arithmetic to calculate and set the distance variable
}
}
object Point{
implicit def orderByDistance[A <: Point]: Ordering[A] =
Ordering.by(_.distance)
}
Вот несколько заметок о том, что я пробовал, чтобы определить причину:
distance
находится между Float.MaxValue и Float.MinValue и не существует значений отрицательного нуляdistance
(мой вариант использования позволяет это, но я думал, что проверю его на всякий случай)Point
вместо использования implicitspointID
вместо distance
, который работает, но бесполезен для контекста этой проблемы. Я также отметил, что выполнение одного и того же кода не всегда достоверно воспроизводит ошибку. Я читаю Vector[Points]
полностью детерминированным способом, поэтому единственной возможной причиной такого поведения должен быть планировщик Flink или некоторый расчет состояния в методе сортировки.
Другие сообщения в одной и той же теме, по-видимому, связаны с пропущенным сценарием в пользовательском компараторе, но это должна быть простая операция сортировки по одному значению Float, поэтому я не знаю, что может вызвать проблему.
Я не знаком с Flink, но у меня нет причин предполагать, что он выполнит каждую неловко параллельную задачу MapFunction
в последовательном однопоточном режиме.
Поскольку ваша Point
содержит var
s, а те var
мутируются в методе map
MapFunction
, код должен завершиться неудачей, если "Метод сравнения нарушает общий контракт" -exception всякий раз, когда MapFunction
выполняется с параллелизмом != 1
.
Чтобы избежать каких-либо побочных эффектов внутри функции map
, вы можете изменить код следующим образом:
var
из main
, сделайте points
неизменным val
.var
из Point
Внедрить метод
def eucDist(other: Point): Double
который просто вычисляет расстояние до другой точки (без каких-либо изменений).
Использовать sortBy
:
val nn = points.sortBy(_.eucDist(queryPoint))
В качестве альтернативы, если вы хотите избежать повторного вычисления евклидова расстояния несколько раз во время сортировки, предварительно скопируйте расстояния один раз, сортируйте, а затем отбросьте расстояния:
val nn = points.map(p => (p, p.eucDist(queryPoint))).sortBy(_._2).map(_._1)
var
S в случае класса, почемуeucDist
возвращаетPoint
вместо скалярного значения, а почему бы вам не просто.sortBy(_.eucDist(queryPoint))
?.sorted
я написал, и предложенным.sortBy
методом.sortBy
.