2013-11-23 3 views
1

У меня довольно сложный проект, который в значительной степени использует многопоточность Java. В ответе на один из моих предыдущих вопросов я описал уродливый хак, который должен преодолеть присущую невозможности повторять итерацию по Java ConcurrentHashMap параллельно. Хотя это работает, мне не нравятся уродливые хаки, и у меня было много проблем с попыткой представить предлагаемое доказательство концепции в реальной системе. Пытаясь найти альтернативное решение, я столкнулся с ParHashMap от Scala, который утверждает, что реализует метод foreach, который, похоже, работает параллельно. Прежде чем я начну изучать новый язык для реализации единой функции, я хотел бы задать следующие вопросы:Использование Scala ParHashMap в проекте Java вместо ConcurrentHashMap

1) Есть foreach метод Scala's ParHashMap масштабируемый?

2) Простой и понятный код Java от Scala и наоборот? Напомню, что код является параллельным и использует generics.

3) Будет ли штраф за исполнение для переключения части кода на Scala?

Для справки, это мой предыдущий вопрос о параллельной итерации ConcurrentHashMap:

Scalable way to access every element of ConcurrentHashMap<Element, Boolean> exactly once

EDIT

Я осуществил доказательство концепции, в вероятно, очень не-идиоматических Scala, но он работает отлично. AFAIK НЕВОЗМОЖНО реализовать соответствующее решение в Java с учетом текущего состояния его стандартной библиотеки и любых доступных сторонних библиотек.

import scala.collection.parallel.mutable.ParHashMap 

class Node(value: Int, id: Int){ 
    var v = value 
    var i = id 
    override def toString(): String = v toString 
} 

object testParHashMap{ 
    def visit(entry: Tuple2[Int, Node]){ 
     entry._2.v += 1 
    } 
    def main(args: Array[String]){ 
     val hm = new ParHashMap[Int, Node]() 
     for (i <- 1 to 10){ 
      var node = new Node(0, i) 
      hm.put(node.i, node) 
     } 

     println("========== BEFORE ==========") 
     hm.foreach{println} 

     hm.foreach{visit} 

     println("========== AFTER ==========") 
     hm.foreach{println} 

    } 
} 

ответ

1

Я пришел к этому с некоторыми оговорками:

  • Хотя я могу сделать некоторые вещи, я считаю себя относительно новым для Scala.
  • Я только что прочитал, но не использовал par материал, описанный here.
  • Я никогда не пытался добиться того, чего вы пытаетесь достичь.

Если вы по-прежнему заботитесь о том, что я должен сказать, читайте дальше.

Во-первых, вот академический paper, описывающий работу параллельных коллекций.

На ваши вопросы.

1) Когда дело доходит до многопоточности, Scala делает жизнь намного проще, чем Java. Абстракции просто потрясающие. ParHashMap, который вы получаете от звонка par, будет распространять работу на несколько потоков. Я не могу сказать, как это будет масштабироваться для вас без лучшего понимания вашей машины, конфигурации и использования, но сделано правильно (особенно в отношении побочных эффектов), это будет по крайней мере так же хорошо, как реализация Java. Тем не менее, вы также можете посмотреть на Akka, чтобы иметь больше контроля над всем. Похоже, это может быть более подходящим для вашего случая использования, чем просто ParHashMap.

2) Как правило, легко конвертировать между коллекциями Java и Scala, используя JavaConverters и методы asJava и asScala. Я бы предположил, что, убедившись, что публичный API для вашего метода вызывает «выглядит Java», поскольку Java является наименее общим знаменателем. Кроме того, в этом случае Scala представляет собой детальную реализацию, и вы никогда не захотите ее протекать. Так что держите абстракцию на уровне Java.

3) Я бы предположил, что на Scala будет достигнуто усиление производительности - во время выполнения. Тем не менее, вы найдете гораздо медленнее время компиляции (которое можно обойти. Ish). Этот переполнение стека post автор Scala старый, но по-прежнему актуальный.

Надеюсь, что это поможет. Это проблема, с которой вы столкнулись.

+0

Спасибо за отличный ответ. Я не знал о JavaConverters, но они звучат довольно удивительно. Большое спасибо за то, что указали мне на исследовательскую статью, я буду внимательно ее читать. На данном этапе мне кажется, что я могу ограничить свой код scala всего несколькими слоями, выставить Java-API и решить проблему к полуночи :). Хуже всего будет изучать совершенно новый синтаксис, но, надеюсь, Scala будет полезен в будущем! Большое спасибо! –

+0

Рад помочь. Удачи с вашим проектом! – Vidya

+0

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

0

Поскольку Scala компилируется в тот же байт-код, что и Java, то сделать то же самое на обоих языках вполне возможно, независимо от задачи. Однако есть некоторые вещи, которые легче решить в Scala, но если это стоит изучать новый язык, это другой вопрос. Тем более, что Java 8 будет включать в себя именно то, что вы просите: простое параллельное выполнение функций в списках.

Но даже сейчас вы можете сделать это на Java, вам просто нужно написать то, что Scala уже имеет по своему усмотрению.

final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 
//... 
final Entry<String, String>[] elements = (Entry<String, String>[]) myMap.entrySet().toArray(); 
final AtomicInteger index = new AtomicInteger(elements.length); 

for (int i = Runtime.getRuntime().availableProcessors(); i > 0; --i) { 
    executor.submit(new Runnable() { 

    public void run() { 
     int myIndex; 
     while ((myIndex = index.decrementAndGet()) >= 0) { 
     process(elements[myIndex]); 
     } 
    } 
    }); 
} 

Трюк состоит в том, чтобы вытащить эти элементы во временный массив, чтобы потоки могли извлекать элементы поточно-безопасным способом. Очевидно, что здесь требуется некоторое кэширование вместо повторного создания Runnables и массива, потому что для создания Runnable может потребоваться больше времени, чем фактическая задача.

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

Я не знаю, как работает Scala, но учитывая тот факт, что она должна работать на одной и той же JVM, она будет делать что-то подобное в фоновом режиме, она просто легко доступна в стандартной библиотеке.

Смежные вопросы