Реализация очереди приоритетов Java - локальность памяти - PullRequest
3 голосов
/ 05 апреля 2011

Я пытаюсь реализовать эффективную очередь приоритетов в Java. Я получил хорошую реализацию двоичной кучи, но она не обладает идеальной производительностью кэша. Для этого я начал изучать макет Ван Эмде Боаса в двоичной куче, что привело меня к «заблокированной» версии двоичной кучи, где хитрость заключается в вычислении дочерних и родительских индексов.

Хотя я смог это сделать, поведение кеша (и время работы) стало хуже. Я думаю, что проблема в том, что локальность ссылки , вероятно, не достигается, поскольку это Java - Я не уверен, если использование массива объектов действительно делает объекты смежными в памяти в Java , кто-нибудь может подтвердить это, пожалуйста?

Также мне очень хотелось бы знать, какие структуры данных использует нативный PriorityQueue для Java, если таковой будет известен.

Ответы [ 3 ]

2 голосов
/ 05 апреля 2011

В общем, нет хорошего способа заставить ваши объекты в очереди занимать непрерывный кусок памяти. Однако есть некоторые методы, которые подходят для особых случаев.

На высоком уровне методы включают использование байтовых массивов и «сериализацию» данных в массив и из него. Это на самом деле довольно эффективно, если вы храните очень простые объекты. Например, если вы храните набор 2D точек + весов, вы можете просто написать байтовый эквивалент веса, координаты x, координаты y.

Проблема на данный момент, конечно, заключается в распределении экземпляров при просмотре / прослушивании. Вы можете избежать этого, используя обратный вызов.

Обратите внимание, что даже в тех случаях, когда сам сохраняемый объект является сложным, использование метода, подобного этому, где вы сохраняете один массив для весов и отдельный массив ссылок для реальных объектов, позволяет вам избегать следования ссылкам на объекты до абсолютно необходимо.

Возвращаясь к подходу для хранения простого неизменяемого типа значения, вот неполный набросок того, что вы могли бы сделать:

abstract class LowLevelPQ<T> {

  interface DataHandler<R, T> {
    R handle(byte[] source, int startLoc);
  }

  LowLevelPQ(int entryByteSize) { ... }
  abstract encode(T element, byte[] target, int startLoc);
  abstract T decode(byte[] source, int startLoc);
  abstract int compare(byte[] data, int startLoc1, int startLoc2);

  abstract <R> R peek(DataHandler<R, T> handler) { ... }
  abstract <R> R pop(DataHandler<R, T> handler) { ... }
}

class WeightedPoint {
  WeightedPoint(int weight, double x, double y) { ... }
  double weight() { ... }
  double x() { ... }
  ...
}

class WeightedPointPQ extends LowLevelPQ<WeightedPoint> {
  WeightedPointPQ() {
    super(4 + 8 + 8); // int,double,double
  }

  int compare(byte[] data, int startLoc1, int startLoc2) {
    // relies on Java's big endian-ness
    for (int i = 0; i < 4; ++i) {
      int v1 = 0xFF & (int) data[startLoc1];
      int v2 = 0xFF & (int) data[startLoc2];
      if (v1 < v2) { return -1; }
      if (v1 > v2) { return  1; }
    }
    return 0;
  }

  ...
}
1 голос
/ 05 апреля 2011

Я думаю, что здесь происходит ФУД.В принципе, немыслимо, чтобы любая реализация массивов не использовала непрерывную память.И то, как этот термин используется в спецификации JVM при описании формата файла .class, делает совершенно ясным, что никакая другая реализация не рассматривается.Javadoc, реализованный через массив.

1 голос
/ 05 апреля 2011

Не думаю, что так будет. Помните, что «массивы объектов» - это не массивы объектов, а массивы ссылок на объекты (в отличие от массивов примитивов, которые действительно являются массивами примитивов). Я ожидал бы, что ссылки на объекты являются непрерывными в памяти, но, поскольку вы можете заставить эти ссылки ссылаться на любые объекты, когда захотите, я сомневаюсь, что есть какая-либо гарантия, что объекты, на которые ссылается массив ссылок, будут смежными в памяти.

Что бы это ни стоило, в разделе JLS о массивах ничего не говорится о каких-либо гарантиях смежности.

...