라이브러리의 사용

이 단원에서는 GSL의 설치와 환경 구성, GSL을 사용한 프로그램을 어떻게 컴파일 하는지 그리고 규약들에 대해 기술합니다.

예제 프로그램

다음 프로그램 코드는 베셀 함수 \(J_0(x)\) 일 때의 값을 구하는 프로그램 입니다.

#include <stdio.h>
#include <gsl/gsl_sf_bessel.h>

int
main (void)
{
  double x = 5.0;
  double y = gsl_sf_bessel_J0 (x);
  printf ("J0(%g) = %.18e\n", x, y);
  return 0;
}

결과는 다음과 같습니다. 이는 배정밀도의 정확도를 가집니다 1 .

J0(5) = -1.775967713143382642e-01

컴파일과 링킹

이 라이브러리의 헤더 파일들은 gsl 디렉토리 내에 존재합니다. 라이브러리를 사용하려면 전처리기가 gsl/ 디렉토리를 포함하도록 다음과 같이 작성해야합니다.

#include <gsl/gsl_math.h>

만약 표준 경로에 디렉토리가 있지 않다면 전처리기에 옵션으로 이 위치를 넘겨주어야 합니다. gsl 의 기본 디렉토리는 /usr/local/include/gsl 입니다. 전 단락의 프로그램 코드파일을 example.c 로 저장했다면 gcc 에서 다음과 같이 쓸 수 있습니다.

$gcc -Wall -I/usr/local/include -c example.c

결과물은 example.o 파일로 나올 것입니다. gcc 의 include 경로는 기본적으로 /usr/local/include 이므로 위 명령어의 -I 옵션은 없이 쓰는 것과 차이가 없습니다. 다른 경로에 설치된 라이브러리를 사용할 경우 -I 옵션으로 추가해주면 됩니다.

라이브러리 링킹

라이브러리는 libgsl.a 라는 한개의 파일로 설치되어있습니다. 공유 버전의 경우 libgsl.so 로 같이 설치되었있습니다. 이 파일들의 기본 위치는 /usr/local/lib 입니다. 이 디렉토리가 링커의 표준 검색 경로에 포함되어 있지 않다면, 커멘드 라인 명령어로 설정해 주어야 합니다.

$gcc -L/usr/local/lib example.o -lgsl -lgslcblas -lm

gcc 의 기본 검색 경로는 /usr/local/lib 입니다. 따라서 GSL이 기본 경로에 설치되어 있다면, -L 옵션은 무시 가능합니다.

-lm 옵션은 시스템의 수학 라이브러리를 링크합니다. 다른 시스템에서는 필요 없을 수도 있습니다 2 .

GNU C Complier와 관련 프로그램의 튜토리얼을 보고 싶다면 An Introduction to GCC (ISBN:0954161793)를 참고 하시기를 바랍니다.

참고

해당 GCC 문서는 2004년도에 작성된 문서입니다. 큰 틀은 비슷하지만 GCC 또한 18여년 동안 많은 발전이 있어왔습니다. 최신 GCC 사용 설명서와 문서는 GCC 온라인 문서 의 문서들을 참고할 수 있습니다(*).

대체 BLAS 라이브러리 링킹

다음의 명령어는 다른 BLAS라이브러리(libcblas.a)를 어떻게 프로그램과 링크하는 지 보여줍니다.

$gcc example.o -lgsl -lcblas -lm

최고의 효율을 위해서는 -lcblas 를 통해 최적화된 특정 플랫폼을 위한 CBLAS 라이브러리를 사용해야합니다. 이때, 해당 라이브러리는 반드시 CBLAS 표준을 준수해야 합니다. ATLAS 패키지는 고효율의 BLAS 라이브러리를 CBLAS 인터페이스를 통해 제공합니다. 이 패키지는 자유 소프트웨어이고 빠른 벡터와 매트릭스 연산이 필요할 때, 설치되어있어야 합니다. 다음 명령줄은 ATLAS 라이브러리와 CBLAS 인터페이스를 링크합니다.

