Zulip Chat Archive

Stream: lean4

Topic: include lean.h


Floris van Doorn (Dec 14 2021 at 12:00):

Another stupid question: How do I compile and run code? I can compile a file using lake env lean ... -c ....
If I just stupidly compile this C file, I get an error that lean/lean.h is not found (at line 4: #include <lean/lean.h>).
How do I compile this C file correctly?

Henrik Böving (Dec 14 2021 at 12:02):

There is an FFI example over here https://github.com/leanprover/lake/blob/master/examples/ffi/lakefile.lean that shows how to marry C and Lean 4 code.

Floris van Doorn (Dec 14 2021 at 12:14):

I see, that is useful. Can I somehow use this to compile tactics/programs from a single file in the mathlib4 repository (which, by default doesn't compile its files)?

Arthur Paulino (Dec 14 2021 at 12:15):

@Floris van Doorn more specifically, this part tells the compiler where the lean/lean.h header is:
#["-I", (← getLeanIncludeDir).toString]

Arthur Paulino (Dec 14 2021 at 12:33):

Floris van Doorn said:

I see, that is useful. Can I somehow use this to compile tactics/programs from a single file in the mathlib4 repository (which, by default doesn't compile its files)?

What do you mean? Do you want to define a function in Mathlib4 that runs C code?

Sebastian Ullrich (Dec 14 2021 at 12:58):

@Floris van Doorn AFAIK, Lake can only build a single library or executable per package right now. But you can change binRoot to temporarily point at an appropriate module and then use lake build :bin.

Floris van Doorn (Dec 14 2021 at 13:14):

Ok, let me take a step back.

I am trying to collect all declarations in the environment and do some operations on them. When doing this with #eval inside Lean, these operations are starting to become really expensive, and I was wondering if using compiled code instead of interpreted code would help here.

So if I understand the comments correctly, I now have to write a function
def main (strs : List String) : IO UInt32 := ...
then use lake build with the appropriate configuration to run the code after compiling it?
Or can I do the compilation from Lean itself (using compileO?)

Also, how do I pass (data computed from) the current environment to the compiled function (if the function I compile lives in IO)?

Gabriel Ebner (Dec 14 2021 at 13:29):

What operations are you trying to do? The interpreter isn't that slow, and mathlib4+lean is pretty small.

Patrick Massot (Dec 14 2021 at 13:35):

Maybe he is operating on binport output?

Floris van Doorn (Dec 14 2021 at 13:37):

I was also a bit surprised that it would already slow down. I might have some inefficient code.
I'm trying to do a kind of page rank on the declarations. So it mostly involves mapping functions over arrays and Float operations.

Floris van Doorn (Dec 14 2021 at 13:37):

I am currently just doing it on mathlib4+Lean, but the idea is to later do it on the binport output. Unfortunately the former is already slow.

Floris van Doorn (Dec 14 2021 at 13:40):

The (very messy) file is here: https://github.com/leanprover-community/mathlib4/blob/fpvandoorn/graph/Personal/Graph.lean

Sebastian Ullrich (Dec 14 2021 at 13:40):

If you're on Linux, you can get a rough idea of what it is spending time on by running perf top in parallel

Floris van Doorn (Dec 14 2021 at 13:46):

A majority of the computation time seems to be spent on a quicksort:

def sortRevByWeight (g : Array NameNode) : Array NameNode :=
g.qsort λ nn1 nn2 => nn1.weight > nn2.weight

Here nni.weight are Floats

Floris van Doorn (Dec 14 2021 at 13:48):

and the array is about 27k entries long.

Sebastian Ullrich (Dec 14 2021 at 13:50):

Ah, makes sense. qsort will be specialized to the comparison function, so it cannot use any precompiled version. And LLVM will make a much better job of the resulting code than the interpreter.

Floris van Doorn (Dec 14 2021 at 13:52):

If interesting, these are the top entries of sudo perf top

  18.93%  libleanshared.so              [.] lean_inc_ref_cold
  18.42%  libleanshared.so              [.] lean_dec_ref_cold
  11.17%  libleanshared.so              [.] lean::ir::interpreter::eval_body
  11.14%  libleanshared.so              [.] lean::rb_map<lean::name, lean::ir::interpreter::symbol_cache_entry, lean::name_quick_cmp>::find
   9.00%  libleanshared.so              [.] lean::ir::interpreter::call
   7.86%  libleanshared.so              [.] lean_name_eq
   3.53%  libleanshared.so              [.] lean_inc
   3.04%  libleanshared.so              [.] lean::ir::interpreter::pop_frame
   2.72%  libleanshared.so              [.] lean::ir::interpreter::lookup_symbol
   2.36%  libleanshared.so              [.] lean::ir::interpreter::eval_expr
   2.28%  libleanshared.so              [.] lean::ir::interpreter::stub_m
   1.06%  libleanshared.so              [.] std::__1::vector<lean::ir::value, std::__1::allocator<lean::ir::value> >::__append
   0.85%  libleanshared.so              [.] lean::ir::interpreter::stub_m_aux
   0.71%  libleanshared.so              [.] lean::mk_pair<lean::name, lean::ir::interpreter::symbol_cache_entry>
   0.52%  libleanshared.so              [.] lean::rb_map<lean::name, lean::ir::interpreter::constant_cache_entry, lean::name_quick_cmp>::find

Sebastian Ullrich (Dec 14 2021 at 13:52):

That definitely looks like the interpreter :)

Sebastian Ullrich (Dec 14 2021 at 13:52):

Floris van Doorn said:

So if I understand the comments correctly, I now have to write a function
def main (strs : List String) : IO UInt32 := ...
then use lake build with the appropriate configuration to run the code after compiling it?

Yes, I think that is the simplest way currently.

Also, how do I pass (data computed from) the current environment to the compiled function (if the function I compile lives in IO)?

You can't directly. You can get an Environment in the compiled program by running importModules, in which case you should run the binary inside lake env so that LEAN_PATH is set correctly.

Henrik Böving (Dec 14 2021 at 13:54):

I implemented this over here: https://github.com/hargoniX/doc-gen4/blob/main/DocGen4/Load.lean if you want some place you can copy paste from :P (its largely adapted from the compiler and language server though)

Floris van Doorn (Dec 14 2021 at 13:58):

Sebastian Ullrich said:

You can't directly. You can get an Environment in the compiled program by running importModules, in which case you should run the binary inside lake env so that LEAN_PATH is set correctly.

Thanks! So something like this?

def main (strs : List String) : IO UInt32 := do
  let env  importModules [{module := `Mathlib}] {}
  let u  EIO.toIO (λ _ => "error") (CoreM.run' test {} {env := env})
  return 0

Floris van Doorn (Dec 14 2021 at 13:59):

Henrik Böving said:

I implemented this over here: https://github.com/hargoniX/doc-gen4/blob/main/DocGen4/Load.lean if you want some place you can copy paste from :P (its largely adapted from the compiler and language server though)

Thanks, I'll take a look

Sebastian Ullrich (Dec 14 2021 at 14:06):

Ah, you also need to run

initSearchPath ( Lean.findSysroot?) ["build/lib"]

before importModules. Then you don't even need lake env if you run the executable from the project root.

Floris van Doorn (Dec 14 2021 at 16:00):

@Sebastian Ullrich I think I'm getting closer. I now have the following files:

-- Mathlib/Graph.lean
[...]

def main (strs : List String) : IO UInt32 := do
  initSearchPath ( Lean.findSysroot?) ["build/lib"]
  let env  importModules [{module := `Mathlib}] {}
  let u  CoreM.toIO test {} {env := env}
  return 0

and

-- lakefile.lean
import Lake

open Lake DSL

package mathlib { binRoot := `Mathlib.Graph }

I can compile the file fine with lake build. However, running ./build/bin/mathlib gives me

uncaught exception: could not find native implementation of external declaration 'UInt64.ofNatCore' (symbols 'l_UInt64_ofNatCore___boxed' or 'l_UInt64_ofNatCore')

Searching on Zulip gives me similar errors using leanpkg where the solution is to add the option LINK_OPTS=-rdynamic. Do I have to do something similar with lake?

Sebastian Ullrich (Dec 14 2021 at 16:02):

Ah, you can use supportInterpreter := true in the lakefile for that

Arthur Paulino (Dec 14 2021 at 16:02):

@Floris van Doorn This helped me when I got that error:
add supportInterpreter := true to the package in the lakefile

Floris van Doorn (Dec 14 2021 at 16:06):

Thanks, that works!

Floris van Doorn (Dec 14 2021 at 16:11):

The qsort which was the bottleneck when interpreted is ~100 times faster now :-)


Last updated: Dec 20 2023 at 11:08 UTC