This is an automated email from the ASF dual-hosted git repository.

gianm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 3c3eec5b1cb perf: Use Charset in StringUtils to/fromUtf8. (#20310)
3c3eec5b1cb is described below

commit 3c3eec5b1cb4c16a263bff79393d486f0381a3df
Author: Gian Merlino <[email protected]>
AuthorDate: Tue Sep 29 01:39:20 2026 -0700

    perf: Use Charset in StringUtils to/fromUtf8. (#20310)
    
    There is an old optimization in StringUtils#fromUtf8 and toUtf8 that
    has outlived its usefulness. Apparently it used to be faster to pass the
    String charset name rather than a Charset object, but that is no longer
    the case.
---
 .../seekablestream/StreamChunkReaderTest.java      | 38 +++++++++++-----------
 .../org/apache/druid/msq/exec/MSQExportTest.java   |  7 ++--
 .../apache/druid/java/util/common/StringUtils.java | 24 ++------------
 .../druid/java/util/common/StringUtilsTest.java    |  6 ++--
 .../InputStreamFullResponseHandlerTest.java        |  5 ++-
 .../response/ObjectOrErrorResponseHandlerTest.java |  9 +++--
 6 files changed, 35 insertions(+), 54 deletions(-)

diff --git 
a/indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/StreamChunkReaderTest.java
 
b/indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/StreamChunkReaderTest.java
index 9acee174b98..5ac9d83da40 100644
--- 
a/indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/StreamChunkReaderTest.java
+++ 
b/indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/StreamChunkReaderTest.java
@@ -34,7 +34,6 @@ import org.apache.druid.error.DruidException;
 import org.apache.druid.indexing.common.task.InputRowFilter;
 import org.apache.druid.java.util.common.DateTimes;
 import org.apache.druid.java.util.common.RE;
-import org.apache.druid.java.util.common.StringUtils;
 import org.apache.druid.java.util.common.parsers.JSONPathSpec;
 import org.apache.druid.java.util.common.parsers.ParseException;
 import org.apache.druid.query.filter.AndDimFilter;
@@ -58,6 +57,7 @@ import org.mockito.MockitoAnnotations;
 import javax.annotation.Nullable;
 import java.io.File;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -203,11 +203,11 @@ public class StreamChunkReaderTest
         Arrays.asList(
             new ByteEntity(
                 "{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\", 
\"column_b\": \"other\"}"
-                    .getBytes(StringUtils.UTF8_STRING)
+                    .getBytes(StandardCharsets.UTF_8)
             ),
             new ByteEntity(
                 "{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\", 
\"column_b\": \"title1\"}"
-                    .getBytes(StringUtils.UTF8_STRING)
+                    .getBytes(StandardCharsets.UTF_8)
             )
         ),
         false
@@ -264,19 +264,19 @@ public class StreamChunkReaderTest
         Arrays.asList(
             new ByteEntity(
                 "{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\", 
\"column_b\": \"other\"}"
-                    .getBytes(StringUtils.UTF8_STRING)
+                    .getBytes(StandardCharsets.UTF_8)
             ),
             new ByteEntity(
                 "{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\", 
\"column_b\": \"late\"}"
-                    .getBytes(StringUtils.UTF8_STRING)
+                    .getBytes(StandardCharsets.UTF_8)
             ),
             new ByteEntity(
                 "{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\", 
\"column_b\": \"early\"}"
-                    .getBytes(StringUtils.UTF8_STRING)
+                    .getBytes(StandardCharsets.UTF_8)
             ),
             new ByteEntity(
                 "{\"timestamp\": \"2020-01-01\", \"column_a\": \"y\", 
\"column_b\": \"title1\"}"
-                    .getBytes(StringUtils.UTF8_STRING)
+                    .getBytes(StandardCharsets.UTF_8)
             )
         ),
         false
@@ -314,7 +314,7 @@ public class StreamChunkReaderTest
 
     List<InputRow> parsedRows = chunkParser.parse(
         Collections.singletonList(
-            new ByteEntity(json.getBytes(StringUtils.UTF8_STRING))), false
+            new ByteEntity(json.getBytes(StandardCharsets.UTF_8))), false
     );
     // no exception and no parsed rows
     Assertions.assertEquals(0, parsedRows.size());
@@ -334,11 +334,11 @@ public class StreamChunkReaderTest
     Mockito.when(mockedByteEntityReader.read()).thenThrow(new 
ParseException(null, "error parsing malformed data"));
     final String json = "malformedJson";
     List<ByteEntity> byteEntities = Arrays.asList(
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING))
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8))
     );
     Assertions.assertThrows(
         RE.class,
@@ -367,11 +367,11 @@ public class StreamChunkReaderTest
     final String json = "malformedJson";
 
     List<ByteEntity> byteEntities = Arrays.asList(
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING)),
-        new ByteEntity(json.getBytes(StringUtils.UTF8_STRING))
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8)),
+        new ByteEntity(json.getBytes(StandardCharsets.UTF_8))
     );
 
     List<InputRow> parsedRows = chunkParser.parse(byteEntities, false);