$gcc example.o -lgsl -lcblas -latlas -lm

만약 ATLAS 라이브러리가 비표준 경로에 설치되어있다면, 전 단계들에서 보였다시피 -L 옵션으로 검색 경로에 추가시켜주어야 합니다.

BLAS에 대한 더 자세한 정보를 알고 싶다면, BLAS 지원 을 참고할 수 있습니다.

공유 라이브러리

프로그램이 공유 라이브러리를 사용하려면, 운영체제가 대응되는 .so 파일을 구동 과정에서 제공해야 합니다. 만약 해당 라이브러리를 찾을 수 없다면 다음의 오류 메세지가 나옵니다.

$./a.out
./a.out: error while loadinng shared libraries:
libgsl.so.0: canot open shared object file: No such file or directory

이러한 오류를 피하기 위해서 시스템의 동적 링커의 설정 3 을 바꾸거나 쉘 변수 LD_LIBRARY_PATH 를 정의해 라이브러리가 설치된 디렉토리를 포함 시키게 할 수 있습니다. (둘 다 동시에 할 수도 있습니다.)

예를 들어서, Bourne shell( /bin/sh 이나 /bin/bash)의 경우, 라이브러리 검색 경로는 다음과 같은 명령어로 설정할 수 있습니다.

$LD_LIBRARY_PATH=/usr/local/lib
$export LD_LIBRARY_PATH
$./example

C-shell( /bin/csh 이나 /bin/tcsh)의 경우 동일한 기능을 하는 다음 명령어를 쓸 수 있습니다.

% setenv LD_LIBRARY_PATH /usr/local/lib

C-shell의 표준 프롬프트 기호는 % 입니다. 이 기호는 명령어를 입력할 때, 제외하고 쳐야합니다.

각 세션에서 이러한 명령을 재입력하기 위해서, 해당 명령어들은 시스템 전체나 각각의 계정 로그인 파일에 저장할 수 있습니다.

프로그램의 정적 링크 버전을 원한다면 gcc 에서 -static 플래그를 사용하면 됩니다.

$gcc -static example.o -lgsl -lgslcblas -lm

공유, 정적, 동적 라이브러리에 관한 정보는 Program Library HOWTO 를 추천합니다(*).

ANSI C 규격

본 라이브러리는 ANSI C 로 작성되었고, ANSI C 표준으로(C89)로 쓰여지는 것을 의도하고 있습니다. ANSI C 컴파일러를 지원하는 모든 시스템에서 사용가능합니다.

본 라이브러리는 사용자에게 보이는 어떠한 비 ANSI C 확장기능에도 의존하지 않습니다. GSL을 사용하는 프로그램은 ANSI 표준을 준수해야 합니다. 하지만, 순수 ANSI C와 호환되는 확장 기능은 조건부 컴파일을 이용해서 지원할 수 있습니다. 때문에, 이러한 조건부 컴파일 기능을 지원하는 시스템에서 GSL 라이브러리는 컴파일러 확장 기능과 함께 활용할 수 있습니다.

특정 시스템에서 ANSI C의 기능이 손상되었다면, 라이브러리는 컴파일 과정에서 관련 기능들을 제외하고 컴파일합니다. 이런 경우 해당 기능을 사용하는 프로그램의 링크가 불가능하고 의도치 않은 결과를 얻을 수 있습니다.

네임스페이스 충돌을 방지하기 위해서 모든 함수와 변수들은 앞에 접두사로 gsl_ 이 붙게 됩니다. 매크로의 경우 GSL_ 접두사가 붙습니다.

inline 함수

inline 기능 4 은 ANSI C 표준(C89)에서 지원하는 기능은 아니라 라이브러리에서 inline 함수를 기본적으로 지원하지 않습니다. inline 함수는 C99 표준에서 공식적으로 지원하기 시작했습니다. 하지만 대다수의 C89 표준 컴파일러에서도 오랫동안 inline 기능을 확장기능으로 제공해왔습니다.

