From 8d8a1335252facf6ad046adb481f31fa55322289 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Apr 2024 21:21:48 +0200 Subject: [PATCH 1/5] use optimised ByteString.asInputStream Update Http2FrameHpackSupport.scala Update ByteStringInputStream.scala revert Http2FrameHpackSupport change Update ByteStringInputStream.scala Update ByteStringInputStream.scala --- .../http2/hpack/ByteStringInputStream.scala | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala index f3b3dc5a3..f0fb6fe0e 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala @@ -14,6 +14,9 @@ package org.apache.pekko.http.impl.engine.http2.hpack import java.io.{ ByteArrayInputStream, InputStream } +import java.lang.invoke.{ MethodHandles, MethodType } + +import scala.util.Try import org.apache.pekko import pekko.annotation.InternalApi @@ -24,10 +27,22 @@ import pekko.util.ByteString.ByteString1C @InternalApi private[http2] object ByteStringInputStream { + private lazy val byteStringInputStreamMethodTypeOpt = Try { + val lookup = MethodHandles.publicLookup() + val inputStreamMethodType = MethodType.methodType(classOf[InputStream]) + lookup.findVirtual(classOf[ByteString], "asInputStream", inputStreamMethodType) + }.toOption + def apply(bs: ByteString): InputStream = + byteStringInputStreamMethodTypeOpt.map { mh => + mh.invoke(bs).asInstanceOf[InputStream] + }.getOrElse { + legacyConvert(bs) + } + + private def legacyConvert(bs: ByteString): InputStream = bs match { case cs: ByteString1C => - // TODO optimise, ByteString needs to expose InputStream (esp if array backed, nice!) new ByteArrayInputStream(cs.toArrayUnsafe()) case _ => // NOTE: We actually measured recently, and compact + use array was pretty good usually From 253e3d82bd1346f735a9bc7d944416bc62abe975 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 23 Apr 2024 15:34:56 +0200 Subject: [PATCH 2/5] optimise ByteStringInputStream.scala Update ByteStringInputStream.scala Update ByteStringInputStream.scala --- .../http2/hpack/ByteStringInputStream.scala | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala index f0fb6fe0e..727026b1f 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala @@ -33,19 +33,23 @@ private[http2] object ByteStringInputStream { lookup.findVirtual(classOf[ByteString], "asInputStream", inputStreamMethodType) }.toOption - def apply(bs: ByteString): InputStream = - byteStringInputStreamMethodTypeOpt.map { mh => - mh.invoke(bs).asInstanceOf[InputStream] - }.getOrElse { - legacyConvert(bs) - } - - private def legacyConvert(bs: ByteString): InputStream = - bs match { - case cs: ByteString1C => - new ByteArrayInputStream(cs.toArrayUnsafe()) - case _ => - // NOTE: We actually measured recently, and compact + use array was pretty good usually - apply(bs.compact) + def apply(bs: ByteString): InputStream = bs match { + case cs: ByteString1C => + new ByteArrayInputStream(cs.toArrayUnsafe()) + case _ => { + byteStringInputStreamMethodTypeOpt.map { mh => + mh.invoke(bs).asInstanceOf[InputStream] + }.getOrElse { + legacyConvert(bs) + } } + } + + private def legacyConvert(bs: ByteString): InputStream = bs match { + case cs: ByteString1C => + new ByteArrayInputStream(cs.toArrayUnsafe()) + case _ => + // NOTE: We actually measured recently, and compact + use array was pretty good usually + legacyConvert(bs.compact) + } } From 58240a29b414c30311ee86d188078822ee3ccdad Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 23 Apr 2024 15:51:12 +0200 Subject: [PATCH 3/5] make byteStringInputStreamMethodTypeOpt a val --- .../http/impl/engine/http2/hpack/ByteStringInputStream.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala index 727026b1f..72a403266 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala @@ -27,7 +27,7 @@ import pekko.util.ByteString.ByteString1C @InternalApi private[http2] object ByteStringInputStream { - private lazy val byteStringInputStreamMethodTypeOpt = Try { + private val byteStringInputStreamMethodTypeOpt = Try { val lookup = MethodHandles.publicLookup() val inputStreamMethodType = MethodType.methodType(classOf[InputStream]) lookup.findVirtual(classOf[ByteString], "asInputStream", inputStreamMethodType) From b1c606b4f346c3fbd874bde0707d6d72a1c22d82 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 23 Apr 2024 21:45:24 +0200 Subject: [PATCH 4/5] refactor --- .../engine/http2/hpack/ByteStringInputStream.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala index 72a403266..82af241eb 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala @@ -35,21 +35,25 @@ private[http2] object ByteStringInputStream { def apply(bs: ByteString): InputStream = bs match { case cs: ByteString1C => - new ByteArrayInputStream(cs.toArrayUnsafe()) + getInputStreamUnsafe(cs) case _ => { byteStringInputStreamMethodTypeOpt.map { mh => mh.invoke(bs).asInstanceOf[InputStream] }.getOrElse { - legacyConvert(bs) + legacyConvert(bs.compact) } } } private def legacyConvert(bs: ByteString): InputStream = bs match { case cs: ByteString1C => - new ByteArrayInputStream(cs.toArrayUnsafe()) + getInputStreamUnsafe(cs) case _ => // NOTE: We actually measured recently, and compact + use array was pretty good usually legacyConvert(bs.compact) } + + private def getInputStreamUnsafe(bs: ByteString): InputStream = + new ByteArrayInputStream(bs.toArrayUnsafe()) + } From 02fea87b504b9c3680a489f4f33f3123178d42fe Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 23 Apr 2024 22:17:31 +0200 Subject: [PATCH 5/5] avoid getOrElse --- .../impl/engine/http2/hpack/ByteStringInputStream.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala index 82af241eb..53161e694 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/hpack/ByteStringInputStream.scala @@ -36,13 +36,12 @@ private[http2] object ByteStringInputStream { def apply(bs: ByteString): InputStream = bs match { case cs: ByteString1C => getInputStreamUnsafe(cs) - case _ => { - byteStringInputStreamMethodTypeOpt.map { mh => - mh.invoke(bs).asInstanceOf[InputStream] - }.getOrElse { + case _ => + if (byteStringInputStreamMethodTypeOpt.isDefined) { + byteStringInputStreamMethodTypeOpt.get.invoke(bs).asInstanceOf[InputStream] + } else { legacyConvert(bs.compact) } - } } private def legacyConvert(bs: ByteString): InputStream = bs match {