001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.codec;
019
020import static org.junit.Assert.assertArrayEquals;
021import static org.junit.Assert.assertTrue;
022
023import java.io.ByteArrayInputStream;
024import java.io.ByteArrayOutputStream;
025import java.io.IOException;
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.ExtendedCell;
028import org.apache.hadoop.hbase.ExtendedCellScanner;
029import org.apache.hadoop.hbase.KeyValue;
030import org.apache.hadoop.hbase.io.CellOutputStream;
031import org.apache.hadoop.hbase.util.Bytes;
032import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
033import org.apache.yetus.audience.InterfaceAudience;
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037/**
038 * Do basic codec performance eval.
039 */
040@InterfaceAudience.Public
041public class CodecPerformance {
042
043  private static final Logger LOG = LoggerFactory.getLogger(CodecPerformance.class);
044
045  static ExtendedCell[] getCells(final int howMany) {
046    ExtendedCell[] cells = new ExtendedCell[howMany];
047    for (int i = 0; i < howMany; i++) {
048      byte[] index = Bytes.toBytes(i);
049      KeyValue kv = new KeyValue(index, Bytes.toBytes("f"), index, index);
050      cells[i] = kv;
051    }
052    return cells;
053  }
054
055  static int getRoughSize(final Cell[] cells) {
056    int size = 0;
057    for (Cell c : cells) {
058      size += c.getRowLength() + c.getFamilyLength() + c.getQualifierLength() + c.getValueLength();
059      size += Bytes.SIZEOF_LONG + Bytes.SIZEOF_BYTE;
060    }
061    return size;
062  }
063
064  static byte[] runEncoderTest(final int index, final int initialBufferSize,
065    final ByteArrayOutputStream baos, final CellOutputStream encoder, final ExtendedCell[] cells)
066    throws IOException {
067    long startTime = EnvironmentEdgeManager.currentTime();
068    for (int i = 0; i < cells.length; i++) {
069      encoder.write(cells[i]);
070    }
071    encoder.flush();
072    LOG.info("" + index + " encoded count=" + cells.length + " in "
073      + (EnvironmentEdgeManager.currentTime() - startTime) + "ms for encoder " + encoder);
074    // Ensure we did not have to grow the backing buffer.
075    assertTrue(baos.size() < initialBufferSize);
076    return baos.toByteArray();
077  }
078
079  static ExtendedCell[] runDecoderTest(final int index, final int count,
080    final ExtendedCellScanner decoder) throws IOException {
081    ExtendedCell[] cells = new ExtendedCell[count];
082    long startTime = EnvironmentEdgeManager.currentTime();
083    for (int i = 0; decoder.advance(); i++) {
084      cells[i] = decoder.current();
085    }
086    LOG.info("" + index + " decoded count=" + cells.length + " in "
087      + (EnvironmentEdgeManager.currentTime() - startTime) + "ms for decoder " + decoder);
088    // Ensure we did not have to grow the backing buffer.
089    assertTrue(cells.length == count);
090    return cells;
091  }
092
093  static void verifyCells(final Cell[] input, final Cell[] output) {
094    assertArrayEquals(input, output);
095  }
096
097  static void doCodec(final Codec codec, final ExtendedCell[] cells, final int cycles,
098    final int count, final int initialBufferSize) throws IOException {
099    byte[] bytes = null;
100    ExtendedCell[] cellsDecoded = null;
101    for (int i = 0; i < cycles; i++) {
102      ByteArrayOutputStream baos = new ByteArrayOutputStream(initialBufferSize);
103      Codec.Encoder encoder = codec.getEncoder(baos);
104      bytes = runEncoderTest(i, initialBufferSize, baos, encoder, cells);
105    }
106    for (int i = 0; i < cycles; i++) {
107      ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
108      Codec.Decoder decoder = codec.getDecoder(bais);
109      cellsDecoded = CodecPerformance.runDecoderTest(i, count, decoder);
110    }
111    verifyCells(cells, cellsDecoded);
112  }
113
114  public static void main(String[] args) throws IOException {
115    // How many Cells to encode/decode on each cycle.
116    final int count = 100000;
117    // How many times to do an operation; repeat gives hotspot chance to warm up.
118    final int cycles = 30;
119
120    ExtendedCell[] cells = getCells(count);
121    int size = getRoughSize(cells);
122    int initialBufferSize = 2 * size; // Multiply by 2 to ensure we don't have to grow buffer
123
124    // Test KeyValue codec.
125    doCodec(new KeyValueCodec(), cells, cycles, count, initialBufferSize);
126    doCodec(new CellCodec(), cells, cycles, count, initialBufferSize);
127    doCodec(new MessageCodec(), cells, cycles, count, initialBufferSize);
128  }
129}