forked from M-Labs/artiq
core: support precompilation of kernels
This commit is contained in:
parent
232f28c0e8
commit
ac55da81d8
|
@ -1,5 +1,6 @@
|
||||||
import os, sys
|
import os, sys
|
||||||
import numpy
|
import numpy
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
from pythonparser import diagnostic
|
from pythonparser import diagnostic
|
||||||
|
|
||||||
|
@ -120,7 +121,52 @@ class Core:
|
||||||
except diagnostic.Error as error:
|
except diagnostic.Error as error:
|
||||||
raise CompileError(error.diagnostic) from error
|
raise CompileError(error.diagnostic) from error
|
||||||
|
|
||||||
|
def _run_compiled(self, kernel_library, embedding_map, symbolizer, demangler):
|
||||||
|
if self.first_run:
|
||||||
|
self.comm.check_system_info()
|
||||||
|
self.first_run = False
|
||||||
|
self.comm.load(kernel_library)
|
||||||
|
self.comm.run()
|
||||||
|
self.comm.serve(embedding_map, symbolizer, demangler)
|
||||||
|
|
||||||
def run(self, function, args, kwargs):
|
def run(self, function, args, kwargs):
|
||||||
|
result = None
|
||||||
|
@rpc(flags={"async"})
|
||||||
|
def set_result(new_result):
|
||||||
|
nonlocal result
|
||||||
|
result = new_result
|
||||||
|
embedding_map, kernel_library, symbolizer, demangler = \
|
||||||
|
self.compile(function, args, kwargs, set_result)
|
||||||
|
self._run_compiled(kernel_library, embedding_map, symbolizer, demangler)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def precompile(self, function, *args, **kwargs):
|
||||||
|
"""Precompile a kernel and return a callable that executes it on the core device
|
||||||
|
at a later time.
|
||||||
|
|
||||||
|
Arguments to the kernel are set at compilation time and passed to this function,
|
||||||
|
as additional positional and keyword arguments.
|
||||||
|
The returned callable accepts no arguments.
|
||||||
|
|
||||||
|
Precompiled kernels may use RPCs.
|
||||||
|
|
||||||
|
Object attributes at the beginning of a precompiled kernel execution have the
|
||||||
|
values they had at precompilation time. If up-to-date values are required,
|
||||||
|
use RPC to read them.
|
||||||
|
Similarly, modified values are not written back, and explicit RPC should be used
|
||||||
|
to modify host objects.
|
||||||
|
Carefully review the source code of drivers calls used in precompiled kernels, as
|
||||||
|
they may rely on host object attributes being transfered between kernel calls.
|
||||||
|
Examples include code used to control DDS phase, and Urukul RF switch control
|
||||||
|
via the CPLD register.
|
||||||
|
|
||||||
|
The return value of the callable is the return value of the kernel, if any.
|
||||||
|
|
||||||
|
The callable may be called several times.
|
||||||
|
"""
|
||||||
|
if not hasattr(function, "artiq_embedded"):
|
||||||
|
raise ValueError("Argument is not a kernel")
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
@rpc(flags={"async"})
|
@rpc(flags={"async"})
|
||||||
def set_result(new_result):
|
def set_result(new_result):
|
||||||
|
@ -128,17 +174,15 @@ class Core:
|
||||||
result = new_result
|
result = new_result
|
||||||
|
|
||||||
embedding_map, kernel_library, symbolizer, demangler = \
|
embedding_map, kernel_library, symbolizer, demangler = \
|
||||||
self.compile(function, args, kwargs, set_result)
|
self.compile(function, args, kwargs, set_result, attribute_writeback=False)
|
||||||
|
|
||||||
if self.first_run:
|
@wraps(function)
|
||||||
self.comm.check_system_info()
|
def run_precompiled():
|
||||||
self.first_run = False
|
nonlocal result
|
||||||
|
self._run_compiled(kernel_library, embedding_map, symbolizer, demangler)
|
||||||
|
return result
|
||||||
|
|
||||||
self.comm.load(kernel_library)
|
return run_precompiled
|
||||||
self.comm.run()
|
|
||||||
self.comm.serve(embedding_map, symbolizer, demangler)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
def seconds_to_mu(self, seconds):
|
def seconds_to_mu(self, seconds):
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
from artiq.experiment import *
|
||||||
|
|
||||||
|
|
||||||
|
class Precompile(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.hello_str = "hello ARTIQ"
|
||||||
|
|
||||||
|
def prepare(self):
|
||||||
|
self.precompiled = self.core.precompile(self.hello, "world")
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def hello(self, arg):
|
||||||
|
print(self.hello_str, arg)
|
||||||
|
self.hello_str = "nowriteback"
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.precompiled()
|
||||||
|
self.hello_str = "noupdate"
|
||||||
|
self.precompiled()
|
|
@ -20,6 +20,28 @@ class CheckLog(EnvExperiment):
|
||||||
core_log("test_artiq_compile")
|
core_log("test_artiq_compile")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class _Precompile(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.setattr_device("core")
|
||||||
|
self.x = 1
|
||||||
|
self.y = 2
|
||||||
|
self.z = 3
|
||||||
|
|
||||||
|
def set_attr(self, value):
|
||||||
|
self.x = value
|
||||||
|
|
||||||
|
@kernel
|
||||||
|
def the_kernel(self, arg):
|
||||||
|
self.set_attr(arg + self.y)
|
||||||
|
self.z = 23
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
precompiled = self.core.precompile(self.the_kernel, 40)
|
||||||
|
self.y = 0
|
||||||
|
precompiled()
|
||||||
|
|
||||||
|
|
||||||
class TestCompile(ExperimentCase):
|
class TestCompile(ExperimentCase):
|
||||||
def test_compile(self):
|
def test_compile(self):
|
||||||
core_addr = self.device_mgr.get_desc("core")["arguments"]["host"]
|
core_addr = self.device_mgr.get_desc("core")["arguments"]["host"]
|
||||||
|
@ -34,3 +56,9 @@ class TestCompile(ExperimentCase):
|
||||||
log = mgmt.get_log()
|
log = mgmt.get_log()
|
||||||
self.assertIn("test_artiq_compile", log)
|
self.assertIn("test_artiq_compile", log)
|
||||||
mgmt.close()
|
mgmt.close()
|
||||||
|
|
||||||
|
def test_precompile(self):
|
||||||
|
exp = self.create(_Precompile)
|
||||||
|
exp.run()
|
||||||
|
self.assertEqual(exp.x, 42)
|
||||||
|
self.assertEqual(exp.z, 3)
|
||||||
|
|
Loading…
Reference in New Issue