scala:循环遍历文件,一次读取20个字节,并在第3个位置清空字节

时间:2018-03-09 16:18:35

标签: scala

我在java中有一个代码片段,它逐字节地循环遍历文件,并在每20个字节的第3个位置消隐掉字节。这是为每个循环使用。

logic:
for(byte b: raw){
 if (pos is 3) b = 32;
 if (i > 20) i = 0; 
 i++
}

由于我正在学习scala,我想知道是否有更好的方法在scala中逐字节循环。

我已经在scala中读入了如下的字节数组:

val result = IOUtils.toByteArray(new FileInputStream (new File(fileDir)))

感谢。

2 个答案:

答案 0 :(得分:4)

这是与Tzach Zohar截然相反的解决方案:

def parallel(ba: Array[Byte], blockSize: Int = 2048): Unit = {
  val n = ba.size
  val numJobs = (n + blockSize - 1) / blockSize
  (0 until numJobs).par.foreach { i =>
    val startIdx = i * blockSize
    val endIdx = n min ((i + 1) * blockSize)
    var j = startIdx + ((3 - startIdx) % 20 + 20) % 20
    while (j < endIdx) {
      ba(j) = 32
      j += 20
    }
  }
}

你会看到很多可变变量,可怕的命令式while循环,以及一些带有模运算的奇怪技巧。这实际上不是惯用的Scala。但是这个解决方案的有趣之处在于它并行处理字节数组的块。我已经使用各种块大小将此解决方案所需的时间与您的天真解决方案进行了比较:

Naive:          38.196
Parallel(  16): 11.676000
Parallel(  32): 7.260000
Parallel(  64): 4.311000
Parallel( 128): 2.757000
Parallel( 256): 2.473000
Parallel( 512): 2.462000
Parallel(1024): 2.435000
Parallel(2048): 2.444000
Parallel(4096): 2.416000
Parallel(8192): 2.420000

至少在这个不太透彻的微基准测试(10MB阵列上1000次重复)中,或多或少有效实现的并行版本比你问题中的for循环好15倍。

现在的问题是:“更好”是什么意思?

  • 我的建议比你天真的方法稍快一点
  • @ TzachZohar的功能解决方案可以更好地概括 代码可以在像Apache Spark这样的集群上移动。
  • 我通常更喜欢更接近@ TzachZohar解决方案的东西,因为它更容易阅读。

那么,这取决于你所优化的内容:性能?概论?可读性?可维护性?对于每个“更好”的概念,你可以得到一个不同的答案。我试图优化性能。 @TzachZohar针对可读性和可维护性进行了优化。这导致了两种截然不同的解决方案。

microbenchmark的完整代码,以防有人感兴趣:

val array = Array.ofDim[Byte](10000000)

def naive(ba: Array[Byte]): Unit = {
  var pos = 0
  for (i <- 0 until ba.size) {
    if (pos == 3) ba(i) = 32
    pos += 1
    if (pos == 20) pos = 0
  }
}

def parallel(ba: Array[Byte], blockSize: Int): Unit = {
  val n = ba.size
  val numJobs = (n + blockSize - 1) / blockSize
  (0 until numJobs).par.foreach { i =>
    val startIdx = i * blockSize
    val endIdx = n min ((i + 1) * blockSize)
    var j = startIdx + ((3 - startIdx) % 20 + 20) % 20
    while (j < endIdx) {
      ba(j) = 32
      j += 20
    }
  }
}

def measureTime[U](repeats: Long)(block: => U): Double = {
  val start = System.currentTimeMillis

  var iteration = 0
  while (iteration < repeats) {
    iteration += 1
    block
  }

  val end = System.currentTimeMillis
  (end - start).toDouble / repeats
}

println("Basic sanity check (did I get the modulo arithmetic right?):")

{
  val testArray = Array.ofDim[Byte](50)
  naive(testArray)
  println(testArray.mkString("[", ",", "]"))
}
{
  for (blockSize <- List(3, 7, 13, 16, 17, 32)) {
    val testArray = Array.ofDim[Byte](50)
    parallel(testArray, blockSize)
    println(testArray.mkString("[", ",", "]"))
  }
}

val Reps = 1000

val naiveTime = measureTime(Reps)(naive(array))

println("Naive:          " + naiveTime)
for (blockSize <- List(16,32,64,128,256,512,1024,2048,4096,8192)) {
  val parallelTime = measureTime(Reps)(parallel(array, blockSize))
  println("Parallel(%4d): %f".format(blockSize, parallelTime))
}

答案 1 :(得分:2)

这是实现此目的的一种方法:

val updated = result.grouped(20).flatMap { arr => arr.update(3, 32); arr }