Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • lamp/cs206
  • bwermeil/cs206-2020
  • zabifade/cs206-2020
  • cauderan/cs206-2020
  • malonga/cs206-2020
  • dumoncel/cs206
  • bounekhe/cs206
  • bergerault/cs206
  • flealsan/cs206
  • hsu/cs206
  • mouchel/cs206
  • vebraun/cs206
  • vcanard/cs206
  • ybelghmi/cs206
  • belghmi/cs206
  • bousbina/cs206
  • waked/cs206
  • gtagemou/cs206
  • arahmoun/cs206
  • elhachem/cs206
  • benrahha/cs206
  • benslima/cs206
22 results
Show changes
Showing
with 0 additions and 662 deletions
// Used for Coursera submission (StudentPlugin)
// libraryDependencies += "org.scalaj" %% "scalaj-http" % "2.4.2"
// libraryDependencies += "com.typesafe.play" %% "play-json" % "2.7.4"
// Used for Base64 (StudentPlugin)
libraryDependencies += "commons-codec" % "commons-codec" % "1.10"
// addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.28")
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.5.3")
package m7
import java.util.concurrent._
import scala.util.DynamicVariable
trait M7 extends Lib {
class DLLCombinerImplementation(chunk_size: Int = 3) extends DLLCombiner(chunk_size){
// Computes every other Integer element of data array, starting from the second, up to the middle
def task1(data: Array[Int]): ForkJoinTask[Unit] = task {
var current = head
if(current != null) {
var i_from = 1
var i_to = 1
if(i_from >= current.cnt) {
current = current.next
if(current != null) {
i_from = 0
}
else i_to = cnt/2 // to stop the loop
}
copyForward(data, current, i_from, i_to, cnt/2)
}
}
// Computes every other Integer element of data array, starting from the first (index 0), up to the middle
def task2(data: Array[Int]): ForkJoinTask[Unit] = task {
var current = head
if(current != null) {
var i_from = 0
var i_to = 0
copyForward(data, current, i_from, i_to, cnt/2)
}
}
// Computes every other Integer element of data array, starting from the last, up to the middle
def task3(data: Array[Int]): ForkJoinTask[Unit] = task {
var current = last
if( current != null) {
var i_from = current.cnt - 1
var i_to = cnt - 1
copyBackward(data, current, i_from, i_to, cnt/2)
}
}
// Computes every other Integer element of data array, starting from the second to last, up to the middle
// This is executed on the current thread.
def task4(data: Array[Int]): Unit = {
var current = last
if( current != null) {
var i_to = cnt - 2
var i_from = current.cnt - 2
if(i_from < 0) {
current = current.previous
if(current != null) {
i_from = current.cnt - 1
}
else i_to = cnt/2 - 1 // to stop the loop
}
copyBackward(data, current, i_from, i_to, cnt/2)
}
}
def result(): Array[Int] = {
val data = new Array[Int](cnt)
val t1 = task1(data)
val t2 = task2(data)
val t3 = task3(data)
task4(data)
t1.join()
t2.join()
t3.join()
data
}
private def copyBackward(data: Array[Int], curr: Node, from: Int, to: Int, limit: Int) = {
var current = curr
var i_from = from
var i_to = to
while (i_to >= limit) {
try{
data(i_to) = current.value(i_from)
i_to -= 2
i_from -= 2
if(i_from == -1) {
current = current.previous
i_from = current.cnt - 1
}
else if(i_from == -2) {
current = current.previous
i_from = current.cnt - 2
if(current.cnt == 1) {
current = current.previous
i_from = current.cnt - 1
}
}
}
catch{
case e: Exception =>
}
}
}
private def copyForward(data: Array[Int], curr: Node, from: Int, to: Int, limit: Int) = {
var current = curr
var i_from = from
var i_to = to
while (i_to < limit) {
try {
data(i_to) = current.value(i_from)
i_to += 2
i_from += 2
if(i_from == current.cnt){
current = current.next
i_from = 0
}
else if(i_from > current.cnt) {
current = current.next
i_from = 1
if(current.cnt == 1) {
current = current.next
i_from = 0
}
}
}
catch{
case e: Exception =>
}
}
}
}
}
package m7
import java.util.concurrent._
import scala.util.DynamicVariable
trait Lib {
class Node(val size: Int) {
var value: Array[Int] = new Array(size)
var next: Node = null
var previous: Node = null
var cnt = 0
def add(v: Int) = {
value(cnt) = v
cnt += 1
}
}
// Simplified Combiner interface
// Implements methods += and combine
// Abstract methods should be implemented in subclasses
abstract class DLLCombiner(val chunk_size: Int) {
var head: Node = null
var last: Node = null
var cnt: Int = 0
var chunks: Int = 0
// Adds an Integer to the last node of this array combiner. If the last node is full, allocates a new node.
def +=(elem: Int): Unit = {
if(cnt % chunk_size == 0) {
chunks = chunks + 1
val node = new Node(chunk_size)
if (cnt == 0) {
head = node
last = node
}
else {
last.next = node
node.previous = last
last = node
}
}
last.add(elem)
cnt += 1
}
// Combines this array combiner and another given combiner in constant O(1) complexity.
def combine(that: DLLCombiner): DLLCombiner = {
assert(this.chunk_size == that.chunk_size)
if (this.cnt == 0) {
this.head = that.head
this.last = that.last
this.cnt = that.cnt
this.chunks = that.chunks
this
}
else if (that.cnt == 0)
this
else {
this.last.next = that.head
that.head.previous = this.last
this.cnt = this.cnt + that.cnt
this.chunks = this.chunks + that.chunks
this.last = that.last
this
}
}
def task1(data: Array[Int]): ForkJoinTask[Unit]
def task2(data: Array[Int]): ForkJoinTask[Unit]
def task3(data: Array[Int]): ForkJoinTask[Unit]
def task4(data: Array[Int]): Unit
def result(): Array[Int]
}
def task[T](body: => T): ForkJoinTask[T]
}
\ No newline at end of file
package m7
import java.util.concurrent._
import scala.util.DynamicVariable
class M7Suite extends AbstractM7Suite {
// (70+) 30 / 500 points for correct implementation, don't check parallelism
test("[Correctness] fetch result - simple combiners (5pts)") {
assertCorrectnessSimple()
}
test("[Correctness] fetch result - small combiners (5pts)") {
assertCorrectnessBasic()
}
test("[Correctness] fetch result - small combiners after combining (10pts)") {
assertCorrectnessCombined()
}
test("[Correctness] fetch result - large combiners (10pts)") {
assertCorrectnessLarge()
}
def assertCorrectnessSimple() = {
simpleCombiners.foreach(elem => assert(compare(elem._1, elem._2)))
}
def assertCorrectnessBasic() = {
basicCombiners.foreach(elem => assert(compare(elem._1, elem._2)))
}
def assertCorrectnessCombined() = {
combinedCombiners.foreach(elem => assert(compare(elem._1, elem._2)))
}
def assertCorrectnessLarge() = {
largeCombiners.foreach(elem => assert(compare(elem._1, elem._2)))
}
// (70+30+) 50 / 500 points for correct parallel implementation, don't check if it's exactly 1/4 of the array per task
private var count = 0
private val expected = 3
override def task[T](body: => T): ForkJoinTask[T] = {
count += 1
scheduler.value.schedule(body)
}
test("[TaskCount] number of newly created tasks should be 3 (5pts)") {
assertTaskCountSimple()
}
test("[TaskCount] fetch result and check parallel - simple combiners (5pts)") {
assertTaskCountSimple()
assertCorrectnessSimple()
}
test("[TaskCount] fetch result and check parallel - small combiners (10pts)") {
assertTaskCountSimple()
assertCorrectnessBasic()
}
test("[TaskCount] fetch result and check parallel - small combiners after combining (15pts)") {
assertTaskCountSimple()
assertCorrectnessCombined()
}
test("[TaskCount] fetch result and check parallel - large combiners (15pts)") {
assertTaskCountSimple()
assertCorrectnessLarge()
}
def assertTaskCountSimple(): Unit = {
simpleCombiners.foreach(elem => assertTaskCount(elem._1, elem._2))
}
def assertTaskCount(combiner: DLLCombinerTest, array: Array[Int]): Unit = {
try {
count = 0
build(combiner, array)
combiner.result()
assertEquals(count, expected, {
s"ERROR: Expected $expected instead of $count calls to `task(...)`"
})
} finally {
count = 0
}
}
//(70+30+50+) 200 / 500 points for correct parallel implementation, exactly 1/4 of the array per task
test("[TaskFairness] each task should compute 1/4 of the result (50pts)") {
assertTaskFairness(simpleCombiners.unzip._1)
}
test("[TaskFairness] each task should correctly compute 1/4 of the result - simple combiners (20pts)") {
assertTaskFairness(simpleCombiners.unzip._1)
assertCorrectnessSimple()
}
test("[TaskFairness] each task should correctly compute 1/4 of the result - small combiners (30pts)") {
assertTaskFairness(basicCombiners.unzip._1)
assertCorrectnessBasic()
}
test("[TaskFairness] each task should correctly compute 1/4 of the result - small combiners after combining (50pts)") {
assertTaskFairness(combinedCombiners.unzip._1)
assertCorrectnessCombined()
}
test("[TaskFairness] each task should correctly compute 1/4 of the result - large combiners (50pts)") {
assertTaskFairness(largeCombiners.unzip._1)
assertCorrectnessLarge()
}
def assertTaskFairness(combiners: List[DLLCombinerTest]): Unit = {
def assertNewTaskFairness(combiner: DLLCombinerTest, task: ForkJoinTask[Unit], data: Array[Int]) = {
var count = 0
var expected = combiner.cnt / 4
task.join
count = data.count(elem => elem != 0)
assert((count - expected).abs <= 1)
}
def assertMainTaskFairness(combiner: DLLCombinerTest, task: Unit, data: Array[Int]) = {
var count = 0
var expected = combiner.cnt / 4
count = data.count(elem => elem != 0)
assert((count - expected).abs <= 1)
}
combiners.foreach { elem =>
var data = Array.fill(elem.cnt)(0)
assertNewTaskFairness(elem, elem.task1(data), data)
data = Array.fill(elem.cnt)(0)
assertNewTaskFairness(elem, elem.task2(data), data)
data = Array.fill(elem.cnt)(0)
assertNewTaskFairness(elem, elem.task3(data), data)
data = Array.fill(elem.cnt)(0)
assertMainTaskFairness(elem, elem.task4(data), data)
}
}
//(70+30+50+200+) 150 / 500 points for correct parallel implementation, exactly 1/4 of the array per task, exactly the specified quarter
test("[TaskPrecision] each task should compute specified 1/4 of the result - simple combiners (20pts)") {
assertTaskPrecision(simpleCombiners)
}
test("[TaskPrecision] each task should compute specified 1/4 of the result - small combiners (30pts)") {
assertTaskPrecision(basicCombiners)
}
test("[TaskPrecision] each task should compute specified 1/4 of the result - small combiners after combining (50pts)") {
assertTaskPrecision(combinedCombiners)
}
test("[TaskPrecision] each task should compute specified 1/4 of the result - large combiners (50pts)") {
assertTaskPrecision(largeCombiners)
}
def assertTaskPrecision(combiners: List[(DLLCombinerTest, Array[Int])]): Unit = {
combiners.foreach { elem =>
var data = Array.fill(elem._1.cnt)(0)
var ref = Array.fill(elem._1.cnt)(0)
val task1 = elem._1.task1(data)
task1.join
Range(0, elem._1.cnt).foreach(i => (if (i < elem._1.cnt / 2 - 1 && i % 2 == 1) ref(i) = elem._2(i)))
assert(Range(0, elem._1.cnt / 2 - 1).forall(i => data(i) == ref(i)))
data = Array.fill(elem._1.cnt)(0)
ref = Array.fill(elem._1.cnt)(0)
val task2 = elem._1.task2(data)
task2.join
Range(0, elem._1.cnt).foreach(i => (if (i < elem._1.cnt / 2 - 1 && i % 2 == 0) ref(i) = elem._2(i)))
assert(Range(0, elem._1.cnt / 2 - 1).forall(i => data(i) == ref(i)))
data = Array.fill(elem._1.cnt)(0)
ref = Array.fill(elem._1.cnt)(0)
val task3 = elem._1.task3(data)
task3.join
Range(0, elem._1.cnt).foreach(i => (if (i > elem._1.cnt / 2 + 1 && i % 2 != elem._1.cnt % 2) ref(i) = elem._2(i)))
assert(Range(elem._1.cnt / 2 + 2, elem._1.cnt).forall(i => data(i) == ref(i)))
data = Array.fill(elem._1.cnt)(0)
ref = Array.fill(elem._1.cnt)(0)
val task4 = elem._1.task4(data)
Range(0, elem._1.cnt).foreach(i => (if (i > elem._1.cnt / 2 + 1 && i % 2 == elem._1.cnt % 2) ref(i) = elem._2(i)))
assert(Range(elem._1.cnt / 2 + 2, elem._1.cnt).forall(i => data(i) == ref(i)))
}
}
test("[Public] fetch simple result without combining (5pts)") {
val combiner1 = DLLCombinerTest(2)
combiner1 += 7
combiner1 += 2
combiner1 += 3
combiner1 += 8
combiner1 += 1
combiner1 += 2
combiner1 += 3
combiner1 += 8
val result = combiner1.result()
val array = Array(7, 2, 3, 8, 1, 2, 3, 8)
assert(Range(0,array.size).forall(i => array(i) == result(i)))
}
test("[Public] fetch result without combining (5pts)") {
val combiner1 = DLLCombinerTest(2)
combiner1 += 7
combiner1 += 2
combiner1 += 3
combiner1 += 8
combiner1 += 1
val result = combiner1.result()
val array = Array(7, 2, 3, 8, 1)
assert(Range(0,array.size).forall(i => array(i) == result(i)))
}
test("[Public] fetch result after simple combining (5pts)") {
val combiner1 = DLLCombinerTest(2)
combiner1 += 7
combiner1 += 2
val combiner2 = DLLCombinerTest(2)
combiner2 += 3
combiner2 += 8
val combiner3 = DLLCombinerTest(2)
combiner2 += 1
combiner2 += 9
val combiner4 = DLLCombinerTest(2)
combiner2 += 3
combiner2 += 2
val result = combiner1.combine(combiner2).combine(combiner3).combine(combiner4).result()
val array = Array(7, 2, 3, 8, 1, 9, 3, 2)
assert(Range(0,array.size).forall(i => array(i) == result(i)))
}
test("[Public] fetch result - empty combiner (20pts)") {
val combiner1 = DLLCombinerTest(2)
val result = combiner1.result()
assertEquals(result.size, 0)
}
test("[Public] fetch result - full single element combiner (15pts)") {
val combiner1 = DLLCombinerTest(3)
combiner1 += 4
combiner1 += 2
combiner1 += 6
val result = combiner1.result()
val array = Array(4, 2, 6)
assert(Range(0,array.size).forall(i => array(i) == result(i)))
}
}
trait AbstractM7Suite extends munit.FunSuite with LibImpl {
def simpleCombiners = buildSimpleCombiners()
def basicCombiners = buildBasicCombiners()
def combinedCombiners = buildCombinedCombiners()
def largeCombiners = buildLargeCombiners()
def buildSimpleCombiners() = {
val simpleCombiners = List(
(DLLCombinerTest(4), Array(4, 2, 6, 1, 5, 4, 3, 5, 6, 3, 4, 5, 6, 3, 4, 5)),
(DLLCombinerTest(4), Array(7, 2, 2, 9, 3, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2)),
(DLLCombinerTest(4), Array.fill(16)(5))
)
simpleCombiners.foreach(elem => build(elem._1, elem._2))
simpleCombiners
}
def buildBasicCombiners() = {
val basicCombiners = List(
(DLLCombinerTest(2), Array(4, 2, 6)),
(DLLCombinerTest(5), Array(4, 2, 6)),
(DLLCombinerTest(3), Array(4, 2, 6, 1, 7, 2, 4)),
(DLLCombinerTest(4), Array(7, 2, 2, 9, 3, 2, 11, 12, 13, 14, 15, 16, 17, 22)),
(DLLCombinerTest(3), Array.fill(16)(7)),
(DLLCombinerTest(3), Array.fill(19)(7)),
(DLLCombinerTest(3), Array.fill(5)(7)),
(DLLCombinerTest(3), Array.fill(6)(7))
)
basicCombiners.foreach(elem => build(elem._1, elem._2))
basicCombiners
}
def buildCombinedCombiners() = {
var combinedCombiners = List[(DLLCombinerTest, Array[Int])]()
Range(1, 10).foreach { chunk_size =>
val array = basicCombiners.filter(elem => elem._1.chunk_size == chunk_size).foldLeft(Array[Int]()) { (acc, i) => acc ++ i._2 }
val combiner = DLLCombinerTest(chunk_size)
basicCombiners.filter(elem => elem._1.chunk_size == chunk_size).foreach(elem => combiner.combine(elem._1))
combinedCombiners = combinedCombiners :+ (combiner, array)
}
combinedCombiners
}
def buildLargeCombiners() = {
val largeCombiners = List(
(DLLCombinerTest(21), Array.fill(1321)(4) ++ Array.fill(1322)(7)),
(DLLCombinerTest(18), Array.fill(1341)(2) ++ Array.fill(1122)(5)),
(DLLCombinerTest(3), Array.fill(1321)(4) ++ Array.fill(1322)(7) ++ Array.fill(321)(4) ++ Array.fill(322)(7)),
(DLLCombinerTest(12), Array.fill(992321)(4) ++ Array.fill(99322)(7)),
(DLLCombinerTest(4), Array.fill(953211)(4) ++ Array.fill(999322)(1))
)
largeCombiners.foreach(elem => build(elem._1, elem._2))
largeCombiners
}
def build(combiner: DLLCombinerTest, array: Array[Int]): DLLCombinerTest = {
array.foreach(elem => combiner += elem)
combiner
}
def compare(combiner: DLLCombinerTest, array: Array[Int]): Boolean = {
val result = combiner.result()
Range(0,array.size).forall(i => array(i) == result(i))
}
def buildAndCompare(combiner: DLLCombinerTest, array: Array[Int]): Boolean = {
array.foreach(elem => combiner += elem)
val result = combiner.result()
Range(0,array.size).forall(i => array(i) == result(i))
}
}
trait LibImpl extends M7 {
val forkJoinPool = new ForkJoinPool
abstract class TaskScheduler {
def schedule[T](body: => T): ForkJoinTask[T]
}
class DefaultTaskScheduler extends TaskScheduler {
def schedule[T](body: => T): ForkJoinTask[T] = {
val t = new RecursiveTask[T] {
def compute = body
}
Thread.currentThread match {
case wt: ForkJoinWorkerThread =>
t.fork()
case _ =>
forkJoinPool.execute(t)
}
t
}
}
val scheduler =
new DynamicVariable[TaskScheduler](new DefaultTaskScheduler)
def task[T](body: => T): ForkJoinTask[T] = {
scheduler.value.schedule(body)
}
class DLLCombinerTest(chunk_size: Int = 3) extends DLLCombinerImplementation(chunk_size) {
override def +=(elem: Int): Unit = {
if(cnt % chunk_size == 0) {
chunks = chunks + 1
val node = new Node(chunk_size)
if (cnt == 0) {
head = node
last = node
}
else {
last.next = node
node.previous = last
last = node
}
}
last.add(elem)
cnt += 1
}
override def combine(that: DLLCombiner): DLLCombiner = {
assert(this.chunk_size == that.chunk_size)
if (this.cnt == 0) {
this.head = that.head
this.last = that.last
this.cnt = that.cnt
this.chunks = that.chunks
this
}
else if (that.cnt == 0)
this
else {
this.last.next = that.head
that.head.previous = this.last
this.cnt = this.cnt + that.cnt
this.chunks = this.chunks + that.chunks
this.last = that.last
this
}
}
}
}
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted
File deleted