8300493: Use ArraysSupport.vectorizedHashCode in j.u.zip.ZipCoder

Reviewed-by: alanb, lancea
This commit is contained in:
Claes Redestad 2023-01-21 11:54:51 +00:00
parent 06394ee8b1
commit bb42e61a61
4 changed files with 112 additions and 17 deletions

View File

@ -2461,6 +2461,9 @@ public final class System {
return ModuleLayer.layers(loader);
}
public int countPositives(byte[] bytes, int offset, int length) {
return StringCoding.countPositives(bytes, offset, length);
}
public String newStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException {
return String.newStringNoRepl(bytes, cs);
}

View File

@ -34,6 +34,7 @@ import java.nio.charset.CharacterCodingException;
import java.nio.charset.CodingErrorAction;
import java.util.Arrays;
import jdk.internal.util.ArraysSupport;
import sun.nio.cs.UTF_8;
/**
@ -209,25 +210,18 @@ class ZipCoder {
if (len == 0) {
return 0;
}
int end = off + len;
int h = 0;
while (off < end) {
byte b = a[off];
if (b >= 0) {
// ASCII, keep going
h = 31 * h + b;
off++;
} else {
// Non-ASCII, fall back to decoding a String
// We avoid using decoder() here since the UTF8ZipCoder is
// shared and that decoder is not thread safe.
// We use the JLA.newStringUTF8NoRepl variant to throw
// exceptions eagerly when opening ZipFiles
return hash(JLA.newStringUTF8NoRepl(a, end - len, len));
}
int asciiLen = JLA.countPositives(a, off, len);
if (asciiLen != len) {
// Non-ASCII, fall back to decoding a String
// We avoid using decoder() here since the UTF8ZipCoder is
// shared and that decoder is not thread safe.
// We use the JLA.newStringUTF8NoRepl variant to throw
// exceptions eagerly when opening ZipFiles
return hash(JLA.newStringUTF8NoRepl(a, off, len));
}
// T_BOOLEAN to treat the array as unsigned bytes, in line with StringLatin1.hashCode
int h = ArraysSupport.vectorizedHashCode(a, off, len, 0, ArraysSupport.T_BOOLEAN);
if (a[end - 1] != '/') {
h = 31 * h + '/';
}

View File

@ -302,6 +302,11 @@ public interface JavaLangAccess {
*/
Stream<ModuleLayer> layers(ClassLoader loader);
/**
* Count the number of leading positive bytes in the range.
*/
int countPositives(byte[] ba, int off, int len);
/**
* Constructs a new {@code String} by decoding the specified subarray of
* bytes using the specified {@linkplain java.nio.charset.Charset charset}.

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.util.zip;
import org.openjdk.jmh.annotations.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* Simple benchmark measuring cost of opening zip files, parsing CEN
* entries.
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
public class ZipFileOpen {
@Param({"512", "1024"})
private int size;
public File zipFile;
@Setup(Level.Trial)
public void beforeRun() throws IOException {
// Create a test Zip file with the number of entries.
File tempFile = Files.createTempFile("zip-micro", ".zip").toFile();
tempFile.deleteOnExit();
try (FileOutputStream fos = new FileOutputStream(tempFile);
ZipOutputStream zos = new ZipOutputStream(fos)) {
// Vary dir and entry sizes, with a bias towards shorter entries.
String[] dirPrefixes = new String[] { "dir1", "dir2", "dir3",
"longer-directory-name-", "ridiculously-long-pathname-to-help-exercize-vectorized-subroutines-"};
String[] entryPrefixes = new String[] { "e", "long-entry-name-",
"ridiculously-long-entry-name-to-help-exercize-vectorized-subroutines-"};
for (int i = 0; i < size; i++) {
String ename = dirPrefixes[i % dirPrefixes.length] + i + "/";
zos.putNextEntry(new ZipEntry(ename));
ename += entryPrefixes[i % entryPrefixes.length] + "-" + i;
zos.putNextEntry(new ZipEntry(ename));
}
}
zipFile = tempFile;
}
@Benchmark
public ZipFile openCloseZipFile() throws Exception {
// Some shared resources in ZipFile are cached in a shared structure
// and needs to be cleaned up to properly capture overhead of creating
// a ZipFile - otherwise opening the same zip file again will reuse the
// cached data and look artificially fast. By including the ZipFile.close()
// we aggressively clear those resources pre-emptively. The operations
// appears to be complex enough to not be subject to DCE but care needs
// to be taken to check that things like initCEN is properly accounted
// for if/when the ZipFile setup improves.
ZipFile zf = new ZipFile(zipFile);
zf.close();
return zf;
}
}