2017년 5월 2일 화요일

임베디드 엔지니어의 IoT 이야기 #1. Why NOT Java for embedded system?

실리콘밸리 생존기는 소재 고갈로 시즌 2를 종료(?) 하기로 했다.

일하기도 바쁘고, 나에게는 더 중요한 목표에 집중을 해야 할 시기가 온 듯 하다.
대신 다시 엔지니어의 본분으로 돌아와 중간 중간 머릿속에 떠오르는 기술 이야기나 기록하기 위해 끄적이고자 한다.


주제도 개념도 철학도 없는 일기장이 될거 같긴 하다만..




기.술.적.으.로 우리 회사에서 개발하는 제품은 IoT gadget 이라고 할 수 있다.
그래서 IoT 트렌드에서 임베디드 소프트웨어에 대한 이야기를 주로 할 것 같다.



요즘은 프론트니 백엔드니 리액트니 앵귤러니 인공지능이니 하는 세간의 트렌드와 이야깃 거리는 차고 넘치는데 임베디드 엔지니어들은 유독 조용하다.
(내가 봐도 이 쪽 사람들은 참 Nerdy 하다.)

임베디드 세계는 High level 소프트웨어 엔지니어들과는 다른 세상에서 다른 사고 방식으로 사물을 바라보기 때문에 어떤 고민을 하고 어떤 시각으로 문제를 바라 보는지,

그 미묘한 온도 차이에 대한 이야기들도 하고 싶어졌다.

사실 이러한 고민들은 팀 어웨어 내에서 다른 분야 사람들과의 대화에서 그동안 느껴온 것들에 대한 이야기이다.
어느덧 입사한지 1년이 다 되어 가고, 그 동안 내가 부족했던 것이 너무 많음을 느끼고 하루하루를 성장하며 살고 있다는 사실이 고맙기만 하다.



서론이 길었는데, 오늘 이야기는 자바 이야기다.

우리 회사의 백엔드 시스템은 대부분 스칼라로 갈아타는 추세이다. 초창기 생산성을 위해 NodeJS 나 아마존 AWS 람다 등을 활용했었던듯 하지만, 이제 트렌드는 Scala 언어, 그리고 Actor 모델이라는 프로그래밍 패러다임을 활용한 scale out이 용이한 Akka 프레임 워크로 핵심 서비스로 헤쳐 모이는 중이다.
(서당개 삼년 풍월이라고.. 나름 이정도 개념은 어깨너머로 이해했다)

하루는 회사의 백엔드 엔지니어 분이 나에게 와서 새로 개발하는 제품에 리눅스를 사용하면 그냥 자바나 스칼라로 구현하는건 어떻겠냐고 질문을 해 왔다.
(목적은 자기도 디바이스 개발하고 싶다고..ㅋㅋ)

그 질문에 대한 대답은 글쎄요..


나는 임베디드 환경에서 Java가 인기 없는 여러 가지 이유를 나열했는데


1. Java 의 고질적인 메모리 문제는 제한적인 컴퓨팅 자원에서 엄청난 리스크다. GC가 언제 발동할지도 모르는 카오스를 임베디드 엔지니어들은 혐오한다. 정리할거면 정리하던가 질질 그는 메모리 밀당 따위.. (맺고 끊음이 확실한 C/C++ 가 여전히 인기있는 이유다.)

2. JVM이 표준 I/O, filesystem, network I/O 를 제외한 다양한 하드웨어 I/O에 대한 완벽한 지원을 하기 힘들 것이라는 예상(시리얼 통신 정도야 문제 없겠지만 i2c, spi, ADC 등의 I/O를 저수준 언어가 아닌 VM을 통해 접근을 하면 어떤 예외 상황이 벌어질 지 예측하기가 어렵다. 예를 들면 i2c 통신 같은 경우 조금이라도 단말간 통신에 timing 이 어긋나 버리면 통신 자체가 무너져 버린다.)

3. NodeJS 가 임베디드 환경에서 인기가 많다는 점. NodeJS engine 이 ARM 아키텍쳐 환경에 지원이 잘 되고 많은 package 들이 C++ addon 코드로 지원이 잘 되어서 binary가 없으면 그자리에서 바로 binary object 로 빌드를 해준다.

4. 팀원들 중에 Java 를 안써본 사람이 있다는 점. (그래봤자 한명이지만.. 이 사람은 NodeJS나 함수형 프로그래밍도 굉장히 싫어한다)

등을 설명했다.

그래도 NodeJS 같은 인터프리터 언어보단 Java 가 빠르지 않느냐 질문에는
자바 생산성이나 C++ 생산성이나 비슷해서 차라리 C++ 쓸래요...



라고 대답하곤 했다.












그런데 말입니다.  정말 자바와 JVM을 임베디드 환경에서 못쓸까요?




아니 못쓰진 않는다. 왜 안쓸까?
요즘 스칼라를 혼자 공부하고 있는데 언어가 이렇게 섹시하게 발전할 수 있다는 감탄과 함께 활용해 보고 싶은 마음은 굴뚝같다.  (스칼라로 짜면 백엔드 엔지니어들을 디바이스 개발에 부려먹을수는 있겠다)

