JPoint 2016 - Etudes of DIY Java profiler

62
Как сделать профилировщик из палок... и других подручных средств @antonarhipov JPoint 2016, Москва

Transcript of JPoint 2016 - Etudes of DIY Java profiler

Page 1: JPoint 2016 - Etudes of DIY Java profiler

Как сделать профилировщикиз палок... и других подручных средств

@antonarhipov

JPoint 2016, Москва

Page 2: JPoint 2016 - Etudes of DIY Java profiler
Page 3: JPoint 2016 - Etudes of DIY Java profiler
Page 4: JPoint 2016 - Etudes of DIY Java profiler

Batch / ETL

Исходные данные : ~10k записейРезультат: ~20M записейj.u.Date / j.u.Calendar

Java 1.4 HP-UX

Первый запуск: 25 ч :-(После оптимизаций: 10 ч После изменения дизайна: 30 мин :-)

2005

История одного проекта

Page 5: JPoint 2016 - Etudes of DIY Java profiler

Тот самый случай когда выглядишь умным, но чувствуешь себя полным ДУРАКОМ

Page 6: JPoint 2016 - Etudes of DIY Java profiler

https://twitter.com/shipilev/status/578193813946134529

Page 7: JPoint 2016 - Etudes of DIY Java profiler

Как и когда вы используете профилировщик?

Page 8: JPoint 2016 - Etudes of DIY Java profiler

QA Staging Production

Тормозит!

Тесты показывают падение производительности

Всё плохо!

Page 9: JPoint 2016 - Etudes of DIY Java profiler
Page 10: JPoint 2016 - Etudes of DIY Java profiler

> Chrome DevTools

> FireBug

> YSlow

> …

> XRebel

“Проактивные” инструменты

Page 11: JPoint 2016 - Etudes of DIY Java profiler

> Пользовательский интерфейс

> Профилирование приложения

> Трассировка внешних вызовов

> Трассировка потоков

> Распределённые приложения

Этюды профайлероводства

Page 12: JPoint 2016 - Etudes of DIY Java profiler

UI & UX

Page 13: JPoint 2016 - Etudes of DIY Java profiler
Page 14: JPoint 2016 - Etudes of DIY Java profiler

package org.apache.catalina.connector;

public class Response implements HttpServletResponse {

public ServletOutputStream getOutputStream() throws IOException { if (this.usingWriter) { throw new IllegalStateException(…)); } this.usingOutputStream = true; if (this.outputStream == null) { this.outputStream = new CoyoteOutputStream(this.outputBuffer); } return this.outputStream; }

Javassist

Page 15: JPoint 2016 - Etudes of DIY Java profiler

package org.apache.catalina.connector;

public class Response implements HttpServletResponse {

public ServletOutputStream getOutputStream() throws IOException { if (this.usingWriter) { throw new IllegalStateException(…)); } this.usingOutputStream = true; if (this.outputStream == null) { this.outputStream = new CoyoteOutputStream(this.outputBuffer); } return this.outputStream; }    ctClass.getDeclaredMethod("getOutputStream").insertAfter(        "if  ("  +  _interceptor  +  "  !=  null)  {"  +        "    $_  =  (ServletOutputStream)"  +  _interceptor  +  ".getOutputStream($_);"  +        "}");  

Javassist

Page 16: JPoint 2016 - Etudes of DIY Java profiler

package org.apache.catalina.connector;

public class Response implements HttpServletResponse {

public ServletOutputStream getOutputStream() throws IOException { if (this.usingWriter) { throw new IllegalStateException(…)); } this.usingOutputStream = true; if (this.outputStream == null) { this.outputStream = new CoyoteOutputStream(this.outputBuffer); } if (_interceptor != null) { this.outputStream = (ServletOutputStream)_interceptor .getOutputStream(this.outputStream); } return this.outputStream; }

Page 17: JPoint 2016 - Etudes of DIY Java profiler

Куда уходит время?

Page 18: JPoint 2016 - Etudes of DIY Java profiler

Windows: Ctrl+Break

*nix: kill -3 PID

"http-nio-8080-exec-1" #40 daemon prio=5 os_prio=31 tid=0x00007fd7057ed800 nid=0x7313 runnable java.lang.Thread.State: RUNNABLE at o.s.s.p.w.OwnerController.processFindForm(OwnerController.java:89) at s.r.NativeMethodAccessorImpl.invoke0(Native Method) at s.r.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at s.r.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at j.l.r.Method.invoke(Method.java:497)

Сэмплирование

Page 19: JPoint 2016 - Etudes of DIY Java profiler

Интервал : 20 мс

