【问题标题】:How to measure performance/speed of Files.copy method while the process is on-going?如何在过程进行时测量 Files.copy 方法的性能/速度?
【发布时间】:2019-05-28 14:12:30
【问题描述】:

我有一个通用的抽象方法,它获取输入流(可以来自网络套接字,或来自本地存储上的文件)并将数据保存在磁盘上。

下面是函数的小sn-p:

fun saveToFile(data: InputStream, fileDestination: File) {

            val bytesWritten = Files.copy(data, fileDestination.toPath(), StandardCopyOption.REPLACE_EXISTING)
            println("$bytesWritten bytes were saved at ${fileDestination.absolutePath}")

}

是否可以在过程/方法进行时测量数据保存在磁盘上的速度/速率?例如,是否有可能调用返回速率/速度的函数或更新保存该数据的对象?

如果我自己使用 InputStream/OutputStream 进行实现,我可以有如下示例:

fun saveData(data: InputStream, fileDestination: File, measureSpeed : (Statistics) -> Unit = { }) {

        val outputStream = fileDestination.outputStream()
        val maxBufferSize = 1024
        val totalAmountData = data.available()
        var totalBytesWritten = 0
        var bytesWriteNextIteration: Int // amount of bytes that will be sent in only one write call
        val statistics = Statistics(amountSent = 0, lastWriteBytes = 0, lastWriteTime = 1)
        while (totalBytesWritten < totalAmountData) {
            bytesWriteNextIteration = totalAmountData - totalBytesWritten
            if (bytesWriteNextIteration > maxBufferSize) {
                bytesWriteNextIteration = maxBufferSize
            }
            val bytes = ByteArray(bytesWriteNextIteration)

            val nano = measureNanoTime {
                outputStream.write(bytes)
            }
            statistics.amountSent = totalBytesWritten.toLong()
            statistics.lastWriteBytes = bytesWriteNextIteration.toLong()
            statistics.lastWriteTime = nano
            measureSpeed(statistics)
            totalBytesWritten += bytesWriteNextIteration
        }

        outputStream.flush()
        outputStream.close()
    }

    data class Statistics(var amountSent: Long, var lastWriteBytes: Long, var lastWriteTime: Long)

并用measureSpeed方法计算复制/传输率。

【问题讨论】:

  • 您的替代 Kotlin 实现远比必要的复杂。在此处查看“标准 Java 复制循环”。它没有你做的 1/4 困难。
  • 我在我的实现中使用第一种方法saveToFile,单行命令有多复杂? @user207421 发布第二个 sn-p 是为了显示我希望通过使用 Files.copy 方法或类似方法获得的功能。

标签: java performance kotlin stream


【解决方案1】:

由于我没有找到任何内置功能,因此最简单的方法是“重载”所需的 Files.copy 方法并改为调用该函数。

重载方法可以类似如下:

private val BUFFER_SIZE = 8192

@Throws(IOException::class)
private fun copy(source: InputStream, sink: OutputStream, networkStatistics: NetworkStatistics, measureSpeed : (NetworkStatistics) -> Unit = { }): Long {
    var nread = 0L
    val buf = ByteArray(BUFFER_SIZE)
    var n: Int
    n = source.read(buf)
    while (n > 0) {
        val nano = measureNanoTime {
            sink.write(buf, 0, n)
            nread += n.toLong()
            n = source.read(buf)
        }
        networkStatistics.amountSent = nread
        networkStatistics.lastPacketBytes = n.toLong()
        networkStatistics.lastPacketTime = nano
        measureSpeed(networkStatistics)
    }
    return nread
}

@Throws(IOException::class)
fun copy(`in`: InputStream, target: Path, networkStatistics: NetworkStatistics, measureSpeed : (NetworkStatistics) -> Unit = { }, vararg options: CopyOption ): Long {
    // ensure not null before opening file
    Objects.requireNonNull(`in`)

    // check for REPLACE_EXISTING
    var replaceExisting = false
    for (opt in options) {
        if (opt === StandardCopyOption.REPLACE_EXISTING) {
            replaceExisting = true
        } else {
            if (opt == null) {
                throw NullPointerException("options contains 'null'")
            } else {
                throw UnsupportedOperationException(opt.toString() + " not supported")
            }
        }
    }

    // attempt to delete an existing file
    var se: SecurityException? = null
    if (replaceExisting) {
        try {
            Files.deleteIfExists(target)
        } catch (x: SecurityException) {
            se = x
        }

    }

    // attempt to create target file. If it fails with
    // FileAlreadyExistsException then it may be because the security
    // manager prevented us from deleting the file, in which case we just
    // throw the SecurityException.
    val ostream: OutputStream
    try {
        ostream = Files.newOutputStream(target, StandardOpenOption.CREATE_NEW,
                StandardOpenOption.WRITE)
    } catch (x: FileAlreadyExistsException) {
        if (se != null)
            throw se
        // someone else won the race and created the file
        throw x
    }

    // do the copy
    ostream.use { out -> return copy(`in`, out, networkStatistics, measureSpeed = { networkStatistics -> measureSpeed(networkStatistics) }) }
}

它会被称为:

val statistics = NetworkStatistics(responseShouldBe, 0, 0, 1)
copy(inputStream, file.toPath(), statistics, { it: NetworkStatistics -> measureSpeed(it) }, StandardCopyOption.REPLACE_EXISTING)

private fun measureSpeed(stats: NetworkStatistics) {
    val a = stats.lastPacketBytes
    val b = stats.lastPacketTime
    val miliseconds = b.toDouble() / 1000
    val seconds = miliseconds / 1000
    println("$a per ($seconds seconds) or ($miliseconds milisecs) or ($b nanosecs) -- ${(a.toDouble()/(1024*1024))/seconds} MB/seconds")
}

【讨论】:

    猜你喜欢
    • 2023-03-23
    • 1970-01-01
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多