Files
MP-SPDZ/doc/compilation.rst
2025-12-24 13:47:42 +11:00

212 lines
7.6 KiB
ReStructuredText

Compilation Process
-------------------
The easiest way of using MP-SPDZ is using ``compile.py`` as
described below. If you would like to run compilation directly from
Python, see :ref:`direct-compilation`.
After putting your code in ``Program/Source/<progname>.[mpc|py]``, run the
compiler from the root directory as follows
.. code-block:: bash
./compile.py [options] <progname> [args]
The arguments ``<progname> [args]`` are accessible as list under
``program.args`` within ``progname.[mpc|py]``, with ``<progname>`` as
``program.args[0]``. The resulting program for the virtual machine
will be called ``<progname>[-<arg0>[-<arg1>...]``.
The following options influence the computation domain:
.. cmdoption:: -F <integer length>
--field=<integer length>
Compile for computation modulo a prime and the default integer
length. This means that secret integers are assumed to have at most
said length unless explicitly told otherwise. The compiled output
will communicate the minimum length of the prime number to the
virtual machine, which will fail if this is not met. This is the
default with an *integer length* set to 64. When not specifying the
prime, the minimum prime length will be around 40 bits longer than
the integer length. Furthermore, the computation will be optimistic
in the sense that overflows in the secrets might have security
implications.
.. cmdoption:: -P <prime>
--prime=<prime>
Use bit decomposition by `Nishide and Ohta
<https://doi.org/10.1007/978-3-540-71677-8_23>`_ with a concrete
prime modulus for non-linear computation. This can be used
together with :option:`-F`, in which case *integer length* has to
be at most the prime length minus two. The security implications of
overflows in the secrets do not go beyond incorrect results. You
can use prime order domains without specifying this option.
Using this option involves algorithms for non-linear computation
which are generally more expensive but allow for integer lengths
that are close to the bit length of the prime. See
:ref:`nonlinear` for more details
.. cmdoption:: -R <ring size>
--ring=<ring size>
Compile for computation modulo 2^(*ring size*). This will set the
assumed length of secret integers to one less because many
operations require this. The exact ring size will be communicated
to the virtual machine, which will use it automatically if
supported.
.. cmdoption:: -B <integer length>
--binary=<integer length>
Compile for binary computation using *integer length* as default.
.. cmdoption:: -G
--garbled-circuit
Compile for garbled circuits (does not replace :option:`-B`).
For arithmetic computation (:option:`-F`, :option:`-P`, and
:option:`-R`) you can set the bit
length during execution using ``program.set_bit_length(length)``. For
binary computation you can do so with ``sint =
sbitint.get_type(length)``.
Use :py:func:`sfix.set_precision` to change the range for fixed-point
numbers.
The following options switch from a single computation domain to
mixed computation when using in conjunction with arithmetic
computation:
.. cmdoption:: -X
--mixed
Enables mixed computation using daBits.
.. cmdoption:: -Y
--edabit
Enables mixed computation using edaBits.
The implementation of both daBits and edaBits are explained in this paper_.
.. _paper: https://eprint.iacr.org/2020/338
.. cmdoption:: -Z <number of parties>
--split=<number of parties>
Enables mixed computation using local conversion. This has been
used by `Mohassel and Rindal <https://eprint.iacr.org/2018/403>`_
and `Araki et al. <https://eprint.iacr.org/2018/762>`_ It only
works with additive secret sharing modulo a power of two.
You can also tell the compiler which protocol you intend to run the
computation with:
.. cmdoption:: -E <protocol>
--execute <protocol>
Enable all suitable optimizations and restrictions for a particular
protocol. This is the same as in ``compile-run.py``. It will also
let the compiler estimate the total communication cost for many
arithmetic protocols.
The following options change less fundamental aspects of the
computation:
.. cmdoption:: -D
--dead-code-elimination
Eliminates unused code. This currently means computation that isn't
used for input or output or written to the so-called memory (e.g.,
:py:class:`~Compiler.types.Array`; see :py:mod:`~Compiler.types`).
.. cmdoption:: -b <budget>
--budget=<budget>
Set the budget for loop unrolling with
:py:func:`~Compiler.library.for_range_opt` and similar. This means
that loops are unrolled up to *budget* instructions. Default is
1000 instructions.
.. cmdoption:: -C
--CISC
Speed up the compilation of repetitive code at the expense of a
potentially higher number of communication rounds. For example, the
compiler by default will try to compute a division and a logarithm
in parallel if possible. Using this option complex operations such
as these will be separated and only multiple divisions or
logarithms will be computed in parallel. This speeds up the
compilation because of reduced complexity.
.. cmdoption:: -l
--flow-optimization
Optimize simple loops (``for <iterator> in range(<n>)``) by using
:py:func:`~Compiler.library.for_range_opt` and defer if statements
to the run time.
.. _direct-compilation:
Direct Compilation in Python
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You may prefer to not have an entirely static `.mpc` file to compile,
and may want to compile based on dynamic inputs. For example, you may
want to be able to compile with different sizes of input data without
making a code change to the `.mpc` file. To handle this, the compiler
an also be directly imported, and a function can be compiled with the
following interface:
.. code-block:: python
# hello_world.mpc
from Compiler.library import print_ln
from Compiler.compilerLib import Compiler
compiler = Compiler()
@compiler.register_function('helloworld')
def hello_world():
print_ln('hello world')
if __name__ == "__main__":
compiler.compile_func()
You could then run this with the same args as used with `compile.py`:
.. code-block:: bash
python hello_world.mpc <compile args>
This is particularly useful if want to add new command line arguments
specifically for your `.mpc` file. See `test_args.mpc
<https://github.com/data61/MP-SPDZ/blob/master/Programs/Source/test_args.mpc>`_
for more details on this use case.
Note that when using this approach, all objects provided in the high level
interface (e.g. sint, print_ln) need to be imported, because the `.mpc` file
is interpreted directly by Python (instead of being read by `compile.py`.)
Compilation vs run time
~~~~~~~~~~~~~~~~~~~~~~~
The most important thing to keep in mind is that the Python code is
executed at compile-time. This means that Python data structures such
as :py:class:`list` and :py:class:`dict` only exist at compile-time
and that all Python loops are unrolled. For run-time loops and lists,
you can use :py:func:`~Compiler.library.for_range` (or the more
optimizing :py:func:`~Compiler.library.for_range_opt`) and
:py:class:`~Compiler.types.Array`. For convenient multithreading you
can use :py:func:`~Compiler.library.for_range_opt_multithread`, which
automatically distributes the computation on the requested number of
threads.
This reference uses the term 'compile-time' to indicate Python types
(which are inherently known when compiling). If the term 'public' is
used, this means both compile-time values as well as public run-time
types such as :py:class:`~Compiler.types.regint`.