Кол-во сэмплов: 4

Page 20: JPoint 2016 - Etudes of DIY Java profiler

Интервал : 1 мс

Кол-во сэмплов: 100+

Page 21: JPoint 2016 - Etudes of DIY Java profiler

main()foo()bar()baz()

Page 22: JPoint 2016 - Etudes of DIY Java profiler

Safepoints

main()foo()bar()baz()

Page 23: JPoint 2016 - Etudes of DIY Java profiler

Safepoints

main()foo()bar()baz()

Page 24: JPoint 2016 - Etudes of DIY Java profiler

Java Mission ControlJava 7u40

Honest Profiler

https://github.com/RichardWarburton/honest-profiler

AsyncGetCallTraceJVMTI

Как обуздать safepoint bias?

Page 25: JPoint 2016 - Etudes of DIY Java profiler

main()foo()bar()baz()

Инструментирующий профилировщик

Высокие накладные расходы для быстрых методов

Page 26: JPoint 2016 - Etudes of DIY Java profiler

public  void  businessMethod()  {    long  start  =  System.currentTimeMillis();    work();    Profiler.log(System.currentTimeMillis()-­‐start);  }

Page 27: JPoint 2016 - Etudes of DIY Java profiler

public  void  businessMethod()  {    long  start  =  System.nanoTime();    work();    Profiler.log(System.nanoTime()-­‐start);}

Page 28: JPoint 2016 - Etudes of DIY Java profiler

public  void  businessMethod()  {    Profiler.start("businessMethod");      try  {        work();    }  finally  {        Profiler.log("businessMethod");    }}

Page 29: JPoint 2016 - Etudes of DIY Java profiler

System.nanoTime

Параллельный поток для замеров времениsleep-wakup-updateyield-wakup-updatebusy-loop-update

Page 30: JPoint 2016 - Etudes of DIY Java profiler

class  Profiler  {      Loop  loop;    public  static  void  start(String  method)  {        long  now  =  loop.getTime();          …    }  

Page 31: JPoint 2016 - Etudes of DIY Java profiler

public  class  Loop  implements  Runnable  {        private  volatile  long  time;        public  void  run()  {                while  (running)  {                        time  =  System.nanoTime();                        sleep();                }        }  

