Easy::jit เป็นไลบรารี ที่ได้รับความช่วยเหลือจากคอมไพลเลอร์ ที่ช่วยให้สามารถสร้างโค้ด Just-In-Time อย่างง่ายสำหรับโค้ด C++
ขั้นแรก ให้ติดตั้ง 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
ทุกอย่างพร้อมลุย!
หากคุณต้องการทดสอบโปรเจ็กต์อย่างรวดเร็วเท่านั้น ทุกอย่างมีไว้เพื่อใช้กับนักเทียบท่า หากต้องการทำสิ่งนี้ ให้สร้าง Dockerfile จากไดเร็กทอรีปัจจุบันโดยใช้สคริปต์ใน <path_to_easy_jit_src>/misc/docker
จากนั้นสร้างอินสแตนซ์ 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
สุดท้ายนี้ ไบนารีจะต้องเชื่อมโยงกับไลบรารีรันไทม์ Easy::Jit โดยใช้ -L<path_to_easy_jit_build>/bin -lEasyJitRuntime
เมื่อรวมทุกอย่างเข้าด้วยกันแล้วเราจะได้คำสั่งดังต่อไปนี้
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)
ฮวน มานูเอล มาร์ติเนซ กามาโน (เจมมาร์ติเนซ)
Kavon Farvardin (kavon) ผู้เขียน atJIT