월별 글 목록: 2021년 7월월

‘길벗과 함께’ 출시

길벗체로 이미지를 쉽게 만들 수 있는 서비스 ‘길벗과 함께(https://gilbeot.osg.kr)‘를 지난 주에 출시 하였다.

길벗체로 작성한 문구를 프로필 사진으로 활용하고 싶은 분들이 많다는 사실을 알고서, 어떻게 하면 이용자들이 쉽게 이용할 수 있을지 몇 달 동안 고민하고 있었다.

두어 달 전에 기술적 가능성을 살펴봤는데, 그때까지 확인한 방법으로는 컬러폰트를 사용할 수 없었다. 그런데, 최근에 작업했던 내용들과 관련해서 지난 주에 갑자기 아이디어가 떠올랐다. 그래서 확인해 본 결과 가능성이 있음을 확인하고, 작업에 착수하였다.

대략 3일간 전체적인 개발을 마치고, 이틀 정도 디버깅을 마친 후 출시하였다. 물론 문제가 있다. 사람들은 예상치 못했던 이모티콘을 사용하고 싶어했고, 준비되지 않은 글자를 사용하는 경우 오류가 발생하는데, 사용자가 인지할 수 있는 UI/UX가 준비되어 있지 않아서, 이용자들이 아직도 불편을 겪고 있다. 이에 대해 미안한 마음이 든다. 혹시 길벗과 함께 이용자가 이 글을 읽는다면 이해를 부탁드린다.

빨리 개선하고 싶은데 개인 프로젝트로 하는 것이다보니 아무래도 완전히 여기에만 집중할 수 없음이 아쉽다. 그래도 짬짬이 시간을 내어서 조금씩 개선해가고 있다. 매일 방문하고 이용해 주시는 분들이 계셔서 ‘내가 뻘짓으로 시간을 버리지는 않았구나’ 싶은 생각이 들기도 한다.

앞으로도 필요한 서비스들을 틈틈이 만들어 가려고 한다.

numba guvectorize 활용하기

numba의 guvectorize를 사용하면 array 형태의 값 처리의 속도를 매우 빠르게 할 수 있다.

우선 실행 결과를 먼저 보겠다. aaa는 guvectorize를 사용해서 연산한 경우이고, bbb는 그냥 python 코드로 실행한 결과이다. ccc는 numpy의 벡터 연산을 실시한 결과다. guvectorize를 적용했을 때 단순 for loop를 사용했을 때에 비해 약 300배 가까이 빠른 실행을 보였다. numpy의 벡터 연산을 활용하면 for loop을 사용했을 때 보다 1400배 가까이 빠르게 실행되었다.

aaa: elapsed=0.0275056362s
bbb: elapsed=9.2325780392s
ccc: elapsed=0.0066127777s
>>> 9.2325780392 / 0.0275056362
335.6613158142476
>>> 9.2325780392 / 0.0066127777
1396.1724494685493

결론부터 말하자면, Array의 단순 연산은 numpy의 벡터연산이 가장 빠르다. 특정 로직에 따른 연산이 필요할 땐 guvectorize를 활용하자.

guvectorize의 사용법을 간단히 살펴보자. guvectorize의 경우에는 값을 return하지 않는다. 대신에 파라미터에 대한 선언을 정확히 해 주면 된다.

@guvectorize(['void(int64[:], int64[:])'], '(n) -> (n)')
def function_name(x, y):

순서대로 int64[:], int64[:]는 뒤의 (n) -> (n)과 매칭된다.

Array가 아닌 단순 값을 파라미터로 추가하는 경우에는 아래와 같이 처리하면 된다. 데이터 타입의 정의 개수와 입력, 출력 파라미터의 형식, 그리고 함수의 정의까지 모두 일치해야 한다.

@guvectorize(['void(int64[:], boolean, int64[:])'], '(n),() -> (n)')
def function_name(x, twist, y):

아래에는 위의 결과를 만든 소스 코드를 붙인다.

import time
import numpy as np
from numba import guvectorize


def timer(func):
    def measure(*args, **kwargs):
        begin = time.time()
        val = func(*args, **kwargs)
        end = time.time()
        print(f">> {func.__name__}: elapsed={'%.10f' % (end - begin)}s")
        return val

    return measure

@timer
@guvectorize(['void(int64[:], int64[:])'], '(n) -> (n)')
def aaa(x, y):
    for i, val in enumerate(x):
        y[i] = val + 3 * 3 / 5 * 1.23 / 6.53

@timer
def bbb(x):
    y = np.empty([len(x), 1])
    for i, val in enumerate(x):
        y[i] = (val + 3 * 3 / 5 * 1.23 / 6.53)
    return y

@timer
def ccc(x):
    return x + 3 * 3 / 5 * 1.23 / 6.53

a = np.int32(np.round(np.random.rand(5000000) * 1000))
out = aaa(a)
out = bbb(a)
out = ccc(a)