Skip to content

Commit

Permalink
Changed test execution to parallel to enhance performance(#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaeho authored and jaeho committed Jan 15, 2024
1 parent f117b8c commit 1cf3085
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 40 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ lazy val root = project
version := "0.1.0-SNAPSHOT",
scalaVersion := scala3Version,
libraryDependencies ++= Seq(
"org.scalameta" %% "munit" % "0.7.29" % Test,
"org.scalatest" %% "scalatest" % "3.2.11" % Test,
"org.twc" % "t2" % "1.0" from file(
"lib/terminator-compiler-1.0.jar",
).toURI.toString,
Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/fhetest/Command.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ case object CmdCompile extends Command("compile") {
case file :: backendString :: _ =>
parseBackend(backendString) match {
case Some(backend) =>
given workspaceDir: String = getWorkspaceDir(backend)
val (ast, symbolTable, encType) = Parse(file)
Print(ast, symbolTable, encType, backend)
case None => println("Argument parsing error: Invalid backend.")
Expand All @@ -91,6 +92,7 @@ case object CmdExecute extends Command("execute") {
case backendString :: _ =>
parseBackend(backendString) match {
case Some(backend) =>
given workspaceDir: String = getWorkspaceDir(backend)
val output = Execute(backend)
println(output)
case None => println("Argument parsing error: Invalid backend.")
Expand Down
3 changes: 1 addition & 2 deletions src/main/scala/fhetest/Phase/Execute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ package fhetest.Phase
import fhetest.Utils.*

case object Execute {
def apply(backend: Backend): String = {
val workspaceDir = getWorkspaceDir(backend)
def apply(backend: Backend)(using workspaceDir: String): String = {
val binPath = s"$workspaceDir/bin"
val cmakeCommand = "cmake ."
val makeCommand = "make -j"
Expand Down
3 changes: 1 addition & 2 deletions src/main/scala/fhetest/Phase/Print.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ case object Print {
symbolTable: SymbolTable,
encType: ENC_TYPE,
backend: Backend,
): Unit = {
val workspaceDir = getWorkspaceDir(backend)
)(using workspaceDir: String): Unit = {
val compiledDirPath = Paths.get(s"$workspaceDir/compiled")
if (!Files.exists(compiledDirPath)) {
Files.createDirectories(compiledDirPath)
Expand Down
37 changes: 37 additions & 0 deletions src/main/scala/fhetest/Utils/Utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package fhetest.Utils

import org.twc.terminator.Main.ENC_TYPE as T2ENC_TYPE

import java.nio.file.{Files, Path, Paths, StandardCopyOption}
import java.util.{Comparator, UUID}
import scala.util.Try

enum Backend(val name: String):
case SEAL extends Backend("SEAL")
case OpenFHE extends Backend("OpenFHE")
Expand All @@ -28,3 +32,36 @@ def parseBackend(backendString: String): Option[Backend] =
def getWorkspaceDir(backend: Backend): String = backend match
case Backend.SEAL => fhetest.SEAL_DIR
case Backend.OpenFHE => fhetest.OPENFHE_DIR

def withBackendTempDir[Result](
backend: Backend,
action: (String) => Result,
): Result = {
val baseWorkspaceDirName = getWorkspaceDir(backend)
val baseWorkspaceDir = Paths.get(baseWorkspaceDirName)

val prefix = "fhetest"
val tempDir: Path = Files.createTempDirectory(prefix)
val tempDirName = tempDir.toString

try {
Files.walk(baseWorkspaceDir).parallel().forEach { basePath =>
val targetPath = tempDir.resolve(baseWorkspaceDir.relativize(basePath))
if (Files.isDirectory(basePath)) {
if (!Files.exists(targetPath)) Files.createDirectory(targetPath)
} else {
Files.copy(basePath, targetPath, StandardCopyOption.REPLACE_EXISTING)
}
}
action(tempDirName)
} finally {
// 임시 디렉토리 삭제
Try(
Files
.walk(tempDir)
.parallel()
.sorted(Comparator.reverseOrder())
.forEach(Files.delete),
)
}
}
82 changes: 53 additions & 29 deletions src/test/scala/BackendTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,78 @@ import fhetest.Phase.{Execute, Parse, Print}
import fhetest.Utils.*

import java.nio.file.{Files, Paths}
import scala.jdk.CollectionConverters._
import scala.jdk.CollectionConverters.*
import scala.concurrent.{Future, ExecutionContext}
import scala.util.{Success, Failure}

import munit.Clue.generate
import org.scalatest.funsuite.AsyncFunSuite
import org.scalatest.Assertion

abstract class BackendTest(val backends: List[Backend]) extends AsyncFunSuite {
implicit val ec: ExecutionContext = ExecutionContext.global

abstract class BackendTest(val backends: List[Backend]) extends munit.FunSuite {
// T2_RESOURCE_DIR에서 .t2 확장자를 가진 모든 파일을 가져옵니다.
val t2Files =
Files
.newDirectoryStream(Paths.get(T2_RESOURCE_DIR), "*.t2")
.iterator()
.asScala
.toList

// 각 .t2 파일에 대해, 해당 파일의 이름을 가져와서 .res 확장자로 변경하고,
// 그 이름으로 RESULT_RESOURCE_DIR에서 파일을 찾아 그 내용을 읽습니다.
for t2File <- t2Files do {
// 결과 검증 로직을 별도의 함수로 분리
def verifyResults(obtained: String, expected: String): Assertion = {
val obtainedLines = obtained.split("\n")
val resultLines = expected.split("\n")
obtainedLines
.zip(resultLines)
.foreach {
case (obtainedLine, resultLine) =>
val obtainedNumbers =
obtainedLine.split(" ").map(_.toDouble)
val resultNumbers = resultLine.split(" ").map(_.toDouble)
obtainedNumbers
.zip(resultNumbers)
.foreach {
case (obtained, result) =>
assert(
Math.abs(obtained - result) < 0.0001,
s"$obtained and $result are not close",
)
}
}
succeed
}

// 모든 테스트 결과를 담을 Future 리스트
val allTests: List[Future[Assertion]] = t2Files.flatMap { t2File =>
val t2FileName = t2File.getFileName.toString
val resultFileName = t2FileName.replace(".t2", ".res")
val resultFilePath = Paths.get(RESULT_RESOURCE_DIR, resultFileName)
val testName = t2FileName.replace(".t2", "")

// 파일의 내용을 문자열로 읽습니다.
val resultFileContents = new String(Files.readAllBytes(resultFilePath))
val (ast, symTable, enc_type) = Parse(t2File.toString)

for backend <- backends do {
test(testName + "/" + backend) {
Print(ast, symTable, enc_type, backend)
backends.map { backend =>
val testFuture = Future {
withBackendTempDir(
backend,
workspaceDir => {
Print(ast, symTable, enc_type, backend)(using workspaceDir)
Execute(backend)(using workspaceDir)
},
)
}.map(obtained => verifyResults(obtained, resultFileContents),
) // 결과 검증 함수 호출

val obtained = Execute(backend)

val obtainedLines = obtained.split("\n")
val resultLines = resultFileContents.split("\n")
obtainedLines.zip(resultLines).foreach {
case (obtainedLine, resultLine) =>
val obtainedNumbers = obtainedLine.split(" ").map(_.toDouble)
val resultNumbers = resultLine.split(" ").map(_.toDouble)
obtainedNumbers.zip(resultNumbers).foreach {
case (obtained, result) =>
assert(
Math.abs(obtained - result) < 0.0001,
s"$obtained and $result are not close",
)
}
}
}
test(testName + "/" + backend)(testFuture)
testFuture
}
}

// 모든 테스트가 완료될 때까지 기다림
Future.sequence(allTests).map(_ => ())
}

import Backend.*
class ALLbackendTest extends BackendTest(List(SEAL, OpenFHE))

This comment has been minimized.

Copy link
@Maokami

Maokami Jan 15, 2024

Collaborator

Currently, due to the race condition occurred by mutable objects(i.e. symbol_table in T2), each backend test must be executed separately.

class SEALbackendTest extends BackendTest(List(SEAL))
class OpenFHEbackendTest extends BackendTest(List(OpenFHE))
6 changes: 0 additions & 6 deletions workspace/SEAL/README.md

This file was deleted.

0 comments on commit 1cf3085

Please sign in to comment.