@@ -398,7 +398,7 @@ public class StreamChunkReaderTest
   private void parseAndAssertResult(StreamChunkReader<ByteEntity> chunkParser) 
throws IOException
   {
     final String json = "{\"timestamp\": \"2020-01-01\", \"dim\": \"val\", 
\"met\": \"val2\"}";
-    List<InputRow> parsedRows = 
chunkParser.parse(Collections.singletonList(new 
ByteEntity(json.getBytes(StringUtils.UTF8_STRING))), false);
+    List<InputRow> parsedRows = 
chunkParser.parse(Collections.singletonList(new 
ByteEntity(json.getBytes(StandardCharsets.UTF_8))), false);
     Assertions.assertEquals(1, parsedRows.size());
     InputRow row = parsedRows.get(0);
     Assertions.assertEquals(DateTimes.of("2020-01-01"), row.getTimestamp());
diff --git 
a/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQExportTest.java 
b/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQExportTest.java
index 158484462e7..402d0ac8a34 100644
--- 
a/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQExportTest.java
+++ 
b/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQExportTest.java
@@ -36,6 +36,7 @@ import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -368,7 +369,7 @@ public class MSQExportTest extends MSQTestBase
   private List<String> readResultsFromFile(File resultFile) throws IOException
   {
     List<String> results = new ArrayList<>();
-    try (BufferedReader br = new BufferedReader(new 
InputStreamReader(Files.newInputStream(resultFile.toPath()), 
StringUtils.UTF8_STRING))) {
+    try (BufferedReader br = new BufferedReader(new 
InputStreamReader(Files.newInputStream(resultFile.toPath()), 
StandardCharsets.UTF_8))) {
       String line;
       while (!(line = br.readLine()).isEmpty()) {
         results.add(line);
@@ -430,7 +431,7 @@ public class MSQExportTest extends MSQTestBase
     final File manifestFile = new File(exportDir, 
ExportMetadataManager.MANIFEST_FILE);
     try (
         BufferedReader bufferedReader = new BufferedReader(
-            new InputStreamReader(Files.newInputStream(manifestFile.toPath()), 
StringUtils.UTF8_STRING)
+            new InputStreamReader(Files.newInputStream(manifestFile.toPath()), 
StandardCharsets.UTF_8)
         )
     ) {
       for (File file : resultFiles) {
@@ -445,7 +446,7 @@ public class MSQExportTest extends MSQTestBase
     final File metaFile = new File(exportDir, ExportMetadataManager.META_FILE);
     try (
         BufferedReader bufferedReader = new BufferedReader(
-            new InputStreamReader(Files.newInputStream(metaFile.toPath()), 
StringUtils.UTF8_STRING)
+            new InputStreamReader(Files.newInputStream(metaFile.toPath()), 
StandardCharsets.UTF_8)
         )
     ) {
       Assertions.assertEquals(
diff --git 
a/processing/src/main/java/org/apache/druid/java/util/common/StringUtils.java 
b/processing/src/main/java/org/apache/druid/java/util/common/StringUtils.java
index f351abd0cd8..84915633f70 100644
--- 
a/processing/src/main/java/org/apache/druid/java/util/common/StringUtils.java
+++ 
b/processing/src/main/java/org/apache/druid/java/util/common/StringUtils.java
@@ -28,12 +28,10 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
-import java.nio.charset.Charset;
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
@@ -43,15 +41,11 @@ import java.util.IllegalFormatException;
 import java.util.Locale;
 
 /**
- * As of OpenJDK / Oracle JDK 8, the JVM is optimized around String charset 
variable instead of Charset passing, that
- * is exploited in {@link #toUtf8(String)} and {@link #fromUtf8(byte[])}.
+ * Utilities for working with strings, including UTF-8 encoding and decoding.
  */
 public class StringUtils
 {
   public static final byte[] EMPTY_BYTES = new byte[0];
-  @Deprecated // Charset parameters to String are currently slower than the 
charset's string name
-  public static final Charset UTF8_CHARSET = StandardCharsets.UTF_8;
-  public static final String UTF8_STRING = StandardCharsets.UTF_8.toString();
   private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
   private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
 
@@ -231,13 +225,7 @@ public class StringUtils
 
   public static String fromUtf8(final byte[] bytes, int offset, int length)
   {
-    try {
-      return new String(bytes, offset, length, UTF8_STRING);
-    }
-    catch (UnsupportedEncodingException e) {
-      // Should never happen
-      throw new RuntimeException(e);
-    }
+    return new String(bytes, offset, length, StandardCharsets.UTF_8);
   }
 
   /**
@@ -299,13 +287,7 @@ public class StringUtils
    */
   public static byte[] toUtf8(final String string)
   {
-    try {
-      return string.getBytes(UTF8_STRING);
-    }
-    catch (UnsupportedEncodingException e) {
-      // Should never happen
-      throw new RuntimeException(e);
-    }
+    return string.getBytes(StandardCharsets.UTF_8);
   }
 
   /**
diff --git 
a/processing/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java
 
b/processing/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java
index 8a7c69fd46c..97bf05c74ac 100644
--- 
a/processing/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java
+++ 
b/processing/src/test/java/org/apache/druid/java/util/common/StringUtilsTest.java
@@ -24,9 +24,9 @@ import org.apache.druid.collections.ResourceHolder;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-import java.io.UnsupportedEncodingException;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 
 /**
@@ -53,13 +53,13 @@ public class StringUtilsTest
   );
 
   @Test
-  public void fromUtf8ConversionTest() throws UnsupportedEncodingException
+  public void fromUtf8ConversionTest()
   {
     byte[] bytes = new byte[]{'a', 'b', 'c', 'd'};
     Assertions.assertEquals("abcd", StringUtils.fromUtf8(bytes));
 
     String abcd = "abcd";
-    Assertions.assertEquals(abcd, 
StringUtils.fromUtf8(abcd.getBytes(StringUtils.UTF8_STRING)));
+    Assertions.assertEquals(abcd, 
StringUtils.fromUtf8(abcd.getBytes(StandardCharsets.UTF_8)));
   }
 
   @Test
diff --git 
a/processing/src/test/java/org/apache/druid/java/util/http/client/response/InputStreamFullResponseHandlerTest.java
 
b/processing/src/test/java/org/apache/druid/java/util/http/client/response/InputStreamFullResponseHandlerTest.java
index 38d4c1ac5df..48d57f79ce8 100644
--- 
a/processing/src/test/java/org/apache/druid/java/util/http/client/response/InputStreamFullResponseHandlerTest.java
+++ 
b/processing/src/test/java/org/apache/druid/java/util/http/client/response/InputStreamFullResponseHandlerTest.java
@@ -26,7 +26,6 @@ import io.netty.handler.codec.http.HttpResponse;
 import io.netty.handler.codec.http.HttpResponseStatus;
 import io.netty.handler.codec.http.HttpVersion;
 import org.apache.commons.io.IOUtils;
-import org.apache.druid.java.util.common.StringUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -43,10 +42,10 @@ public class InputStreamFullResponseHandlerTest
     ClientResponse<InputStreamFullResponseHolder> clientResp = 
responseHandler.handleResponse(response, null);
 
     // In Netty 4 the body arrives via HttpContent chunks after the initial 
HttpResponse.
-    DefaultHttpContent firstChunk = new 
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StringUtils.UTF8_STRING)));
+    DefaultHttpContent firstChunk = new 
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StandardCharsets.UTF_8)));
     clientResp = responseHandler.handleChunk(clientResp, firstChunk, 1);
 
-    DefaultHttpContent secondChunk = new 
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StringUtils.UTF8_STRING)));
+    DefaultHttpContent secondChunk = new 
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StandardCharsets.UTF_8)));
     clientResp = responseHandler.handleChunk(clientResp, secondChunk, 2);
 
     clientResp = responseHandler.done(clientResp);
diff --git 
a/processing/src/test/java/org/apache/druid/java/util/http/client/response/ObjectOrErrorResponseHandlerTest.java
 
b/processing/src/test/java/org/apache/druid/java/util/http/client/response/ObjectOrErrorResponseHandlerTest.java
index d49d3f2c674..f7dd0d421b6 100644
--- 
a/processing/src/test/java/org/apache/druid/java/util/http/client/response/ObjectOrErrorResponseHandlerTest.java
+++ 
b/processing/src/test/java/org/apache/druid/java/util/http/client/response/ObjectOrErrorResponseHandlerTest.java
@@ -27,7 +27,6 @@ import io.netty.handler.codec.http.HttpResponseStatus;
 import io.netty.handler.codec.http.HttpVersion;
 import org.apache.commons.io.IOUtils;
 import org.apache.druid.java.util.common.Either;
-import org.apache.druid.java.util.common.StringUtils;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -50,10 +49,10 @@ public class ObjectOrErrorResponseHandlerTest
 
     // In Netty 4 the body arrives via HttpContent chunks after the initial 
HttpResponse.
     DefaultHttpContent firstChunk =
-        new 
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StringUtils.UTF8_STRING)));
+        new 
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StandardCharsets.UTF_8)));
     clientResp = responseHandler.handleChunk(clientResp, firstChunk, 1);
     DefaultHttpContent secondChunk =
-        new 
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StringUtils.UTF8_STRING)));
+        new 
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StandardCharsets.UTF_8)));
     clientResp = responseHandler.handleChunk(clientResp, secondChunk, 2);
     clientResp = responseHandler.done(clientResp);
 
@@ -104,10 +103,10 @@ public class ObjectOrErrorResponseHandlerTest
 
     // Body chunks for an error response.
     DefaultHttpContent firstChunk =
-        new 
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StringUtils.UTF8_STRING)));
+        new 
DefaultHttpContent(Unpooled.wrappedBuffer("abcd".getBytes(StandardCharsets.UTF_8)));
     clientResp = responseHandler.handleChunk(clientResp, firstChunk, 1);
     DefaultHttpContent secondChunk =
-        new 
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StringUtils.UTF8_STRING)));
+        new 
DefaultHttpContent(Unpooled.wrappedBuffer("efg".getBytes(StandardCharsets.UTF_8)));
     clientResp = responseHandler.handleChunk(clientResp, secondChunk, 2);
     clientResp = responseHandler.done(clientResp);
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to