HADOOP-14424. Add CRC32C performance test. Contributed by LiXin Ge.
(cherry picked from commit 75043d3429
)
This commit is contained in:
parent
3106f256e2
commit
023dcdc73d
|
@ -33,7 +33,7 @@ import org.apache.hadoop.fs.ChecksumException;
|
||||||
import org.apache.log4j.Level;
|
import org.apache.log4j.Level;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performance tests to compare performance of Crc32 implementations
|
* Performance tests to compare performance of Crc32|Crc32C implementations
|
||||||
* This can be run from the command line with:
|
* This can be run from the command line with:
|
||||||
*
|
*
|
||||||
* java -cp path/to/test/classes:path/to/common/classes \
|
* java -cp path/to/test/classes:path/to/common/classes \
|
||||||
|
@ -43,17 +43,21 @@ import org.apache.log4j.Level;
|
||||||
*
|
*
|
||||||
* hadoop org.apache.hadoop.util.Crc32PerformanceTest
|
* hadoop org.apache.hadoop.util.Crc32PerformanceTest
|
||||||
*
|
*
|
||||||
|
* If any argument is provided, this test will run with non-directly buffer.
|
||||||
|
*
|
||||||
* The output is in JIRA table format.
|
* The output is in JIRA table format.
|
||||||
*/
|
*/
|
||||||
public class Crc32PerformanceTest {
|
public class Crc32PerformanceTest {
|
||||||
static final int MB = 1024 * 1024;
|
static final int MB = 1024 * 1024;
|
||||||
|
|
||||||
static interface Crc32 {
|
interface Crc32 {
|
||||||
|
|
||||||
public void verifyChunked(ByteBuffer data, int bytesPerCrc, ByteBuffer crcs,
|
void verifyChunked(ByteBuffer data, int bytesPerCrc, ByteBuffer crcs,
|
||||||
String filename, long basePos) throws ChecksumException;
|
String filename, long basePos) throws ChecksumException;
|
||||||
|
|
||||||
static final class Native implements Crc32 {
|
DataChecksum.Type crcType();
|
||||||
|
|
||||||
|
final class Native implements Crc32 {
|
||||||
@Override
|
@Override
|
||||||
public void verifyChunked(ByteBuffer data, int bytesPerSum,
|
public void verifyChunked(ByteBuffer data, int bytesPerSum,
|
||||||
ByteBuffer sums, String fileName, long basePos)
|
ByteBuffer sums, String fileName, long basePos)
|
||||||
|
@ -61,40 +65,91 @@ public class Crc32PerformanceTest {
|
||||||
NativeCrc32.verifyChunkedSums(bytesPerSum, DataChecksum.Type.CRC32.id,
|
NativeCrc32.verifyChunkedSums(bytesPerSum, DataChecksum.Type.CRC32.id,
|
||||||
sums, data, fileName, basePos);
|
sums, data, fileName, basePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataChecksum.Type crcType() {
|
||||||
|
return DataChecksum.Type.CRC32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class NativeC implements Crc32 {
|
||||||
|
@Override
|
||||||
|
public void verifyChunked(ByteBuffer data, int bytesPerSum,
|
||||||
|
ByteBuffer sums, String fileName, long basePos)
|
||||||
|
throws ChecksumException {
|
||||||
|
|
||||||
static abstract class AbstractCrc32<T extends Checksum> implements Crc32 {
|
if (data.isDirect()) {
|
||||||
|
NativeCrc32.verifyChunkedSums(bytesPerSum,
|
||||||
|
DataChecksum.Type.CRC32C.id, sums, data, fileName, basePos);
|
||||||
|
} else {
|
||||||
|
final int dataOffset = data.arrayOffset() + data.position();
|
||||||
|
final int crcsOffset = sums.arrayOffset() + sums.position();
|
||||||
|
NativeCrc32.verifyChunkedSumsByteArray(bytesPerSum,
|
||||||
|
DataChecksum.Type.CRC32C.id, sums.array(), crcsOffset,
|
||||||
|
data.array(), dataOffset, data.remaining(), fileName, basePos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataChecksum.Type crcType() {
|
||||||
|
return DataChecksum.Type.CRC32C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractCrc32<T extends Checksum> implements Crc32 {
|
||||||
abstract T newAlgorithm();
|
abstract T newAlgorithm();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void verifyChunked(ByteBuffer data, int bytesPerCrc,
|
public void verifyChunked(ByteBuffer data, int bytesPerCrc,
|
||||||
ByteBuffer crcs, String filename, long basePos)
|
ByteBuffer sums, String filename, long basePos)
|
||||||
throws ChecksumException {
|
throws ChecksumException {
|
||||||
final Checksum algorithm = newAlgorithm();
|
final Checksum algorithm = newAlgorithm();
|
||||||
if (data.hasArray() && crcs.hasArray()) {
|
final DataChecksum.Type type = crcType();
|
||||||
DataChecksum.verifyChunked(DataChecksum.Type.CRC32, algorithm,
|
if (data.hasArray() && sums.hasArray()) {
|
||||||
data.array(), data.position(), data.remaining(), bytesPerCrc,
|
DataChecksum.verifyChunked(type, algorithm, data.array(),
|
||||||
crcs.array(), crcs.position(), filename, basePos);
|
data.position(), data.remaining(), bytesPerCrc, sums.array(),
|
||||||
|
sums.position(), filename, basePos);
|
||||||
} else {
|
} else {
|
||||||
DataChecksum.verifyChunked(DataChecksum.Type.CRC32, algorithm,
|
DataChecksum.verifyChunked(type, algorithm, data, bytesPerCrc,
|
||||||
data, bytesPerCrc, crcs, filename, basePos);
|
sums, filename, basePos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class Zip extends AbstractCrc32<CRC32> {
|
final class Zip extends AbstractCrc32<CRC32> {
|
||||||
@Override
|
@Override
|
||||||
public CRC32 newAlgorithm() {
|
public CRC32 newAlgorithm() {
|
||||||
return new CRC32();
|
return new CRC32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataChecksum.Type crcType() {
|
||||||
|
return DataChecksum.Type.CRC32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class PureJava extends AbstractCrc32<PureJavaCrc32> {
|
final class PureJava extends AbstractCrc32<PureJavaCrc32> {
|
||||||
@Override
|
@Override
|
||||||
public PureJavaCrc32 newAlgorithm() {
|
public PureJavaCrc32 newAlgorithm() {
|
||||||
return new PureJavaCrc32();
|
return new PureJavaCrc32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataChecksum.Type crcType() {
|
||||||
|
return DataChecksum.Type.CRC32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class PureJavaC extends AbstractCrc32<PureJavaCrc32C> {
|
||||||
|
@Override
|
||||||
|
public PureJavaCrc32C newAlgorithm() {
|
||||||
|
return new PureJavaCrc32C();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataChecksum.Type crcType() {
|
||||||
|
return DataChecksum.Type.CRC32C;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,9 +169,13 @@ public class Crc32PerformanceTest {
|
||||||
|
|
||||||
crcs.add(Crc32.Zip.class);
|
crcs.add(Crc32.Zip.class);
|
||||||
crcs.add(Crc32.PureJava.class);
|
crcs.add(Crc32.PureJava.class);
|
||||||
|
crcs.add(Crc32.PureJavaC.class);
|
||||||
|
|
||||||
if (direct && NativeCrc32.isAvailable()) {
|
if (NativeCrc32.isAvailable()) {
|
||||||
crcs.add(Crc32.Native.class);
|
if (direct) {
|
||||||
|
crcs.add(Crc32.Native.class);
|
||||||
|
}
|
||||||
|
crcs.add(Crc32.NativeC.class);
|
||||||
((Log4JLogger)LogFactory.getLog(NativeCodeLoader.class))
|
((Log4JLogger)LogFactory.getLog(NativeCodeLoader.class))
|
||||||
.getLogger().setLevel(Level.ALL);
|
.getLogger().setLevel(Level.ALL);
|
||||||
}
|
}
|
||||||
|
@ -131,13 +190,18 @@ public class Crc32PerformanceTest {
|
||||||
out.printf("Elapsed %.1fs\n", secondsElapsed(startTime));
|
out.printf("Elapsed %.1fs\n", secondsElapsed(startTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String args[]) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
new Crc32PerformanceTest(64, 5, true).run();
|
boolean isdirect = true;
|
||||||
|
|
||||||
|
if (args.length > 0) {
|
||||||
|
isdirect = false;
|
||||||
|
}
|
||||||
|
new Crc32PerformanceTest(64, 5, isdirect).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printCell(String s, int width, PrintStream out) {
|
private static void printCell(String s, int width, PrintStream outCrc) {
|
||||||
final int w = s.length() > width? s.length(): width;
|
final int w = s.length() > width? s.length(): width;
|
||||||
out.printf(" %" + w + "s |", s);
|
outCrc.printf(" %" + w + "s |", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ByteBuffer allocateByteBuffer(int length) {
|
private ByteBuffer allocateByteBuffer(int length) {
|
||||||
|
@ -155,16 +219,29 @@ public class Crc32PerformanceTest {
|
||||||
return dataBufs;
|
return dataBufs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ByteBuffer computeCrc(ByteBuffer dataBufs, int bytePerCrc) {
|
private ByteBuffer computeCrc(ByteBuffer dataBufs, int bytePerCrc,
|
||||||
|
DataChecksum.Type type) {
|
||||||
final int size = 4 * (dataBufs.remaining() - 1) / bytePerCrc + 1;
|
final int size = 4 * (dataBufs.remaining() - 1) / bytePerCrc + 1;
|
||||||
final ByteBuffer crcBufs = allocateByteBuffer(size);
|
final ByteBuffer crcBufs = allocateByteBuffer(size);
|
||||||
final DataChecksum checksum = DataChecksum.newDataChecksum(
|
final DataChecksum checksum = DataChecksum.newDataChecksum(
|
||||||
DataChecksum.Type.CRC32, bytePerCrc);
|
type, bytePerCrc);
|
||||||
checksum.calculateChunkedSums(dataBufs, crcBufs);
|
checksum.calculateChunkedSums(dataBufs, crcBufs);
|
||||||
return crcBufs;
|
return crcBufs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doBench(final List<Class<? extends Crc32>> crcs)
|
private ByteBuffer computeCrc(Class<? extends Crc32> clazz,
|
||||||
|
ByteBuffer dataBufs, int bytePerCrc) throws Exception {
|
||||||
|
final Constructor<? extends Crc32> ctor = clazz.getConstructor();
|
||||||
|
final Crc32 crc = ctor.newInstance();
|
||||||
|
final int size = 4 * (dataBufs.remaining() - 1) / bytePerCrc + 1;
|
||||||
|
final ByteBuffer crcBufs = allocateByteBuffer(size);
|
||||||
|
final DataChecksum checksum = DataChecksum.newDataChecksum(
|
||||||
|
crc.crcType(), bytePerCrc);
|
||||||
|
checksum.calculateChunkedSums(dataBufs, crcBufs);
|
||||||
|
return crcBufs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doBench(final List<Class<? extends Crc32>> crcTargets)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
final ByteBuffer[] dataBufs = new ByteBuffer[16];
|
final ByteBuffer[] dataBufs = new ByteBuffer[16];
|
||||||
for(int i = 0; i < dataBufs.length; i++) {
|
for(int i = 0; i < dataBufs.length; i++) {
|
||||||
|
@ -176,9 +253,9 @@ public class Crc32PerformanceTest {
|
||||||
out.printf(" (bpc: byte-per-crc in MB/sec; #T: #Theads)\n");
|
out.printf(" (bpc: byte-per-crc in MB/sec; #T: #Theads)\n");
|
||||||
|
|
||||||
// Warm up implementations to get jit going.
|
// Warm up implementations to get jit going.
|
||||||
final ByteBuffer[] crc32 = {computeCrc(dataBufs[0], 32)};
|
for (Class<? extends Crc32> c : crcTargets) {
|
||||||
final ByteBuffer[] crc512 = {computeCrc(dataBufs[0], 512)};
|
final ByteBuffer[] crc32 = {computeCrc(c, dataBufs[0], 32)};
|
||||||
for (Class<? extends Crc32> c : crcs) {
|
final ByteBuffer[] crc512 = {computeCrc(c, dataBufs[0], 512)};
|
||||||
doBench(c, 1, dataBufs, crc32, 32);
|
doBench(c, 1, dataBufs, crc32, 32);
|
||||||
doBench(c, 1, dataBufs, crc512, 512);
|
doBench(c, 1, dataBufs, crc512, 512);
|
||||||
}
|
}
|
||||||
|
@ -189,57 +266,69 @@ public class Crc32PerformanceTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doBench(final List<Class<? extends Crc32>> crcs,
|
private void doBench(final List<Class<? extends Crc32>> crcTargets,
|
||||||
final ByteBuffer[] dataBufs, final int bytePerCrc, final PrintStream out)
|
final ByteBuffer[] dataBufs, final int bytePerCrc,
|
||||||
|
final PrintStream outCrc)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
final ByteBuffer[] crcBufs = new ByteBuffer[dataBufs.length];
|
final ByteBuffer[] crcBufs = new ByteBuffer[dataBufs.length];
|
||||||
for(int i = 0; i < crcBufs.length; i++) {
|
final ByteBuffer[] crcBufsC = new ByteBuffer[dataBufs.length];
|
||||||
crcBufs[i] = computeCrc(dataBufs[i], bytePerCrc);
|
for(int i = 0; i < dataBufs.length; i++) {
|
||||||
|
crcBufs[i] = computeCrc(dataBufs[i], bytePerCrc,
|
||||||
|
DataChecksum.Type.CRC32);
|
||||||
|
crcBufsC[i] = computeCrc(dataBufs[i], bytePerCrc,
|
||||||
|
DataChecksum.Type.CRC32C);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String numBytesStr = " bpc ";
|
final String numBytesStr = " bpc ";
|
||||||
final String numThreadsStr = "#T";
|
final String numThreadsStr = "#T";
|
||||||
final String diffStr = "% diff";
|
final String diffStr = "% diff";
|
||||||
|
|
||||||
out.print('|');
|
outCrc.print('|');
|
||||||
printCell(numBytesStr, 0, out);
|
printCell(numBytesStr, 0, outCrc);
|
||||||
printCell(numThreadsStr, 0, out);
|
printCell(numThreadsStr, 0, outCrc);
|
||||||
for (int i = 0; i < crcs.size(); i++) {
|
for (int i = 0; i < crcTargets.size(); i++) {
|
||||||
final Class<? extends Crc32> c = crcs.get(i);
|
final Class<? extends Crc32> c = crcTargets.get(i);
|
||||||
out.print('|');
|
outCrc.print('|');
|
||||||
printCell(c.getSimpleName(), 8, out);
|
printCell(c.getSimpleName(), 8, outCrc);
|
||||||
for(int j = 0; j < i; j++) {
|
if (i > 0) {
|
||||||
printCell(diffStr, diffStr.length(), out);
|
printCell(diffStr, diffStr.length(), outCrc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.printf("\n");
|
outCrc.printf("\n");
|
||||||
|
|
||||||
for(int numThreads = 1; numThreads <= dataBufs.length; numThreads <<= 1) {
|
for(int numThreads = 1; numThreads <= dataBufs.length; numThreads <<= 1) {
|
||||||
out.printf("|");
|
outCrc.printf("|");
|
||||||
printCell(String.valueOf(bytePerCrc), numBytesStr.length(), out);
|
printCell(String.valueOf(bytePerCrc), numBytesStr.length(), outCrc);
|
||||||
printCell(String.valueOf(numThreads), numThreadsStr.length(), out);
|
printCell(String.valueOf(numThreads), numThreadsStr.length(), outCrc);
|
||||||
|
|
||||||
final List<BenchResult> previous = new ArrayList<BenchResult>();
|
final List<BenchResult> previous = new ArrayList<BenchResult>();
|
||||||
for(Class<? extends Crc32> c : crcs) {
|
for(Class<? extends Crc32> c : crcTargets) {
|
||||||
System.gc();
|
System.gc();
|
||||||
|
|
||||||
final BenchResult result = doBench(c, numThreads, dataBufs, crcBufs,
|
final BenchResult result;
|
||||||
bytePerCrc);
|
final Constructor<? extends Crc32> ctor = c.getConstructor();
|
||||||
|
final Crc32 crc = ctor.newInstance();
|
||||||
|
if (crc.crcType() == DataChecksum.Type.CRC32) {
|
||||||
|
result = doBench(c, numThreads, dataBufs, crcBufs, bytePerCrc);
|
||||||
|
} else {
|
||||||
|
result = doBench(c, numThreads, dataBufs, crcBufsC, bytePerCrc);
|
||||||
|
}
|
||||||
printCell(String.format("%9.1f", result.mbps),
|
printCell(String.format("%9.1f", result.mbps),
|
||||||
c.getSimpleName().length() + 1, out);
|
c.getSimpleName().length() + 1, outCrc);
|
||||||
|
|
||||||
//compare result with previous
|
//compare result with the last previous.
|
||||||
for(BenchResult p : previous) {
|
final int size = previous.size();
|
||||||
|
if (size > 0) {
|
||||||
|
BenchResult p = previous.get(size - 1);
|
||||||
final double diff = (result.mbps - p.mbps) / p.mbps * 100;
|
final double diff = (result.mbps - p.mbps) / p.mbps * 100;
|
||||||
printCell(String.format("%5.1f%%", diff), diffStr.length(), out);
|
printCell(String.format("%5.1f%%", diff), diffStr.length(), outCrc);
|
||||||
}
|
}
|
||||||
previous.add(result);
|
previous.add(result);
|
||||||
}
|
}
|
||||||
out.printf("\n");
|
outCrc.printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private BenchResult doBench(Class<? extends Crc32> clazz,
|
private BenchResult doBench(Class<? extends Crc32> clazz,
|
||||||
final int numThreads, final ByteBuffer[] dataBufs,
|
final int numThreads, final ByteBuffer[] dataBufs,
|
||||||
final ByteBuffer[] crcBufs, final int bytePerCrc)
|
final ByteBuffer[] crcBufs, final int bytePerCrc)
|
||||||
|
@ -248,36 +337,34 @@ public class Crc32PerformanceTest {
|
||||||
final Thread[] threads = new Thread[numThreads];
|
final Thread[] threads = new Thread[numThreads];
|
||||||
final BenchResult[] results = new BenchResult[threads.length];
|
final BenchResult[] results = new BenchResult[threads.length];
|
||||||
|
|
||||||
{
|
final Constructor<? extends Crc32> ctor = clazz.getConstructor();
|
||||||
final Constructor<? extends Crc32> ctor = clazz.getConstructor();
|
|
||||||
|
|
||||||
for(int i = 0; i < threads.length; i++) {
|
for(int i = 0; i < threads.length; i++) {
|
||||||
final Crc32 crc = ctor.newInstance();
|
final Crc32 crc = ctor.newInstance();
|
||||||
final long byteProcessed = dataBufs[i].remaining() * trials;
|
final long byteProcessed = dataBufs[i].remaining() * trials;
|
||||||
final int index = i;
|
final int index = i;
|
||||||
threads[i] = new Thread() {
|
threads[i] = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final long startTime = System.nanoTime();
|
final long startTime = System.nanoTime();
|
||||||
for (int i = 0; i < trials; i++) {
|
for (int i = 0; i < trials; i++) {
|
||||||
dataBufs[index].mark();
|
dataBufs[index].mark();
|
||||||
crcBufs[index].mark();
|
crcBufs[index].mark();
|
||||||
try {
|
try {
|
||||||
crc.verifyChunked(dataBufs[index], bytePerCrc, crcBufs[index],
|
crc.verifyChunked(dataBufs[index], bytePerCrc, crcBufs[index],
|
||||||
crc.getClass().getSimpleName(), dataBufs[index].position());
|
crc.getClass().getSimpleName(), dataBufs[index].position());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
results[index] = new BenchResult(t);
|
results[index] = new BenchResult(t);
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
dataBufs[index].reset();
|
dataBufs[index].reset();
|
||||||
crcBufs[index].reset();
|
crcBufs[index].reset();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
final double secsElapsed = secondsElapsed(startTime);
|
|
||||||
results[index] = new BenchResult(byteProcessed/secsElapsed/MB);
|
|
||||||
}
|
}
|
||||||
};
|
final double secsElapsed = secondsElapsed(startTime);
|
||||||
}
|
results[index] = new BenchResult(byteProcessed/secsElapsed/MB);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
for(Thread t : threads) {
|
for(Thread t : threads) {
|
||||||
|
@ -295,7 +382,7 @@ public class Crc32PerformanceTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class BenchResult {
|
private static class BenchResult {
|
||||||
/** Speed (MB per second) */
|
/** Speed (MB per second). */
|
||||||
final double mbps;
|
final double mbps;
|
||||||
final Throwable thrown;
|
final Throwable thrown;
|
||||||
|
|
||||||
|
@ -321,7 +408,7 @@ public class Crc32PerformanceTest {
|
||||||
return (System.nanoTime() - startTime) / 1000000000.0d;
|
return (System.nanoTime() - startTime) / 1000000000.0d;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void printSystemProperties(PrintStream out) {
|
static void printSystemProperties(PrintStream outCrc) {
|
||||||
final String[] names = {
|
final String[] names = {
|
||||||
"java.version",
|
"java.version",
|
||||||
"java.runtime.name",
|
"java.runtime.name",
|
||||||
|
@ -344,7 +431,7 @@ public class Crc32PerformanceTest {
|
||||||
|
|
||||||
final Properties p = System.getProperties();
|
final Properties p = System.getProperties();
|
||||||
for(String n : names) {
|
for(String n : names) {
|
||||||
out.printf("%" + max + "s = %s\n", n, p.getProperty(n));
|
outCrc.printf("%" + max + "s = %s\n", n, p.getProperty(n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue