RichTextFX.CodeArea에 css 를 적용하기 위해서는 다음과 같은 절차를 따른다.


먼저 프로젝트에 css파일을 생성하고 다음과 같이 작성한다.


.code-area {
   -fx-font-family: monospace;     //폰트
   -fx-font-size: 20;              // 폰트사이즈
   -fx-background-color: #272822;  //배경색
}

.code-area .text { //.code-area 와 .text 사이에 반드시 공백이 있어야 한다.
   -fx-fill: #FFFFFF;             // 문자 색상
}

.caret {
   -fx-stroke:white;              // 캐럿 색상
}

그리고 CodeArea 객체를 다음과 같이 생성하여 설정한다. 여기서 프로젝트의 이름은 Espy_Harper 라고 가정하고 css파일 이름은 caREPL.css 라고 가정한다.


caREPL = new CodeArea();
caREPL.setWrapText(true); // text-wrapping 설정
String stylesheet =
     Espy_Harper.class.getResource("caREPL.css").toExternalForm();
caREPL.getStylesheets().add(stylesheet);

주) 파란색은 프로젝트 이름, 빨간색은 css파일의 이름이다.


이렇게 하면 css에서 지정한 폰트, 배경색, 문자색, 캐럿 색상으로 설정된다.


c{espy}n{espy002}



Posted by 살레시오
,

  RichTextFX를 javaFX에 적용하기 위해서는 다음과 같은 절차를 따른다.


 먼저 github 페이지에 가서 richtextfx-fat-0.x.jar 라이브러리를 다운받는다. ( 반드시 -fat- 버전이어야 한다. 이것이 아니면 compile할 때 오류가 발생한다.) 이 포스트를 작성할 때 최신버전이 0.7-M2 였다. 따라서 richtextfx-fat-0.7-M2.jar 파일을 다운로드 받는다.


 그 다음 netbeans를 실행하여 javaFX FXML Application 을 생성한다. 이 프로젝트는 Scene Builder를 이용하여 UI를 구성할 수 있다.


생성된 프로젝트의 Libraries를 우클릭한 후 [Add Jar/Foler...] 를 선택하여 다운로드 받은 jar 파일을 추가시킨다.



FXMLDocController.java 파일에서 다음과 같이 AnchorPane 객체를 추가한다.


SceneBuilder 의 AnchorPane 에 이 id를 지정해 준다.


그 다음 AnchorPane 객체와 CodeArea 객체를 import 한다.


import javafx.scene.layout.AnchorPane;
import org.fxmisc.richtext.CodeArea;

그리고 FXMLDocController 클래스의 initialize() 메소드에 다음과 같이 추가한다.



실행 결과는 다음과 같다.



이것으로 javaFX 의 AnchorPane 에 CodeArea를 붙이는 것을 해 보았다.


c{espy}n{jv016}


Posted by 살레시오
,

ardpy 개발 동기

연구/Ardpy 2016. 5. 2. 19:06

  필자는 아두이노(Arduino)와 라즈베리파이(Raspberry pi)에 관심이 많다. 교육 현장에서 2000년대 초반부터 AVR로 마이크로컨트롤러(micro-controller)로 수업을 진행했었는데 간단해 보이는 8비트 마이크로컨트롤러도 그 개발 환경을 구축하고 매뉴얼을 참고하며 코딩을 수행한다는 것이 (학생들 입장에서는) 결코 수월하지 않다. 이러던 차에 아두이노가 개발되어 많은 사람들에게 사용되면서 이러한 고민을 해소시켜 주었다. 아두이노의 장점을 들라면 다음과 같은 것들이 있을 것이다.


  • PC와 USB로 연결만 하면 구축되는 간편한 개발 환경

  • 사용하기 쉬운 전용  IDE와 라이브러리

  • 폭넓은 사용자들이 쌓아 놓은 풍부한 개발 정보

  • 저가로 쉽게 구매 가능

  • 다양한 목적에 부합되는 보드들을 동일한 개발 환경을 사용


이에 반해서 단점을 꼽으라면 아무래도  C++에 대한 지식이 있어야 된다는 점인데 제대로 코딩하기 위한 학습 노력이 요구된다.


[그림 1] 다양한 아두이노 제품들


 이러한 장점들 때문에 현재는 소위 메이커(maker)들의 절대적인 지지를 얻고 있으며 필자의 마이크로컨트롤러 수업도 아두이노를 이용하여 진행하고 있는데 교육 효과가 이전보다 훨씬 더 낫다고 느끼고 있다.


 아두이노가 저변을 넓혀가는 와중에 사물인터넷(IoT)이 화두로 떠오르기 시작했는데 아두이노로 사물인터넷 제품을 구현하고자 하는 시도가 많이 일어났다. 보통은 wifi 쉴드를 아두이노에 얹어서 네트워크에 연결하여 아두이노에 연결된 기기들을 제어하는 방식인데 아두이노가 기본적으로 AVR 기반이다 보니 낮은 성능과 복잡한 코드로 인해서 이런 작업을 수행하기엔에는 수월하지가 않다. 사물인터넷을 겨냥한 아두이노 융(YUN)이라는 제품도 있으나 비싼 가격과 제한된 성능으로 인해서 별로 매력적이지 않다. 필자는 아두이노 자체만으로 네트워크 연결을 구현하거나 사물인터넷 제품을 만드는 것은 부적합하다고 생각하는 편이다.


 한편, 라즈베리파이는 영국에서 개발된 저가의 컴퓨터이며 가격이 35달러 이하의 제품군으로 구성되어 있다. 신용카드만한 크기에다 하드디스크를 대신할 메모리카드에 운영체제를 올려서 완벽한 리눅스 컴퓨터로 사용할 수 있는 획기적인 제품으로서 저가이면서도 상대적으로 높은 성능으로 인해서 크게 각광을 받고 있는 제품이다.


[그림 2] 라즈베리파이 컴퓨터


원래는 저개발국이나 후진국 아이들의 교육용으로 개발되었으나 일반인들의 관심도 많이 받아서 다양한 분야에서 널리 사용되고 있다. 기본적으로 리눅스 컴퓨터이기 때문에 C/C++은 물론이고 파이썬(python), 자바스크립트(node.js), 자바(java) 등 각종 언어들을 자유롭게 사용할 수 있으며 웹서버를 구축하거나 하는데 전혀 제약이 없다. 하지만 헤더핀의 기능이 제한적이기 때문에 여기에 각종 센서나 작동기 등을 연결하여 제어하는 데에는 제약이 따른다. 센서나 모터와 같은 기기를 제어하는 기능으로만 따져 본다면 아두이노보다도 더 비효율적이다.

 

 이러한 배경으로 인해서 필자는 라즈베리파이에 아두이노를 연결하여 파이썬으로 제어할 수 있는 환경을 구축하는데 관심이 많았다. 시중에 이러한 제품들이 몇몇 있기는 하지만 필자의 요구를 충족시키지는 못하였다. 그래서 직접 라이브러리를 개발하기 시작하였고 ardpy라는 이름을 붙여서 배포하고 있다. 이것을 오픈소스로 공개한 이유는 다양한 사용자들과 같이 고민하면서 발전시키면서 좀 더 많은 사람들에게 도움이 되는 라이브러리가 되기를 기대하기 때문이다.

{gdoc}



'연구 > Ardpy' 카테고리의 다른 글

Ardpy API reference  (0) 2016.10.23
Ardpy examples  (0) 2016.10.23
Ardpy setup  (0) 2016.10.23
About Ardpy  (0) 2016.10.23
파이썬의 smbus 패키지를 이용하여 i2c 통신하기  (0) 2015.11.25
Posted by 살레시오
,

 며칠간 라즈베리파이와 아두이노간 i2c 통신을 위해서 python3-smbus 라이브러리를 사용하였는데 여기에 몇 가지를 기록한다. 설치하는 법은 다음과 같다.


sudo apt-get update (먼저 반드시 수행해야 한다.)
sudo apt-get install python3-smbus


라즈베리파이 B+와 ver. 2 에는 i2c 포트가 두 개가 있는데 각각 i2c-0, i2c-1 이다. 만약 1번을 사용한다면 다음과 같이 초기화 할 수 있다.


import smbus
dev = smbus.SMBus(1)


smbus 레퍼런스에 보면 cmd (command) 파라메터가 있는데 이것은 단순히 보내는 데이터의 맨 앞에 먼저 보내지는 데이터이다. 보통 슬레이브는 이것을 보고 어떤 명령인지(또는 어떤 데이터가 따르는지)를 판별한다.


 그리고 함수들 중에 i2c 가 붙은 것들이 있는데 이것이 더 효율적이라고 한다.


read_i2c_block_data(addr, cmd [, length] )
write_i2c_block_data(addr, cmd, lst)


write_2ic_block_data()함수는 다음과 같이 데이터를 전송한다.


cmd

lst[0]

lst[1]

...

lst[-1]


read_2ic_block_data()함수는 cmd 한 바이트를 먼저 전송한 후 데이터를 읽어온다.


smbus에는 write_block_data()라는 함수도 있는데 write_i2c_block_data()와의 차이점은 전송되는 데이터가 하나 다르다는 것이다. 두 번째 바이트에 데이터의 길이를 전송한다.



cmd

size

lst[0]

lst[1]

...

lst[-1]


만약 i2c 통신을 사용하는 것이라면 read_i2c_block_data(), write_i2c_block_data() 함수를 사용하는 것이 좋다.





'연구 > Ardpy' 카테고리의 다른 글

Ardpy API reference  (0) 2016.10.23
Ardpy examples  (0) 2016.10.23
Ardpy setup  (0) 2016.10.23
About Ardpy  (0) 2016.10.23
ardpy 개발 동기  (0) 2016.05.02
Posted by 살레시오
,

 자바에서 같은 바이트 수를 가지는 long 형과 double 형의 계산 속도 차이가 거의 나지 않는다는 것을 며칠 삽질한 결과 알아냈다. 정수형 연산이 훨씬 빠를거라고 지레 짐작했던 것이 보기 좋게 빗나간 것이다. 왜 그런지는 아직도 잘 모르겠다.


 실험 방법은 long형으로 고정 소숫점 숫자 연산을 간단하게 구현하고,  예를 들어 처음 여섯 자리를 소수 자리로 간주한다면 (즉 1,000,000이 1.0임)


  • 덧셈,뺄셈은 그대로 수행한다.

  • 두 수의 곱셈은 각각을 1000으로 먼저 나누고 곱셈

  • 두 수의 나눗셈은 나눈 결과에 1000000을 곱한다.


이렇게 연산을 하게끔 구성해 놓고 거의 동일한 연산을 수행해 보았더니 오히려 double형을 사용한 것이 더 빠르다는 결과가 나왔다.


 이제는 C++로 코딩해도 똑같은 결과가 나올까 궁금하다.





Posted by 살레시오
,

 자바에는 기본형에 대응하는 wrapper 클래스가 있다. 예를 들면 int 는 Interger, double은 Double 이다. 제네릭 클래스에는 기본형을 사용할 수 없으므로 이 wrapper 클래스를 사용해야 한다.


 그런데 기본형과 wrapper클래스의 성능 차이가 궁금해서 다음과 같은 간단한 프로그래으로 시험해 보았다.


package wrappertest01;

public class WrapperTest01 {
   public static long iter = 1000000L;
   public static int id;
   public static double db, dSum;
   public static long le, ls;

   public static Double[] dArr;
   public static double[] darr;

   public static void main(String[] args) {
       dArr = new Double[100];
       for(int k=0;k<100;k++) dArr[k]=new Double(1.0);
       
       darr = new double[100];
       //for(int k=0;k<100;k++) dArr[k]=new Double(1.0);

       lst = new ArrayList<>();
       for(int k=0;k<100;k++) lst.add(new Double(1.0));

       doIt();
       doIt();
       doIt();
   }
   
   public static void doIt() {
       double dSum1 =0, dSum2=0;
       long id;
       for(int n=0;n<10;n++) {
           
           ls = System.nanoTime();
           for(int k=0;k<iter;k++)
               for(int x=0;x<100;x++) {
                   db=dArr[x];
                   dArr[x]=db;
               }
           le = System.nanoTime();
           dSum1 += (le-ls)/1e9;

           ls = System.nanoTime();
           for(int k=0;k<iter;k++)
               for(int x=0;x<100;x++) {
                   db=darr[x];
                   darr[x]=db;
               }
           le = System.nanoTime();
           dSum2 += (le-ls)/1e9;
        }
       System.out.println("Double Arr: "+dSum1/10+ "sec.");
       System.out.println("double arr: "+dSum2/10+ "sec.");
   }
}


