Quellcode durchsuchen

Speed up image uploading

Him188 vor 5 Jahren
Ursprung
Commit
f5a8420231

+ 19 - 12
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt

@@ -40,6 +40,9 @@ import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 import kotlin.coroutines.CoroutineContext
 import kotlin.jvm.JvmSynthetic
+import kotlin.math.roundToInt
+import kotlin.time.ExperimentalTime
+import kotlin.time.measureTime
 
 @OptIn(ExperimentalContracts::class)
 internal fun GroupImpl.Companion.checkIsInstance(instance: Group) {
@@ -344,6 +347,7 @@ internal class GroupImpl(
         return MessageReceipt(source, this, botAsMember)
     }
 
+    @OptIn(ExperimentalTime::class)
     @JvmSynthetic
     override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage = try {
         if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
@@ -391,18 +395,21 @@ internal class GroupImpl(
                     // 每 10KB 等 1 秒, 最少等待 5 秒
                     val success = response.uploadIpList.zip(response.uploadPortList).any { (ip, port) ->
                         withTimeoutOrNull((image.inputSize * 1000 / 1024 / 10).coerceAtLeast(5000)) {
-                            bot.network.logger.verbose { "[Highway] Uploading group image to ${ip.toIpV4AddressString()}:$port: size=${image.inputSize / 1024} KiB" }
-                            HighwayHelper.uploadImage(
-                                client = bot.client,
-                                serverIp = ip.toIpV4AddressString(),
-                                serverPort = port,
-                                imageInput = image.input,
-                                inputSize = image.inputSize.toInt(),
-                                fileMd5 = image.md5,
-                                ticket = response.uKey,
-                                commandId = 2
-                            )
-                            bot.network.logger.verbose { "[Highway] Uploading group image: succeed" }
+                            bot.network.logger.verbose { "[Highway] Uploading group image to ${ip.toIpV4AddressString()}:$port, size=${image.inputSize / 1024} KiB" }
+
+                            val time = measureTime {
+                                HighwayHelper.uploadImage(
+                                    client = bot.client,
+                                    serverIp = ip.toIpV4AddressString(),
+                                    serverPort = port,
+                                    imageInput = image.input,
+                                    inputSize = image.inputSize.toInt(),
+                                    fileMd5 = image.md5,
+                                    ticket = response.uKey,
+                                    commandId = 2
+                                )
+                            }
+                            bot.network.logger.verbose { "[Highway] Uploading group image: succeed at ${(image.inputSize.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s" }
                             true
                         } ?: kotlin.run {
                             bot.network.logger.verbose { "[Highway] Uploading group image: timeout, retrying next server" }

+ 47 - 3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/md5.common.kt

@@ -2,6 +2,9 @@
 
 package net.mamoe.mirai.utils.internal
 
+import kotlinx.io.pool.DefaultPool
+import kotlinx.io.pool.ObjectPool
+
 expect abstract class InputStream {
     open fun available(): Int
     open fun close()
@@ -21,9 +24,50 @@ internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) {
     require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" }
 }
 
-internal inline fun InputStream.readInSequence(block: (Int) -> Unit) {
+
+internal inline fun InputStream.readInSequence(block: (ByteArray, len: Int) -> Unit) {
     var read: Int
-    while (this.read().also { read = it } != -1) {
-        block(read)
+    ByteArrayPool.useInstance { buf ->
+        while (this.read(buf).also { read = it } != -1) {
+            block(buf, read)
+        }
     }
 }
+
+
+/**
+ * 缓存 [ByteArray] 实例的 [ObjectPool]
+ */
+internal object ByteArrayPool : DefaultPool<ByteArray>(32) {
+    /**
+     * 每一个 [ByteArray] 的大小
+     */
+    const val BUFFER_SIZE: Int = 8192
+
+    override fun produceInstance(): ByteArray = ByteArray(BUFFER_SIZE)
+
+    override fun clearInstance(instance: ByteArray): ByteArray = instance
+
+    fun checkBufferSize(size: Int) {
+        require(size <= BUFFER_SIZE) { "sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE" }
+    }
+
+    fun checkBufferSize(size: Long) {
+        require(size <= BUFFER_SIZE) { "sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE" }
+    }
+
+    /**
+     * 请求一个大小至少为 [requestedSize] 的 [ByteArray] 实例.
+     */ // 不要写为扩展函数. 它需要优先于 kotlinx.io 的扩展函数 resolve
+    inline fun <R> useInstance(requestedSize: Int = 0, block: (ByteArray) -> R): R {
+        if (requestedSize > BUFFER_SIZE) {
+            return ByteArray(requestedSize).run(block)
+        }
+        val instance = borrow()
+        try {
+            return block(instance)
+        } finally {
+            recycle(instance)
+        }
+    }
+}

+ 2 - 2
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/md5.jvm.kt

@@ -13,8 +13,8 @@ internal actual fun ByteArray.md5(offset: Int, length: Int): ByteArray {
 internal actual fun InputStream.md5(): ByteArray {
     val digest = MessageDigest.getInstance("md5")
     digest.reset()
-    this.readInSequence {
-        digest.update(it.toByte())
+    this.readInSequence { buf, len ->
+        digest.update(buf, 0, len)
     }
     return digest.digest()
 }