그리고 나도 플랫폼 의존성 없는 JVM 세상에서 마음껏 코딩 하고 싶은 사람이다.
임베디드 엔지니어들은 항상 코드 한줄에도 시스템 특성을 생각하며 짠다.





뭐 그래서 그냥 성의없는 성능 테스트를 해봤다.

일단 메모리는 어차피 많이 먹을거 아니까 건너 뛰고...



50,000,000 번의 루프르 돌며 integer 변수에 +1 을 하는 코드를 짜서 초간단 실행시간 테스트를 수행.
(그냥 느리다 빠르다만 보기 위한 성능 평가 이므로 오차 범위 따위 가볍게 무시하자.)

Java 와 NodeJS 를 다른 플랫폼에서 비교를 해본 결과는.. 아래와 같다.

> x86_64 / Mac OS
Java : 1~2msec
Node JS : 35~38msec

> ARM v7 hf / Fedora Linux
Java : 5000~ msec 응?
Node JS : 240~250msec




아, 이건 예상치 못한 결과다... 너무 이상하지 않은가?



ARM 환경에서 Java 프로그램이 유난히 느린 이유는 임베디드 환경에서 구동한 JVM 이 OpenJDK 인데 OpenJDK 의 Zero VM  은 interpreted mode 만 지원하기 때문이다. (최소한 ARM 아키텍쳐용 OpenJDK 는 그렇다고 알고 있다)


나의 맥북 프로에 설치된 JDK
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

ARM 리눅스 환경에 설치된 OpenJDK
openjdk version "1.8.0_102"
OpenJDK Runtime Environment (build 1.8.0_102-b14)
OpenJDK Zero VM (build 25.102-b14, interpreted mode)


원래 자바는 코드를 컴파일해서 나온 byte code 라는 중간 언어(사실 컴파일러에서 중간 언어를 지칭하는 것은 따로 있지만 여기선 편의상) 코드를 JVM이 런타임에 기계어로 컴파일 하는 과정을 거친다.
마치 파이선이 py 스크립트 텍스트 코드를 pyc 라는 바이너리로 컴파일하는 것과 같다.

그런데 Interpreted mode 는 자바를 컴파일해서 나온 byte code 를 순수하게 execution time 에 읽으며 실행하는 모드인데 이쯤되면 그냥 인터프리터 언어랑 다를게 없다.

내 맥북 프로에서 자바를 인터프리터 모드로 동작하게 하면 약 350 msec 정도로 NodeJS 보다 성능이 형편없게 나온다.

$ java -Xint Test
value : 50000000
exe time :349



Oracle JVM 은 ARM 아키텍쳐용에 mixed mode를 지원하는걸로 아는데 돈을 내야 한다. 이는 생산 단가에 치명적인 단점이다.


오라클 임베디드 용 단가표

https://blogs.oracle.com/jtc/entry/java_embedded_pricing_publicly_available


그래서 여전히 임베디드 환경에서는 C/C++ 이 각광받고 있고, C/C++의 단점을 보완하기 위해 NodeJS나 파이선 같은 스크립트 언어를 같이 쓰고 있다. (이 이야기는 나중에 또 따로 할 것이다)

고로 아직 자바는 임베디드 환경에서 쓸게 못된다는 결론.


자바도 JNI 있어요! 라고 물으신다면
아까도 말했지만 C++ 생산성이나 Java 생산성이나 그게 그거다.

성능과 안정성을 보장해야 하고 코드 검증 커버리지를 신경써야 하는 핵심 코어는 C++ 로 구현하고 NodeJS addon 으로 묶어 어플리케이션 단에서 생산성 높은 javascript 코딩을 하는 전략이 임베디드 리눅스 개발 환경에서 인기가 있는 이유이다.



오라클이 임베디드 환경에서만 과금을 먹이는 라이센싱 문제는 과거로 거슬러 올라가면 그 이야기가 긴데, 자세한 설명은 생.략.한.다.


예전에 라즈베리 파이에서 Alexa Voice Service 데모를 실행한 적이 있었는데 클라이언트가 Java 로 구현되어 있었다. 끔찍한 실행시간에 경악을 금치 못한 적이 있다.

그 때는 그냥 GUI가 있고 프로그램을 너무 무겁게 짰겠거니... 하고 대수롭지 않게 넘겼는데 문제의 원인은 JVM 에 있었다.

문제의 그 코드:
https://github.com/alexa/alexa-avs-sample-app/wiki/Raspberry-Pi

아마 AVS 팀이 임베디드는 해본적 없고, 라즈베리파이에 JVM은 지원하고.. 하니 그렇게 짜놓고 공개한듯 하다. 어차피 샘플 코드고 이거보고 니들 알아서 해라~가 목적이었을테니..




간혹 사람들이 오해하는데, 나는 자바를 싫어하지 않는다.
다만 시스템 설계에 맞지 않는 언어라서 못쓰는 것 뿐