단순히 배열을 만들어서 배열의 요소를 읽고 쓰는 동작만 1억x100 번을 10번 실행한 평균 시간을 구한 것이다. wrapper클래스가 오버헤드가 있으므로 당연히 다소 느릴거라는 예상 했다. 결과는 기본형의 배열이 훨씬 더 동작이 빠르다는 것이다.



수치상으로는 거의 40배 빠르다.  이 실험만으로 전반적인 성능 비교는 물론 안되겠지만 성능이 중요한 곳에서는 왠만하면 기본형을 사용해야하며  제네릭 클래스를 구현하는 경우와 같이 어쩔 수 없는 경우에만 wrapper 클래스를 사용해야 한다는 언급이 맞는 것 같다.




Posted by 살레시오
,

 요즈음 소위 딥러닝(deep learning) 분야가 주목을 받고 있는데 여기에는 수학적인 신경망(neural network)이 사용된다. 신경망은 뭔가 복잡한 것일거라고 오해하기 쉬운데 사실 그 구조는 상당히 단순하다. 여기에서는 신경망을 구성하는 가장 기본적인 단위인 뉴런(neuron)에 대해서 알아보도록 하겠다.


 뉴런은 입력 신호를 받아서 새로운 신호를 발생시키는 신경망의 기본적인 처리 단위이다. 다음 [그림 1]에 개념도를 도시하였다.


[그림 1] 뉴런의 개념도


이 그림에서 보면 원으로 도시된 뉴런은 입력들(이것은 또 다른 뉴런들의 출력들이다)을 받아서 그것을 이용한 어떤 계산을 수행한 후에 출력으로 내 보낸다. 이 출력이 다시 또 다른 뉴런의 입력으로 사용되는 것이다. 이러한 기본적인 수많은 뉴런들이 연결되어 신경망을 구성한다. 여기에서 입력과 출력은 어떤 숫자 값인데 뉴런은 이것을 그대로 받아들이지 않고 가중치(weight)라는 수치를 곱해서 받아들인다.


 이것을 수식으로 표현해 보자. (고등학생 정도의 수학 지식이 있다면 이해할 정도로 간단하다.) 뉴런으로의 입력을 x1, x2, xn 이라고 표기하고 가중치를 w1, w2, …, wn 이라고 표기한다면 뉴런으로의 총 입력 v는 다음과 같이 (입력x가중치) 들의 합으로 표현된다.



이 v를 바이어스(bias) b와 더한 후 활성화 함수(activation function)에 인가시켜 얻은 출력 값이 뉴런의 출력 y가 된다. 활성화 함수를 φ(⋅)로 표기한다면 다음과 같이 뉴런의 출력이 구해진다.



활성화 함수는 여러 종류가 있지만 다음과 같은 시그모이드(sigmoid) 함수가 주로 사용된다.



이 함수는 다음 그림에서 보듯이 양 끝단이 0과 1로 수렴하는 아주 간단한 형태를 갖는 비선형 함수이다. 이 함수의 출력이 0에 가까우면 뉴런은 비활성화되었다고 하며 1에 가까울수록 활성화 되었다고 한다. 여기서 σ는 기울기값으로서 이 값이 클수록 수렴하는 속도가 빨라진다. (즉 원점에서의 기울기가 커진다.)


[그림 2] 시그모이드 함수(실선이 σ=0, 빨간 점선이 σ=2, 녹색 점선이 σ=0.5인 경우)


그리고  바이이스(bias)는 시그모이드 함수를 x축에 대해서 이동시키는 역할을 한다.


[그림 3] (a) b=5 인 경우, (b) b=-5 인 경우


이 바이어스는 어떤 수준의 입력 값에서 뉴런이 활성화될지를 결정하는 인자이다. 보통은 계산식의 편의상 입력이 항상 1인 신호선의 가중치로 간주된다.


[그림 4] 바이어스를 n+1번째 가중치로 간주


즉, n+1번 째 입력을 항상 1로 놓고 그 가중치를 바이어스로 간주하는 것이다. 따라서 [그림 1]과 [그림 4]는 완전히 동일한 뉴런이다. 이제 다음과 같이 가중치 벡터와 입력 벡터를 정의하자.



