2015-10-09 5 views
0

Есть ли более эффективный способ поиска последовательности разделителей строк на ByteBuf? В частности, я ищу способ найти последовательность \r\n.Netty 4 альтернатива LineBasedFrameDecoder

Поскольку я хочу найти 2 байта, используя bytebuf.forEachByte(ByteBufProcessor), это не сработает.

Самый простой способ, который я мог найти, - использовать LineBasedFrameDecoder. Проблема в том, что некоторые сообщения, которые я получаю, могут быть довольно большими (в диапазоне MB) и так же, как упоминал Норман Маурер, here, проходящий через ByteBuf, очень неэффективен при работе с сообщениями этого большого размера (может потребоваться несколько минут, чтобы найти разделитель).

ответ

1

Вы все еще можете найти два байта:

public class CrLfProcessor implements ByteBufProcessor{ 
    private byte previousByte; 

    @Override 
    public boolean process(byte value) { 
    if(previousByte == '\r'){ 
     if(value == '\n'){ 
     return false; 
     } 
    } 

    previousByte = value; 
    return true; 
    } 
} 

Вот JMH тест тестирования различных оптимизаций:

Fork(1) 
@State(Scope.Benchmark) 
@OutputTimeUnit(TimeUnit.MILLISECONDS) 
@Measurement(iterations = 10) 
@Warmup(iterations = 10) 
@BenchmarkMode(Mode.AverageTime) 
public class ByteBufProcessorBenchmark { 
    private static interface ByteProcessor { 
    boolean process(byte value); 
    } 

    private static final int DATA_SIZE = 1024 * 1024; 
    private byte[] data; 

    @Setup(Level.Trial) 
    public void setUp() { 
    data = new byte[DATA_SIZE]; 
    Random random = new Random(); 
    random.nextBytes(data); 
    } 

    @Benchmark 
    public void crFirst(Blackhole blackhole) { 
    ByteProcessor byteProcessor = new ByteProcessor() { 
     private byte previousByte; 
     @Override 
     public boolean process(byte value) { 
     if(previousByte == '\r'){ 
      if(value == '\n'){ 
      return false; 
      } 
     } 

     previousByte = value; 
     return true; 
     } 
    }; 

    doProcess(byteProcessor, blackhole); 
    } 

    @Benchmark 
    public void lfFirst(Blackhole blackhole) { 
    ByteProcessor byteProcessor = new ByteProcessor() { 
     private byte previousByte; 
     @Override 
     public boolean process(byte value) { 
     if (value == '\n') { 
      if(previousByte == '\r'){ 
      return false; 
      } 
     } 

     previousByte = value; 
     return true; 
     } 
    }; 

    doProcess(byteProcessor, blackhole); 
    } 

    @Benchmark 
    public void crFirstUpdateCacheOnDemand(Blackhole blackhole) { 
    ByteProcessor byteProcessor = new ByteProcessor() { 
     private byte previousByte; 
     @Override 
     public boolean process(byte value) { 
     if(previousByte == '\r'){ 
      if(value == '\n'){ 
      return false; 
      } 
      previousByte = 0; 
     }else if(value == '\r'){ 
      previousByte = value; 
     } 
     return true; 
     } 
    }; 

    doProcess(byteProcessor, blackhole); 
    } 

    @Benchmark 
    public void lfFirstUpdateCacheOnDemand(Blackhole blackhole) { 
    ByteProcessor byteProcessor = new ByteProcessor() { 
     private byte previousByte; 
     @Override 
     public boolean process(byte value) { 
     if (value == '\n') { 
      if(previousByte == '\r'){ 
      return false; 
      } 
      previousByte = 0; 
     }else if(value == '\r'){ 
      previousByte = value; 
     } 
     return true; 
     } 
    }; 

    doProcess(byteProcessor, blackhole); 
    } 



@Benchmark 
    public void consume(Blackhole blackhole){ 
    for(int i = 0; i < data.length; i++){ 
     blackhole.consume(data[i]); 
    } 
    } 


    private void doProcess(ByteProcessor byteProcessor, Blackhole blackhole){ 
    for(int i = 0; i < data.length; i++){ 
     blackhole.consume(byteProcessor.process(data[i])); 
    } 
    } 

} 

А вот повторно результаты:

# Run complete. Total time: 00:01:21 

Benchmark            Mode Cnt Score Error Units 
ByteBufProcessorBenchmark.crFirst      avgt 10 4,211 ± 0,061 ms/op 
ByteBufProcessorBenchmark.crFirstUpdateCacheOnDemand avgt 10 4,285 ± 0,336 ms/op 
ByteBufProcessorBenchmark.lfFirst      avgt 10 4,375 ± 0,289 ms/op 
ByteBufProcessorBenchmark.lfFirstUpdateCacheOnDemand avgt 10 4,129 ± 0,075 ms/op 
ByteBufProcessorBenchmark.consume      avgt 10 3,126 ± 0,152 ms/op 

Как вы можете видеть самые быстрые опция ByteBufProcessorBenchmark.lfFirstUpdateCacheOnDemand, но разница с ByteBufProcessorBenchmark.crFirst заключается в том, что она не перевешивает добавленную сложность.

Каковы ваши требования к производительности, так как 4 мс (включая черную дыру, которую вы можете видеть по результатам, занимает 3 мс) на МБ ИМХО не замедляется вообще; В конце вы получаете 1 МБ за миллисекунду, что неплохо.

+0

Я не могу повторно использовать процессор, и он все еще не достаточно быстрый. Но это лучше, чем у меня на данный момент. Благодаря! –

+0

Вы можете его повторно использовать - просто добавьте метод с именем 'reset()', который устанавливает 'previousByte', чтобы сказать 0 и вызывать этот метод перед использованием процессора в другом буфере. Также вы можете проверить, будет ли разница в производительности (из-за эффектов кеша) путем замены двух операторов 'if'. –

+0

Прохладный! Спасибо за информацию. Изменение этого не будет иметь никакого существенного влияния прямо сейчас, потому что у меня есть другие узкие места, которые нужно адресовать первым. Я добавлю примечание, чтобы изучить это в будущем. –

Смежные вопросы