Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified bin/beeline.cmd
100644 → 100755
Empty file.
Empty file modified bin/compute-classpath.cmd
100644 → 100755
Empty file.
Empty file modified bin/load-spark-env.sh
100644 → 100755
Empty file.
Empty file modified bin/pyspark.cmd
100644 → 100755
Empty file.
Empty file modified bin/pyspark2.cmd
100644 → 100755
Empty file.
Empty file modified bin/run-example.cmd
100644 → 100755
Empty file.
Empty file modified bin/run-example2.cmd
100644 → 100755
Empty file.
Empty file modified bin/spark-class.cmd
100644 → 100755
Empty file.
Empty file modified bin/spark-class2.cmd
100644 → 100755
Empty file.
Empty file modified bin/spark-shell2.cmd
100644 → 100755
Empty file.
Empty file modified bin/spark-submit.cmd
100644 → 100755
Empty file.
Empty file modified bin/spark-submit2.cmd
100644 → 100755
Empty file.
Empty file modified bin/windows-utils.cmd
100644 → 100755
Empty file.
29 changes: 21 additions & 8 deletions core/src/main/scala/org/apache/spark/Accumulators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import java.lang.ThreadLocal

import scala.collection.generic.Growable
import scala.collection.mutable.Map
import scala.ref.WeakReference
import scala.reflect.ClassTag

import org.apache.spark.serializer.JavaSerializer
Expand Down Expand Up @@ -280,10 +281,12 @@ object AccumulatorParam {
// TODO: The multi-thread support in accumulators is kind of lame; check
// if there's a more intuitive way of doing it right
private[spark] object Accumulators {
// TODO: Use soft references? => need to make readObject work properly then
val originals = Map[Long, Accumulable[_, _]]()
val localAccums = new ThreadLocal[Map[Long, Accumulable[_, _]]]() {
override protected def initialValue() = Map[Long, Accumulable[_, _]]()
// Store a WeakReference instead of a StrongReference because this way accumulators can be
// appropriately garbage collected during long-running jobs and release memory
type WeakAcc = WeakReference[Accumulable[_, _]]
val originals = Map[Long, WeakAcc]()
val localAccums = new ThreadLocal[Map[Long, WeakAcc]]() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guava MapMaper supports weakValues; not sure if we want to use that here, since it's not super Scala-friendly (e.g. returns nulls, etc): http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/MapMaker.html

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Josh - are you suggesting to replace this snippet with a MapMaker just to simplify the initialization code? I believe the usage of either object would be the same - do you see a specific advantage to trying to use the MapMaker?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just leave this as-is; I don't think MapMaker will buy us much now that I think about it.

override protected def initialValue() = Map[Long, WeakAcc]()
}
var lastId: Long = 0

Expand All @@ -294,9 +297,9 @@ private[spark] object Accumulators {

def register(a: Accumulable[_, _], original: Boolean): Unit = synchronized {
if (original) {
originals(a.id) = a
originals(a.id) = new WeakAcc(a)
} else {
localAccums.get()(a.id) = a
localAccums.get()(a.id) = new WeakAcc(a)
}
}

Expand All @@ -311,7 +314,12 @@ private[spark] object Accumulators {
def values: Map[Long, Any] = synchronized {
val ret = Map[Long, Any]()
for ((id, accum) <- localAccums.get) {
ret(id) = accum.localValue
// Since we are now storing weak references, we must check whether the underlying data
// is valid.
ret(id) = accum.get match {
case Some(values) => values.localValue
case None => None // TODO Is this the right thing to return here?
}
}
return ret
}
Expand All @@ -320,7 +328,12 @@ private[spark] object Accumulators {
def add(values: Map[Long, Any]): Unit = synchronized {
for ((id, value) <- values) {
if (originals.contains(id)) {
originals(id).asInstanceOf[Accumulable[Any, Any]] ++= value
// Since we are now storing weak references, we must check whether the underlying data
// is valid.
originals(id).get match {
case Some(accum) => accum.asInstanceOf[Accumulable[Any, Any]] ++= value
case None => // Do nothing

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an error-case? This seems to mean that you've received an accumulator update for an accumulator that's been garbage-collected, which seems like it should be an error.

}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/org/apache/spark/rdd/RDD.scala
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ abstract class RDD[T: ClassTag](
new PartitionwiseSampledRDD[T, T](this, new BernoulliSampler[T](fraction), true, seed)
}
}

/**
* Randomly splits this RDD with the provided weights.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -889,8 +889,16 @@ class DAGScheduler(
if (event.accumUpdates != null) {
try {
Accumulators.add(event.accumUpdates)

event.accumUpdates.foreach { case (id, partialValue) =>
val acc = Accumulators.originals(id).asInstanceOf[Accumulable[Any, Any]]
// In this instance, although the reference in Accumulators.originals is a WeakRef,
// it's guaranteed to exist since the event.accumUpdates Map exists

val acc = Accumulators.originals(id).get match {
case Some(accum) => accum.asInstanceOf[Accumulable[Any, Any]]
case None => throw new NullPointerException("Non-existent reference to Accumulator")
}

// To avoid UI cruft, ignore cases where value wasn't updated
if (acc.name.isDefined && partialValue != acc.zero) {
val name = acc.name.get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,14 @@ class DAGSchedulerSuite extends TestKit(ActorSystem("DAGSchedulerSuite")) with F
completeWithAccumulator(accum.id, taskSets(0), Seq((Success, 42)))
completeWithAccumulator(accum.id, taskSets(0), Seq((Success, 42)))
assert(results === Map(0 -> 42))
assert(Accumulators.originals(accum.id).value === 1)

val accVal = Accumulators.originals(accum.id).get match {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing a match here, you could probably just call get, since that wil stilll cause the test to fail if the reference expired, but will give a more useful error message.

case Some(accOpt) => accOpt.value
case None => 0
}

assert(accVal === 1)

assertDataStructuresEmpty
}

Expand Down