그러면 뉴런의 출력 y는 다음과 같이 구할 수 있다.



 이 내용을 바탕으로 신경망 중에서 비교적 간단한 구조를 가지는 전방향 신경망(feedforward neural network)이 기본적인 뉴런들로부터 어떻게 구성되는지 다음 포스트에서 알아보도록 하자.


Posted by 살레시오
,

 자바 제네릭(generic) 프로그래밍에 대해서 실습하던 중에 제네닉 타입의 단순 배열을 생성하는데 미묘하게 안 되는게 있기도 해서 구글링을 해 보았더니 배열을 쓰지말고 웬만하면 List<>를 사용하는게 여러 모로 편하다고 하는 의견들이 많았다. 성능차도 별로 없다고 해서 직접 간단한 프로그램으로 실행 시간을 측정 해보기로 하였다..


 초기에 크기는 배열과 리스트 모두 정해졌다고 가정하고 단순 읽고 쓰는 속도만 비교하였다.


package listtestj01;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ListTestj01 {
   public static    long iter = 1000000L;
   public static    int id;
   public static    double db, dSum;
   public static long le, ls;

   public static Double[] dArr;
   public static List<Double> lst;

   public static void main(String[] args) {
       dArr = new Double[100];
       for(int k=0;k<100;k++) dArr[k]=new Double(1.0);
       
       lst = new ArrayList<>();
       for(int k=0;k<100;k++) lst.add(new Double(1.0));

       doIt();
       doIt();
       doIt();
   }
   
   public static void doIt() {
       double dSum1 =0, dSum2=0;
       long id;
       for(int n=0;n<10;n++) {
           
           ls = System.nanoTime();
           for(int k=0;k<iter;k++)
               for(int x=0;x<100;x++) {
                   db=dArr[x];
                   dArr[x]=db;
               }
           le = System.nanoTime();
           dSum1 += (le-ls)/1e9;

           ls = System.nanoTime();
           for(int k=0;k<iter;k++)
               for(int x=0;x<100;x++) {
                   db = lst.get(x);
                   lst.set(x, db);
               }
           le = System.nanoTime();
           dSum2 += (le-ls)/1e9;
        }
       System.out.println("Double Arr: "+dSum1/10+ "sec.");
       System.out.println("List<Doub>: "+dSum2/10+ "sec.");
   }
}


이 예에서 붉은 색으로 마킹된 부분을 보면 단순히 배열 요소에서 하나를 읽고 다시 하나를 쓰고를 1억번 반복하고 이것을 다시 열 번 반복해서 평균시간을 구해서 비교하는 것이다. 내 노트북(윈764비트)에서 다음과 같은 결과를 얻었다.



세 번 반복해서 재 보아도 모두 단순 배열쪽이 두 배 이상 속도가 빨랐다. 리스트가 느리더라도 그 차이가 많이 안 나길 기대했는데 이것으로 적어도 대규모 연산이 있는 곳에서는 단순 배열이 속도 면에서 훨씬 더 낫다는 (당연한) 결과를 얻었다.




Posted by 살레시오
,

 이전 포스트에서 C++의 double형 연산과 long long int 형 연산 속도를 측정한 적이 있는데 똑같은 일을 JAVA에서 수행하고 시간을 측정해 보았다. (JAVA는 long형이 64비트 정수형이고 c++의 long long int 형에 대응한다.)


package javaapplication5;
public class JavaApplication5 {
   public static void main(String[] args) {
       long iter = 100000000; //1억번
       
       System.out.println("Start");
       long ls, le;
       double da=12.3, db=45.6;
       ls = System.nanoTime();
       for(long k=0;k<iter;k++) da *= db;
       le = System.nanoTime();
       double tm1 = (le-ls)/1e9;
       System.out.println("elapsed time 1 = "+tm1);
       
       long la=2, lb=2;
       ls = System.nanoTime();
       for(long k=0;k<iter;k++) la *= lb;
       le = System.nanoTime();
       double tm2 = (le-ls)/1e9;
       System.out.println("elapsed time 2 = "+tm2);
       
       System.out.println(tm1/tm2 + " times faster.");
   }   
}


수행 결과는 의외였다.