       public  final  long  getTime()  {                return  time;        }  

Busy Loop

Page 32: JPoint 2016 - Etudes of DIY Java profiler

Наносекунды в приближении

Чтение памяти имеет цену Занимает циклы процессора = ns

Каждый слой ПО добавляет к цене. JVM, JNI, OS, HW

http://shipilev.net/blog/2014/nanotrusting-nanotime/

Nanotrusting the NanoTime

Page 33: JPoint 2016 - Etudes of DIY Java profiler

Наносекунды в приближении

http://shipilev.net/blog/2014/nanotrusting-nanotime/

Nanotrusting the NanoTime

granularity_nanotime: 26.300 +- 0.205 ns latency_nanotime: 25.542 +- 0.024 ns

granularity_nanotime: 29.322 +- 1.293 ns latency_nanotime: 29.910 +- 1.626 ns

granularity_nanotime: 371,419 +- 1,541 ns latency_nanotime: 14,415 +- 0,389 ns

Linux

Solaris

Windows

Page 34: JPoint 2016 - Etudes of DIY Java profiler

Thread.sleepWin8: 1.7 мс (JNI + socket poll: 1.0 ms )Linux: 0.1 мсOS X: 0.05 мсВсё таки загружает ядро процессора на 25-50%

Thread.yieldПланировшик в Windows может проигнорировать наш поток в случае высокой загрузки процессора

Page 35: JPoint 2016 - Etudes of DIY Java profiler

public  class  Loop  implements  Runnable  {        private  volatile  long  time;        public  void  run()  {                while  (running)  {                        time  =  System.nanoTime();                        sleep();                }        }          private  void  sleep()  {            if  (!MiscUtil.isWindows())  {                  Thread.yield();            }        }

Busy Loop

Page 36: JPoint 2016 - Etudes of DIY Java profiler

Busy Looppublic  class  Loop  implements  Runnable  {        private  volatile  long  time;        public  void  run()  {                while  (running)  {                        time  =  System.nanoTime();                        sleep();                }        }          private  void  sleep()  {            if  (!MiscUtil.isWindows())  {                  Thread.yield();            }        }

Page 37: JPoint 2016 - Etudes of DIY Java profiler

Busy Looppublic  class  Loop  implements  Runnable  {        private  volatile  long  time;        public  void  run()  {                while  (running)  {                        time  =  System.nanoTime();                        sleep();                }        }          private  void  sleep()  {            if  (!MiscUtil.isWindows())  {                  Thread.yield();            }        }

Page 38: JPoint 2016 - Etudes of DIY Java profiler

main()foo()

bar()baz()

234

Page 39: JPoint 2016 - Etudes of DIY Java profiler

main()foo()

bar()baz()

235

boo()

Дисбаланс ошибки для веток с разной глубиной

Page 40: JPoint 2016 - Etudes of DIY Java profiler
Page 41: JPoint 2016 - Etudes of DIY Java profiler
Page 42: JPoint 2016 - Etudes of DIY Java profiler

Оптимизации

LeafMethodAnalyser(AbstractMethodBodyAnalyser  mv)  {    super(mv,  not(eq(VISIT_METHOD_INSN)),                        not(eq(VISIT_INVOKE_DYNAMIC_INSN)),                        not(eq(VISIT_JUMP_INSN)));}

Page 43: JPoint 2016 - Etudes of DIY Java profiler

Оптимизации public  int  calculateMargin(Order  order){    return  order.grossPrice()  /  order.salesPrice()  *  100;}

public  int  grossPrice()  {    return  gross;}

public  int  grossPrice();      Code:          0:  aload_0          1:  getfield  #2      //  Field  gross:I          4:  ireturn

Не вызывает других методов

Нет циклов

Можно игнорировать

Page 44: JPoint 2016 - Etudes of DIY Java profiler

44

Потоки

Page 45: JPoint 2016 - Etudes of DIY Java profiler

val  r  =  new  Runnable(){}  val  t  =  new  Thread(r)  t.start()

t.join()

public  void  run()  {      doSomething();  }

Page 46: JPoint 2016 - Etudes of DIY Java profiler

Runnable  r  =  new  Runnable(){    public  void  run()  {          doSomething();    }};  

Page 47: JPoint 2016 - Etudes of DIY Java profiler

Runnable  r  =  new  Runnable(){    private  WeakReference<Context>  ctx;  

   public  void  run()  {          Context  sink  =  ctx.getChildContext();        sink  =  Sinks.startAsync(sink);        try  {            doSomething();        }  finally  {            Sinks.stopAsync(sink);        }    }};  

Page 48: JPoint 2016 - Etudes of DIY Java profiler
Page 49: JPoint 2016 - Etudes of DIY Java profiler

Внешние вызовы

Page 50: JPoint 2016 - Etudes of DIY Java profiler

JDBC> Вариант 1. Proxy

> Вариант 2. Список известных нам классов. Что знаем, то и инструментируем.

> Вариант 3. Есть известные интерфейсы - сканируем иерархию классов

Привет, ClassCastException

Замедление старта приложения

Привет, DB2

Page 51: JPoint 2016 - Etudes of DIY Java profiler
Page 52: JPoint 2016 - Etudes of DIY Java profiler
Page 53: JPoint 2016 - Etudes of DIY Java profiler

Распределённые приложения

Page 54: JPoint 2016 - Etudes of DIY Java profiler
Page 55: JPoint 2016 - Etudes of DIY Java profiler
Page 56: JPoint 2016 - Etudes of DIY Java profiler

Что уже есть?

> Разные $$$ APM-продукты: Dynatrace, AppDynamics

> Dapper

> ZipKin

http://research.google.com/pubs/pub36356.html

http://zipkin.io

NB! Требуется серверная часть

Page 57: JPoint 2016 - Etudes of DIY Java profiler
Page 58: JPoint 2016 - Etudes of DIY Java profiler

Request headers GET /supplements/ HTTP/1.1 X-XRebel-Event-Id: 6a61eb15-23f1-40ea-a30d-2f62d616a196 X-XRebel-Version: 3.0.2 Cache-Control: no-cache Pragma: no-cache User-Agent: Java/1.8.0_51 Host: localhost:8080

Response headers HTTP/1.1 200 OK Server: Apache-Coyote/1.1 X-XRebel-Event-Id: c5603281-a745-416e-8f27-92ba47c76b26 X-XRebel-Version: 3.0.2 X-xr-bookmark: 3197589b-bc9f-44ab-a2aa-12557a39b090 Content-Type: application/json;charset=ISO-8859-1

Page 59: JPoint 2016 - Etudes of DIY Java profiler
Page 60: JPoint 2016 - Etudes of DIY Java profiler

This slide is intentionally left blank

Page 61: JPoint 2016 - Etudes of DIY Java profiler
Page 62: JPoint 2016 - Etudes of DIY Java profiler

@antonarhipov

[email protected]

https://speakerdeck.com/antonarhipov

https://www.slideshare.net/arhan