Thursday, July 2, 2015

"Open-Sourcing" HyPer queries

We are sometimes contacted by people who would like to inspect the behavior of HyPer. HyPer itself is currently not open source, but it is still possible to look at the source code of queries (which is all that matters for query performance).

To do that, we can use an internal debug interface that we use for profiling. Note that this feature is completely undocumented and unsupported and might go away at any point in time, it is not aimed at end users. But it can still be used to inspect queries. As prerequisite we need the clang compiler in the search path, more precisely a binary called clang-3.5. Then, we can start the server daemon with a debug flag like that:

bin/hyperd mydatabase -xcompilestatic


Now every statement that generates code (e.g., queries, or create table statements) writes the code to the local directory and then call clang-3.5 to compile it into a shared library. Usually we do not use clang but JIT directly for performance reasons, but some debugging and profiling tools can handle shared libraries much better than JITed code. Note that the generated code is probably bested viewed after passing it through c++filt, as that will pretty-print the mangled names.

Consider for example an SQL statement like

create table foo(x integer not null)

It results in three file when run with the debug flag, the file dump1.ll with the source code, a file dump1.debug-ll for debug info purposes, and a file dump1.so with the compiled code. Here the code is not very exciting, it is basically code for memory management (initPartition_foo etc.), multiversioning (retrieveVersionPartition_foo etc.) and the low-level SQL update primitives (insertPartition_foo).

If we run a query like

select count(*) from foo where x<123

another ll file is generated. Here the query consists basically of a single function (planStart).The first few dozen lines are uninteresting, they just navigate memory until the correct columns pointers are computed. The main query code itself is quote short, I have included it below (ignoring the extra code to handle tuples that are modified by concurrent transactions).



loopscan:        
  %tid = phi i64 [ %86, %loopDonescan18 ], [ %66, %loopscan.preheader ]
  %76 = call i64 @hyper::RuntimeFunctions::findUnmodifiedRange(hyper::Transaction::VersionRecord**, unsigned int*, unsigned long, unsigned long)(%"hyper::Transaction::VersionRecord"** %72, i32* %74, i64 %tid, i64 %67)
  %77 = icmp ult i64 %tid, %76
  br i1 %77, label %loopscan11.preheader, label %loopDonescan12

loopscan11.preheader:                           
  br label %loopscan11, !dbg !156

loopscan11:                                     
  %tid13 = phi i64 [ %84, %scanStep ], [ %tid, %loopscan11.preheader ]
  %.sum = sub i64 %tid13, %66
  %78 = getelementptr i32* %68, i64 %.sum
  %x = load i32* %78, align 4
  %79 = icmp slt i32 %x, 123
  br i1 %79, label %then14, label %scanStep

then14:                                         
  %80 = load i64* %0, align 8
  %81 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 %80, i64 1)
  %82 = extractvalue { i64, i1 } %81, 1
  br i1 %82, label %overflow.loopexit35, label %cont16

cont16:                                         
  %83 = extractvalue { i64, i1 } %81, 0
  store i64 %83, i64* %0, align 8
  br label %scanStep

scanStep:                                       
  %84 = add i64 %tid13, 1
  %85 = icmp eq i64 %84, %76
  br i1 %85, label %loopDonescan12.loopexit, label %loopscan11

loopDonescan12.loopexit:                        
  br label %loopDonescan12


It first figures out which tuples are unmodified and can be scanned safely. Then (starting from loopscan11) it scans over these tuples, loads the x column, checks the filter condition (before then14), and updates the count for qualifying tuples (including overflow checks).

Note that the query is not parallelized because the query optimized decided that the (empty) table did not warrant parallelization. For more larger data sets the generated code will be more complex due to parallel execution, but it should be quite readable in general.

Note that there is a particular hackish way to exploit that interface that might be interesting for low-level experiments: Instead of providing the real clang compiler, we can put a script called clang-3.5 into your search path that first opens the provided source code with an editor and then calls clang. This allows is to modify the generated code and play with the query as we like. Do do not do that if you value the data that is currently stored inside your database! Bad things can and will happen. But it is great for ad-hoc experiments.

No comments:

Post a Comment