JAVA로 실행한 결과가 훨씬 더 수행시간이 짧다. 당연히 JAVA가 수행시간이 더 걸릴 줄 알았는데 아니었다. 아래가 g++ 로 컴파일하여 수행시간 결과다. 똑 같은 일은 c++ 은 14.591초, 자바는 0.357초 걸렸다.



윈도에서는 g++ 성능이 낮고 최적화를 전혀 하지 않았음을 감안하더라도 성능 차이가 이렇게 날 줄 몰랐다. 자바도 중간어를 JIT 컴파일해서 실행하므로 c++과의 실행 속도 차이가 그다지 크지 않다고 알고는 있었는데 직접 해보니 사실이었다. 한 가지 자바에서는 정수형과 실수형의 연산 속도 차이가 c++에서만큼 크지 않다는 것도 알 수 있었다.


 그렇다면 c++을 고집할 필요가 없는 것 아닌가 싶다. JAVA를 이용하는 것이 훨씬 더 편하고 쉽다. 더군다나 CUDA4J 같은 GPU를 사용할 수 있는 라이브러리도 있으니 성능 향상이 필요할 때는 이것을 사용하면 되지 않을까.


 실수 연산과 정수 연산의 시간 차이를 알아보기 위해서 자바로 실험을 조금 더 해 보았다.


package javaapplication5;
import static java.lang.Math.exp;
public class JavaApplication5 {
   public static void main(String[] args) {
       long iter = 100000000;
       
       System.out.println("Start");
       long ls, le;
       double da=12.3, db=45.6, dc=0, dd=0;
       ls = System.nanoTime();
       for(long k=0;k<iter;k++) {
           dd = da*db;
           dc = exp(dd);
           da += 1e-8;
       }
       le = System.nanoTime();
       //System.out.println(da + ", " + dc);
       double tm1 = (le-ls)/1e9;
       System.out.println("elapsed time 1 = "+tm1);
       
       long la=2, lb=2, lc = 0, ld=0;
       long[] larr = new long[2000];
       
       ls = System.nanoTime();
       for(long k=0;k<iter;k++) {
           long ll = k%2000;
           ld = la*lb;
           lc = larr[(int)ll];
           la++;
       }
       le = System.nanoTime();
       double tm2 = (le-ls)/1e9;
       System.out.println("elapsed time 2 = "+tm2);
       
       System.out.println(tm1/tm2 + " times faster.");
   }
}


이 연산에서는 30배 정도 속도에서 차이가 난다.


Posted by 살레시오
,

 C/C++의 double형은 수치 해석 분야의 기본적인 자료형이다. float형은 정밀도가 떨어지므로 주로 이 자료형이 사용되며 GPU도 이 자료형의 계산을 고속으로 수행하도록 설계된 것으로 알려져 있다.


 그런데 long long int 형은 8바이트 정수형 자료형인데 단순 곱셈의 경우 수행 속도에 시간 차가 많이 난다. 다음과 같은 간단한 c++ 프로그램을 내 노트북에서 실행시켜서 확인해 보았다.


#include  <stdio.h>
#include <time.h>
#define ITER 100000000 //1억번
int main() {
clock_t clkStart, clkEnd;
double dt1, dt2;
clkStart = clock();
double da = 10.1, db=11.2;
for (long k=0; k<ITER; k++) {da*=db;}
clkEnd = clock();
printf("%.3f sec\n", dt1=((double)clkEnd-clkStart)/CLOCKS_PER_SEC);

clkStart = clock();
long long lla = 101, llb=112;
for (long k=0; k<ITER; k++) {lla*=llb;}
clkEnd = clock();
printf("%.3f sec\n", dt2=((double)clkEnd-clkStart)/CLOCKS_PER_SEC);
printf("%.2f percent", dt2/dt1*100);
}


이 프로그램은 단순히 double형 두 개를 곱셈 후 대입하는 동작 1억 번과 long long 형 덧셈 후 대입하는 동작 1억 번의 수행 시간을 구하는 것이다.  결과는 다음과 같이 후자가 전자 대비 3%의 시간밖에 걸리지 않았다.



 문제는 GPU 기반의 라이브러리인 CUDA에서 정수형 연산도 실수형 만큼 효율적인가 하는 것이다. 하지만 그러지 않더라도 나름 의미가 있지 않을까.





Posted by 살레시오
,