{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "# Remote Backend Compiler - RBC\n", "*by Pearu Peterson*\n", "\n", "## Statement of the problem\n", "\n", "*Compile and run user-defined functions in a language-agnostic JIT enabled program semi-remotely.*\n", "\n", "### Semi-remote compilation\n", "\n", "- *compiler frontend*: user-written source code is parsed and transformed to LLVM IR in client program\n", "- the client program sends the LLVM IR over network to a server program\n", "- *compiler backend*: the server program compiles the LLVM IR to machine code (that can be executed on the server's CPU or GPU devices)\n", "\n", "### Constraints\n", "\n", "- A user-defined function is defined in Python or C/C++\n", " (or any other language with LLVM tools)\n", "- Client host runs on a 32- or 64-bit OS: Linux, MacOSX, Windows\n", "- Server host runs on a 64-bit Linux\n", "\n", "## Solution\n", "\n", "- *Remote Backend Compiler - RBC*: https://github.com/xnd-project/rbc\n", "- A Python-to-Python prototype uses Numba for LLVM IR generation and Numba llvmlite for machine code compilation\n", "- A Python-to-SQL application uses Numba for LLVM IR generation and HeavyDB for machine code compilation\n", " * HeavyDB is a GPU enabled SQL database server\n", " * HeavyDB uses LLVM Compiler C++ library for JIT compilation\n", " * https://www.heavy.ai/\n", "\n", "# Demo of the prototype below" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "staring rpc.thrift server: /home/pearu/git/xnd-project/rbc/rbc/remotejit.thrift" ] } ], "source": [ "#NBVAL_IGNORE_OUTPUT\n", "from rbc import RemoteJIT\n", "rjit = RemoteJIT(host='localhost', port=7890)\n", "rjit.start_server(background=True)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Client-server connector, RBC jit-decorator\n", "rjit = RemoteJIT(host='localhost', port=7890)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# A user-defined function\n", "@rjit('int64(int64, int64)')\n", "def foo(a, b):\n", " return a + b" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "--------------------------------------cpu---------------------------------------\n", "; ModuleID = 'rbc.irtools.compile_to_IR'\n", "source_filename = \"\"\n", "target triple = \"x86_64-unknown-linux-gnu\"\n", "\n", "@_ZN08NumbaEnv8__main__7foo_241B40c8tJTC_2fWQA9HW1CcAv0EjzIkAdRoAEtoAgA_3dExx = common local_unnamed_addr global i8* null\n", "\n", "; Function Attrs: norecurse nounwind readnone\n", "define i64 @foo_lallA(i64 %.1, i64 %.2) local_unnamed_addr #0 {\n", "entry:\n", " %.6.i = add nsw i64 %.2, %.1\n", " ret i64 %.6.i\n", "}\n", "\n", "attributes #0 = { norecurse nounwind readnone }\n", "\n", "!llvm.module.flags = !{!0}\n", "\n", "!0 = !{i32 4, !\"pass_column_arguments_by_value\", i1 false}\n", "\n", "--------------------------------------------------------------------------------\n" ] } ], "source": [ "# NBVAL_IGNORE_OUTPUT\n", "# Generate LLVM IR, useful for debugging\n", "print(foo)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Triggers:\n", "# - local frontend compile,\n", "# - remote backend compile,\n", "# - and remote call\n", "foo(1, 3)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo(2, 4) # reuses remote compile result and triggers remote call" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "found no matching function signature to given argument types:\n", " (float64, float64) -> ...\n", " available function signatures:\n", " int64(int64, int64)\n" ] } ], "source": [ "try:\n", " foo(1.2, 3.4)\n", "except Exception as msg:\n", " print(msg)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# update with new signatures\n", "foo.signature('double(double, double)')\n", "foo.signature('complex128(complex128, complex128)');" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1+3.4j)" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo(1, 3.4j) # triggers remote compile and call" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4.2" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo(1.2, 3) # triggers remote compile and call" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "rjit.stop_server()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.6" } }, "nbformat": 4, "nbformat_minor": 4 }