inline 기능의 사용을 위해, 라이브러리의 외부 헤더 파일에서는 조건부 컴파일 기능을 이용해, 성능 개선이 가능한 몇몇 기능들에 대해 inline 버전을 제공합니다. 이러한 함수들의 inline 버전은 응용 프로그램을 컴파일 할 때, 매크로 HAVE_INLINE 을 정의해 포함시킬 수 있습니다.

$gcc -Wall -c DHAVE_INLINE example.c

만약 autoconf 라는 매크로를 사용한다면, 자동으로 정의됩니다. HAVE_INLINE 매크로를 정의하지 않는다면, inline 함수가 아닌 일반 함수가 대신 사용됩니다.

기본적으로 extern inline 5inline 함수를 정의하기 위한 키워드(keyword)로 사용됩니다. 이는 gcc 에서 불명확한 함수 정의를 막기위한 확장기능입니다. 만약 다른 컴파일러에서 extern inline 이 문제가 생긴다면, autoconf 검사를 사용해볼 수 있습니다. Autoconf 매크로

gcc 를 C99로 컴파일한다면( gcc -std=c99`) 헤더파일들은 자동으로 extern inline 에서 C99 호환 inline 함수 정의들로 바뀝니다. 다른 C99 컴파일러를 사용한다면, GSL_C99_INLINE 매크로를 넣어볼 수 있습니다.

Long double

일반적으로, 이 라이브러리에서 사용된 알고리즘들은 배 정밀도를 기반으로 쓰였습니다. long double 데이터형은 실제 계산에서 지원되지 않습니다.

이러한 선택의 이유는 long double 의 정밀도가 기기에 의존하기 때문입니다. IEEE 표준은 각각의 기기들에서 확장된 숫자형들이 가져야 하는 최소 정밀도만을 정해두었습니다. 반면, 배정밀도 double 의 정밀도는 기기에 관계 없이 모두 동일한 정밀도를 가집니다.

그러나, 실제 계산을 할때는, long double 형의 데이터를 사용해야 할 때도 있습니다. vectormatrix 데이터형은 long double 을 지원하는 데이터형을 포함하고 있습니다.

한가지 알아두어야 할 점은 어떤 시스템의 표준 라이브러리 stdio.h 에 정의된 printfscanf 같은 입출력 함수들은 long double 형을 정확히 포함하지 않는 경우도 있습니다. 라이브러리의 configure 단계에서 이러한 기능을 확인하고 필요한 경우 이에 의존하는 특정 GSL 명령어를 제거해서, 정의되지 않거나 잘못된 결과가 나오는 경우를 피할 수 있습니다. long double 을 지원하지 않을 경우 configure 단계에서 출력 결과는 다음과 같습니다.:

checking whether printf works with long double... no

long double 데이터 형의 입/출력이 사용하고자 하는 시스템에서 지원하지 않는다면, 이에 의존하는 GSL 함수들은 결과적으로, 프로그램에 link할 수 없습니다.

만약, long double 을 지원하지 않는 시스템에서 작업해야 한다면, 이진 형태(binary format)을 사용하거나 long doubledouble 로 변환해 읽고 쓰는 방법 등이 있습니다.

함수의 이식성

이식 가능한 프로그램의 작성을 지원하기 위해, GSL에서는 다른 라이브러리에 작성된 함수들을 이식해 제공하기도 합니다. 예를 들어 BSD 수학 라이브러리가 있습니다. 프로그램을 작성할 때, 원래 라이브러리에 있는 함수들을 사용하거나 아니면, GSL의 이식 버전을 사용해 볼 수 있습니다. 이 과정은 전처리기에서 매크로로 관리 가능하며, 원래 라이브러리가 존재하지 않는 다른 기기에서 사용할 때 유용합니다.

예를 들어서, 사용하는 기기에 BSD 라이브러리의 함수 hypot() 가 있다면, 다음의 메크로를 config.h 와 응용 프로그램에 정의할 수 있습니다.

/* Substitute gsl_hypot for missing system hypot */

#ifndef HAVE_HYPOT
#define hypot gsl_hypot
#endif

응용 프로그램의 소스 파일들에 include 명령어; #include <config.h> 를 사용해 hypot() 이 존재하지 않을 때, 소스 파일 내의 hypot()gsl_hypot() 으로 교체할 수 있습니다. 이러한 교체는 autoconf 를 사용해서 자동으로 이루어지도록 할 수도 있습니다.

Autoconf 매크로 를 참고할 수 있습니다.

대부분의 경우에, 가장 좋은 방법은 본래 함수들이 존재한다는 가정하에, 그 함수들을 사용하고 존재하지 않는다면, 대신에 GSL 함수를 사용하는 것입니다. 이를 이용하면 시스템 별로 최적화된 라이브러리를 사용할 수 있습니다. 이설계 방법은 GSL 스스로도 사용하고 있습니다.

대체 최적화

라이브러리에 있는 대부분의 함수들은 모든 아키텍쳐들에 대해 최적화 되어있지 않습니다. 예를 들어서, 가우스 난수를 계산하는 방법이 여러가지가 있는데, 이들의 상대적 속도는 구동 기기의 종류에 따라 달라집니다. 이 경우에 라이브러리에서는 본래 함수랑 똑같은 인터페이스 6 로 이식 함수를 구현해 제공합니다. 만약, 프로그램을 작성할 때, 표준 함수의 라이브러리 구현체를 사용했다면, 전처리기에서 대체 함수를 선택할 수 있습니다. 이러한 방법은 사용자가 최적화한 함수를 사용할 때도 이식성을 유지하기에 좋은 방법입니다. 다음 줄들은 가우스 분포)에서 표본을 뽑아오는 방법을 플래폼 의존 방식으로 구현한 것입니다.

#ifdef SPARC
#define gsl_ran_gaussian gsl_ran_gaussian_ratio_method
#endif
#ifdef INTEL
#define gsl_ran_gaussian my_gaussian
#endif

이러한 줄들은 응용 프로그램의 구성 헤더 파일 config.h 에 작성되어, 모든 소스파일에서 이 헤더파일을 포함해야 합니다. 주의할 점은 대체한 이식함수들은 비트 단위로 똑같은 결과를 내지는 않으며, 난수 분포의 경우 완전히 다른 난수들을 생성한다는 것에 유의해야합니다.

다양한 수치 자료형 지원

라이브러리에 정의된 많은 함수들은 다양한 자료형을 지원합니다. 한 함수의 지료형 구현체는 자료형을 이름으로 가지는 접사와 함수 이름이 붙은 형태로 구현되어 있습니다. 이러한 자료형의 이름은 C++ 원시 템플릿에 정의된 자료형을 기반으로 합니다. 구체적으로 해당 접사는 모듈의 이름으로 된 접두사와 함수의 이름 사이에 넣어집니다. 다음 표는 가상의 모듈 gsl_foo 자료형으로 정의된 fn() 의 모든 수치형 정의를 보여줍니다.

gsl_foo_fn               double
gsl_foo_long_double_fn   long double
gsl_foo_float_fn         float
gsl_foo_long_fn          long
gsl_foo_ulong_fn         unsigned long
gsl_foo_int_fn           int
gsl_foo_uint_fn          unsigned int
gsl_foo_short_fn         short
gsl_foo_ushort_fn        unsigned short
gsl_foo_char_fn          char
gsl_foo_uchar_fn         unsigned char

일반적으로 배정밀도 double 의 수치형이 기본으로 사용됩니다. 이 경우에는 접사가 필요 없습니다. 예를 들어서 함수 gsl_stats_mean()double 자료형들의 평균값을 구해줍니다. 하지만, gsl_stats_int_mean() 의 경우 정수들의 평균값을 구해줍니다.

라이브러리에서 정의하는 여러 자료형들도 똑같은 규약을 사용합니다. 예를 들어 gsl_vectorgsl_matrix 가 있습니다. 이 경우 자료형의 이름 뒤에 붙는 형태로 구성됩니다. 예를 들어서 어느 모듈이 gsl_foo 라는 자료형을 정의하는 경우, 다음과 같은 방법으로 확장할 수 있습니다.

gsl_foo                  double
gsl_foo_long_double      long double
gsl_foo_float            float
gsl_foo_long             long
gsl_foo_ulong            unsigned long
gsl_foo_int              int
gsl_foo_uint             unsigned int
gsl_foo_short            short
gsl_foo_ushort           unsigned short
gsl_foo_char             char
gsl_foo_uchar            unsigned char

라이브러리에서 제공하는 모듈이 자료형에 의존해 정의되어 있다면, 이 라이브러리에서는 각각의 자료형을 위한 헤더 파일을 독립적으로 제공할 것입니다. 이러한 파일 이름들은 아래와 같이 작성되어 있습니다. 편의를 위해서 기본 헤더파일은 모든 자료형에 대한 정의를 담고 있습니다. 배정밀도로 정의된 함수만을 가져오거나 다른 특정한 자료형으로 정의된 함수만을 가져오고 싶다면 다음의 독립된 헤더 파일들을 포함시키면 됩니다.

#include <gsl/gsl_foo.h>               All types
#include <gsl/gsl_foo_double.h>        double
#include <gsl/gsl_foo_long_double.h>   long double
#include <gsl/gsl_foo_float.h>         float
#include <gsl/gsl_foo_long.h>          long
#include <gsl/gsl_foo_ulong.h>         unsigned long
#include <gsl/gsl_foo_int.h>           int
#include <gsl/gsl_foo_uint.h>          unsigned int
#include <gsl/gsl_foo_short.h>         short
#include <gsl/gsl_foo_ushort.h>        unsigned short
#include <gsl/gsl_foo_char.h>          char
#include <gsl/gsl_foo_uchar.h>         unsigned char

C++과의 호환성

이 라이브러리의 헤더 파일들은 직접 C++ 프로그램에 사용할 수 있도록, 함수들을 extern "C" 형태로 정의합니다. 이 방식은 라이브러리 내의 함수들을 C++에서 바로 불러올 수 있게 해줍니다.

라이브러리에 사용자 정의함수를 인자로 넘기는 경우에 C++ 예외 처리를 사용하고자 한다면, 라이브러리가 추가적인 CFLAFS 설정인 -fexceptions 로 빌드 되어야 합니다.

배열 별칭

이 라이브러리에서 배열, 벡터, 행렬들이 수정 가능한 인자로 전달 되었을 때, 각각의 자료형들이 별칭된 관계가 아니며, 겹치지도 않는다고 가정합니다. 이러한 방법은 라이브러리에서 중접 메모리 구역을 관리하지 않아도 되게 하고 추가적인 최적화 방법을 사용할 수 있게 해줍니다. 만약 중첩된 메모리 구역이 수정 가능한 인자로 전달 된다면, 함수의 결과가 정의되지 않습니다. 만약 인자가 수정되지 않게 할 경우, (예를 들어서 함수 원형에서 const 인자로 정의하는 경우가 있습니다) 중첩되거나 할당된 메모리 구역은 안전하게 사용할 수 있습니다.

스레드 안전성

이 라이브러리는 다중 스레드 프로그램에 사용할 수 있습니다. 모든 함수는 스레드 안전합니다. 이 말은 모든 함수가 정적 변수를 사용하지 않는다는 뜻입니다. 메모리는 항상 함수가 아니라 객체들에 연결되어 있습니다. 임시 공간에 있는 작업 공간 객체를 사용하는 함수의 경우, 작업 공간 객체는 각각의 스레드 기저에 할당되어야 합니다. 읽기 전용 메모리에 있는 객체를 사용하는 경우 여러 스레드에서 동시에 사용될 수 있습니다. 표 객체는 함수 원형에서 항상 상수로 정의되어야 합니다. 이는 다른 스레드에 의해 안전하게 접근할 수 있음을 나타냅니다.

라이브러리 안에 몇몇 정적 변수들이 존재합니다. 이 변수들은 라이브러리 전체의 행동을 제어하기 위해 사용됩니다. (예를 들어, 범위를 확인하고 함수가 치명적인 오류를 반환할 때 등이 있습니다.) 이 변수들은 사용자에 의해 직접적으로 설정됩니다. 따라서 프로그램이 시작될 때, 한번 초기화 되어야 하며, 다른 스레드들에 의해 수정하지 않도록 해야합니다.

제거 예정 함수

라이브러리의 개발 과정에서 라이브러리 내부의 함수들을 수정하거나 제거할 수도 있습니다. 이러한 상황에 있는 함수들은 처음에 deprecated 로 선언되고 다음 버전의 라이브러리에서 제거됩니다. 프로그래밍 과정에서 현재 베포판에서 제거 예정인 함수들 비활성화 할 수도 있습니다. 전처리기에서 GSL_DISALBE_DEPRECATED 를 선언해 주면 됩니다. 이는 다음 버전의 라이브러리와의 호환성 검사에 사용될 수 있습니다.

코드 재사용

라이브러리에 작성된 기능등은 가능한 한 다른 모듈이나 파일들에 의존하지 않도록 짜여져 있습니다. 이는 라이브러리 전체를 설치할 필요 없이 독립된 함수들을 추출해서 다른 응용 프로그램에 사용할 수 있게 합니다. GSL_ERROR 와 같은 매크로를 선언하고 #include 선언을 제거해 파일을 독립적으로 실행할 수 있게 컴파일할 수 있습니다. 이러한 방법의 코드 재사용은 GNU 일반 공중 사용 허가서의 규약에서 권장하고 있습니다.

1

끝의 자리값들은 컴파일러와 환경에 따라 다양하게 나올 수 있습니다.

2

예를 들어 Mac OS system에서는 필요 없습니다.

3

GNU/Linux 시스템의 /etc/ld.so.conf

4

기본적으로 정의 된 함수를 사용하기 위해 코드 내에서 함수를 부르면, 플랫폼별, 언어별 호출 규약(Calling consvention)에 의해 정해진 절차에 따라 함수를 부르게 됩니다. 이러한 과정으로 인해 특정한 기능을 함수로 사용하는 경우 단순히 해당 코드를 안에 넣는 것보다 호출 과정이 추가되어 실행 시간이 늘어나는 제약이 있습니다. 해당 이유로 인해 재귀 함수 기능은 일반적으로 실용적인 프로그래밍 과정에서 권장되지 않습니다. 인라인 기능은 이를 개선할 수 있는 방법 중 하나로, 매크로와 비슷하게 인라인으로 정의된 함수의 내부 코드를 해당 함수가 호출된 부분에 그대로 넣어 컴파일을 해 호출 과정에서의 간극을 개선할 수 있습니다(*).

5

extern inline 은 C89, ANSI C에서 확장으로 지원하는 인라인 함수 선언 방법입니다. C99에서는 간단히 inline 을 사용해 인라인 함수를 선언할 수 있습니다.

6

같은 인터페이스라는 뜻은, 예를 들어서 본래 함수가 double f_get(int i, double, j) 형태로 되어있다면, 이러한 함수의 GSL이식 버전도 똑같은, 인자와 반환값으로 설계되었다는 뜻입니다. double gsl_f_get(int i, double j) 형태로 정의됩니다.