Skip to content

Commit bb42e61

Browse files
committed
8300493: Use ArraysSupport.vectorizedHashCode in j.u.zip.ZipCoder
Reviewed-by: alanb, lancea
1 parent 06394ee commit bb42e61

File tree

4 files changed

+112
-17
lines changed

4 files changed

+112
-17
lines changed

src/java.base/share/classes/java/lang/System.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2461,6 +2461,9 @@ public Stream<ModuleLayer> layers(ClassLoader loader) {
24612461
return ModuleLayer.layers(loader);
24622462
}
24632463

2464+
public int countPositives(byte[] bytes, int offset, int length) {
2465+
return StringCoding.countPositives(bytes, offset, length);
2466+
}
24642467
public String newStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException {
24652468
return String.newStringNoRepl(bytes, cs);
24662469
}

src/java.base/share/classes/java/util/zip/ZipCoder.java

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.nio.charset.CodingErrorAction;
3535
import java.util.Arrays;
3636

37+
import jdk.internal.util.ArraysSupport;
3738
import sun.nio.cs.UTF_8;
3839

3940
/**
@@ -209,25 +210,18 @@ int checkedHash(byte[] a, int off, int len) throws Exception {
209210
if (len == 0) {
210211
return 0;
211212
}
212-
213213
int end = off + len;
214-
int h = 0;
215-
while (off < end) {
216-
byte b = a[off];
217-
if (b >= 0) {
218-
// ASCII, keep going
219-
h = 31 * h + b;
220-
off++;
221-
} else {
222-
// Non-ASCII, fall back to decoding a String
223-
// We avoid using decoder() here since the UTF8ZipCoder is
224-
// shared and that decoder is not thread safe.
225-
// We use the JLA.newStringUTF8NoRepl variant to throw
226-
// exceptions eagerly when opening ZipFiles
227-
return hash(JLA.newStringUTF8NoRepl(a, end - len, len));
228-
}
214+
int asciiLen = JLA.countPositives(a, off, len);
215+
if (asciiLen != len) {
216+
// Non-ASCII, fall back to decoding a String
217+
// We avoid using decoder() here since the UTF8ZipCoder is
218+
// shared and that decoder is not thread safe.
219+
// We use the JLA.newStringUTF8NoRepl variant to throw
220+
// exceptions eagerly when opening ZipFiles
221+
return hash(JLA.newStringUTF8NoRepl(a, off, len));
229222
}
230-
223+
// T_BOOLEAN to treat the array as unsigned bytes, in line with StringLatin1.hashCode
224+
int h = ArraysSupport.vectorizedHashCode(a, off, len, 0, ArraysSupport.T_BOOLEAN);
231225
if (a[end - 1] != '/') {
232226
h = 31 * h + '/';
233227
}

src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ public interface JavaLangAccess {
302302
*/
303303
Stream<ModuleLayer> layers(ClassLoader loader);
304304

305+
/**
306+
* Count the number of leading positive bytes in the range.
307+
*/
308+
int countPositives(byte[] ba, int off, int len);
309+
305310
/**
306311
* Constructs a new {@code String} by decoding the specified subarray of
307312
* bytes using the specified {@linkplain java.nio.charset.Charset charset}.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package org.openjdk.bench.java.util.zip;
25+
26+
import org.openjdk.jmh.annotations.*;
27+
28+
import java.io.File;
29+
import java.io.FileOutputStream;
30+
import java.io.IOException;
31+
import java.nio.file.Files;
32+
import java.util.concurrent.TimeUnit;
33+
import java.util.zip.ZipEntry;
34+
import java.util.zip.ZipFile;
35+
import java.util.zip.ZipOutputStream;
36+
37+
/**
38+
* Simple benchmark measuring cost of opening zip files, parsing CEN
39+
* entries.
40+
*/
41+
@BenchmarkMode(Mode.AverageTime)
42+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
43+
@State(Scope.Thread)
44+
@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
45+
@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
46+
@Fork(3)
47+
public class ZipFileOpen {
48+
49+
@Param({"512", "1024"})
50+
private int size;
51+
52+
public File zipFile;
53+
54+
@Setup(Level.Trial)
55+
public void beforeRun() throws IOException {
56+
// Create a test Zip file with the number of entries.
57+
File tempFile = Files.createTempFile("zip-micro", ".zip").toFile();
58+
tempFile.deleteOnExit();
59+
try (FileOutputStream fos = new FileOutputStream(tempFile);
60+
ZipOutputStream zos = new ZipOutputStream(fos)) {
61+
62+
// Vary dir and entry sizes, with a bias towards shorter entries.
63+
String[] dirPrefixes = new String[] { "dir1", "dir2", "dir3",
64+
"longer-directory-name-", "ridiculously-long-pathname-to-help-exercize-vectorized-subroutines-"};
65+
String[] entryPrefixes = new String[] { "e", "long-entry-name-",
66+
"ridiculously-long-entry-name-to-help-exercize-vectorized-subroutines-"};
67+
68+
for (int i = 0; i < size; i++) {
69+
String ename = dirPrefixes[i % dirPrefixes.length] + i + "/";
70+
zos.putNextEntry(new ZipEntry(ename));
71+
72+
ename += entryPrefixes[i % entryPrefixes.length] + "-" + i;
73+
zos.putNextEntry(new ZipEntry(ename));
74+
}
75+
}
76+
zipFile = tempFile;
77+
}
78+
79+
@Benchmark
80+
public ZipFile openCloseZipFile() throws Exception {
81+
// Some shared resources in ZipFile are cached in a shared structure
82+
// and needs to be cleaned up to properly capture overhead of creating
83+
// a ZipFile - otherwise opening the same zip file again will reuse the
84+
// cached data and look artificially fast. By including the ZipFile.close()
85+
// we aggressively clear those resources pre-emptively. The operations
86+
// appears to be complex enough to not be subject to DCE but care needs
87+
// to be taken to check that things like initCEN is properly accounted
88+
// for if/when the ZipFile setup improves.
89+
ZipFile zf = new ZipFile(zipFile);
90+
zf.close();
91+
return zf;
92+
}
93+
}

0 commit comments

Comments
 (0)