Easy::jit은 C++ 코드에 대한 간단한 Just-In-Time 코드 생성을 가능하게 하는 컴파일러 지원 라이브러리입니다.
먼저 clang과 LLVM을 설치합니다.
apt install llvm-6.0-dev llvm-6.0-tools clang-6.0그런 다음 프로젝트를 구성하고 컴파일합니다.
cmake -DLLVM_DIR=/usr/lib/llvm-6.0/cmake < path_to_easy_jit_src >
cmake --build . 예제를 빌드하려면 opencv 라이브러리를 설치하고 -DEASY_JIT_EXAMPLE=1 플래그를 cmake 명령에 추가하세요.
벤치마킹을 활성화하려면 Google 벤치마크 프레임워크를 설치하고 -DEASY_JIT_BENCHMARK=1 -DBENCHMARK_DIR=<path_to_google_benchmark_install> 플래그를 cmake 명령에 추가하세요.
모든 것이 준비되었습니다!
프로젝트를 빠르게 테스트해보고 싶다면 docker와 함께 사용할 수 있도록 모든 것이 제공됩니다. 이렇게 하려면 <path_to_easy_jit_src>/misc/docker 의 스크립트를 사용하여 현재 디렉터리에서 Dockerfile을 생성한 다음 docker 인스턴스를 생성하세요.
python3 < path_to_easy_jit_src > /misc/docker/GenDockerfile.py < path_to_easy_jit_src > /.travis.yml > Dockerfile
docker build -t easy/test -f Dockerfile
docker run -ti easy/test /bin/bash Easy::Jit 라이브러리는 컴파일러의 지원에 의존하기 때문에 이를 사용하려면 컴파일러 플러그인을 로드해야 합니다. -Xclang -load -Xclang <path_to_easy_jit_build>/bin/EasyJitPass.so 플래그는 플러그인을 로드합니다.
포함된 헤더에는 C++14 지원이 필요하며 포함 디렉터리를 추가하는 것을 잊지 마세요! --std=c++14 -I<path_to_easy_jit_src>/cpplib/include 사용하세요.
마지막으로 -L<path_to_easy_jit_build>/bin -lEasyJitRuntime 사용하여 바이너리를 Easy::Jit 런타임 라이브러리에 연결해야 합니다.
모두 합치면 다음과 같은 명령이 나옵니다.
clang++-6.0 --std=c++14 < my_file.cpp >
-Xclang -load -Xclang /path/to/easy/jit/build/bin/bin/EasyJitPass.so
-I < path_to_easy_jit_src > /cpplib/include
-L < path_to_easy_jit_build > /bin -lEasyJitRuntime 비디오 스트림에 이미지 필터를 적용하는 소프트웨어의 아래 코드를 고려하십시오. 다음 섹션에서는 Easy::jit 라이브러리를 사용하도록 이를 조정하겠습니다. 최적화할 함수는 전체 이미지에 마스크를 적용하는 kernel 입니다.
마스크, 크기 및 면적은 자주 변경되지 않으므로 이러한 매개변수에 대한 기능을 전문화하는 것이 합리적으로 보입니다. 또한 이미지 크기와 채널 수는 일반적으로 전체 실행 중에 일정하게 유지됩니다. 그러나 스트림에 따라 달라지므로 해당 값을 아는 것은 불가능합니다.
static void kernel ( const char * mask, unsigned mask_size, unsigned mask_area,
const unsigned char * in, unsigned char * out,
unsigned rows, unsigned cols, unsigned channels) {
unsigned mask_middle = (mask_size/ 2 + 1 );
unsigned middle = (cols+ 1 )*mask_middle;
for ( unsigned i = 0 ; i != rows-mask_size; ++i) {
for ( unsigned j = 0 ; j != cols-mask_size; ++j) {
for ( unsigned ch = 0 ; ch != channels; ++ch) {
long out_val = 0 ;
for ( unsigned ii = 0 ; ii != mask_size; ++ii) {
for ( unsigned jj = 0 ; jj != mask_size; ++jj) {
out_val += mask[ii*mask_size+jj] * in[((i+ii)*cols+j+jj)*channels+ch];
}
}
out[(i*cols+j+middle)*channels+ch] = out_val / mask_area;
}
}
}
}
static void apply_filter ( const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) {
kernel (mask, mask_size, mask_area, image. ptr ( 0 , 0 ), out-> ptr ( 0 , 0 ), image. rows , image. cols , image. channels ());
} 라이브러리의 기본 헤더는 easy/jit.h 이며, 여기서 라이브러리의 유일한 핵심 기능을 내보냅니다. 이 함수는 호출됩니다. 어떻게 될까요? -- easy::jit . 해당 include 지시문을 파일 상단에 추가합니다.
# include < easy/jit.h > easy::jit 호출을 통해 함수를 특수화하고 두 개의 매개변수(입력 및 출력 프레임)만 사용하는 새 함수를 얻습니다.
static void apply_filter ( const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) {
using namespace std ::placeholders ;
auto kernel_opt = easy::jit (kernel, mask, mask_size, mask_area, _1, _2, image. rows , image. cols , image. channels ());
kernel_opt (image. ptr ( 0 , 0 ), out-> ptr ( 0 , 0 ));
}Easy::jit는 런타임에 특수화할 함수의 LLVM 비트코드 표현을 바이너리 코드에 포함합니다. 이를 수행하려면 라이브러리에 이러한 함수 구현에 대한 액세스가 필요합니다. Easy::jit는 런타임에 어떤 기능이 특화되었는지 추론하기 위해 노력하지만 여전히 많은 경우 이것이 불가능합니다.
이 경우 다음 코드와 같이 EASY_JIT_EXPOSE 매크로를 사용할 수 있습니다.
void EASY_JIT_EXPOSE kernel () { /* ... */ }또는 컴파일 중에 정규식을 사용합니다. 다음 명령은 이름이 "^kernel"로 시작하는 모든 함수를 내보냅니다.
clang++ ... -mllvm -easy-export= " ^kernel.* " ... easy/jit.h 헤더와 병행하여 이미 생성된 함수의 재컴파일을 방지하기 위해 코드 캐시를 제공하는 easy/code_cache.h 가 있습니다.
아래에는 이전 섹션의 코드가 표시되지만 코드 캐시를 사용하도록 조정되었습니다.
# include < easy/code_cache.h > static void apply_filter ( const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) {
using namespace std ::placeholders ;
static easy::Cache<> cache;
auto const &kernel_opt = cache. jit (kernel, mask, mask_size, mask_area, _1, _2, image. rows , image. cols , image. channels ());
kernel_opt (image. ptr ( 0 , 0 ), out-> ptr ( 0 , 0 ));
} 이 프로젝트의 최상위 디렉터리에 있는 LICENSE 파일을 참조하세요.
개인 프로젝트 작업을 지원해준 Quarkslab에 특별히 감사드립니다.
세르주 겔톤(serge_sans_paille)
후안 마누엘 마르티네스 카마뇨(jmmartinez)
Kavon Farvardin (kavon) atJIT의 저자