Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusability of preprocessing functions to reduce code bloat #297

Open
antanas-kalkauskas-sensmetry opened this issue Feb 28, 2022 · 3 comments
Labels
feature request Request or advice for a feature question Further information is requested

Comments

@antanas-kalkauskas-sensmetry

If we have a stream A in a copilot specification that is defined by applying some preprocessing to another stream B (say an external signal), it seems that in the generated code the calculation of stream A would implement the whole preprocessing to calculate it from stream B. But this means that if there is some involved preprocessing function that is commonly used in the specification (i. e. some filtering function or a function to define state transitions in a large state machine) then the preprocessing code would be reimplemented again many times which could lead to bloated .text section in generated binary.

If there is a commonly used preprocessing function it would be good to have it defined once and to be reused in the constructions of the streams in generated C code. Is there some way to do that?

@antanas-kalkauskas-sensmetry
Copy link
Author

antanas-kalkauskas-sensmetry commented Feb 28, 2022

Example

A quick example to illustrate the issue. Say we have three signals to which we apply the same preprocessing function:

{-# LANGUAGE RebindableSyntax #-}

module PreprocessingReimplemented where
import Language.Copilot
import Copilot.Compile.C99


signal1 :: Stream Float 
signal1 = extern "signal1" Nothing 

signal2 :: Stream Float 
signal2 = extern "signal2" Nothing

signal3 :: Stream Float 
signal3 = extern "signal3" Nothing


-- some example filter constants
a = 1
b = 2
c = 3

-- just an example of preprocessing, this can be much more complicated
involvedPreprocessing :: Stream Float -> Stream Float
involvedPreprocessing x = y
    where y = a*x + b*([0]++y) + c*([0, 0]++y)

spec = do
    trigger "triggerFunction1" (involvedPreprocessing signal1 > 10) [arg (involvedPreprocessing signal1)]
    trigger "triggerFunction2" (involvedPreprocessing signal2 > 10) [arg (involvedPreprocessing signal2)]
    trigger "triggerFunction3" (involvedPreprocessing signal3 > 10) [arg (involvedPreprocessing signal3)]

main :: IO ()
main = do
    reify spec >>= compile "preprocessing_reimplemented"

If we look in the generated C code we would see that the filter functions are repeatedly implemented 3 times:

...

bool triggerFunction1_guard(void) {
  return (((signal1_cpy) + (((float)(2.0)) * ((s0_get)((0))))) + (((float)(3.0)) * ((s1_get)((0))))) > ((float)(10.0));
}

float triggerFunction1_arg0(void) {
  return ((signal1_cpy) + (((float)(2.0)) * ((s2_get)((0))))) + (((float)(3.0)) * ((s3_get)((0))));
}

bool triggerFunction2_guard(void) {
  return (((signal2_cpy) + (((float)(2.0)) * ((s4_get)((0))))) + (((float)(3.0)) * ((s5_get)((0))))) > ((float)(10.0));
}

float triggerFunction2_arg0(void) {
  return ((signal2_cpy) + (((float)(2.0)) * ((s6_get)((0))))) + (((float)(3.0)) * ((s7_get)((0))));
}

bool triggerFunction3_guard(void) {
  return (((signal3_cpy) + (((float)(2.0)) * ((s8_get)((0))))) + (((float)(3.0)) * ((s9_get)((0))))) > ((float)(10.0));
}

float triggerFunction3_arg0(void) {
  return ((signal3_cpy) + (((float)(2.0)) * ((s10_get)((0))))) + (((float)(3.0)) * ((s11_get)((0))));

...

In this case the code bloat is not significant, but this is just an example to illustrate the issue.

@ivanperez-keera
Copy link
Member

At present, there is no way to define these kinds of re-usable functions in Copilot. It is a good idea to have them, but it's not in our immediate plans because we are working on other features.

The only way possible as far as I can think is to rely on existing optimizers for C code that could factorize that out.

That being said: is the size of the .text sections being generated a serious issue? (I see how it could potentially be, but is there a real-world problem that needed solving and you could not use copilot for for this reason?)

@ivanperez-keera ivanperez-keera added feature request Request or advice for a feature question Further information is requested labels Feb 28, 2022
@antanas-kalkauskas-sensmetry
Copy link
Author

We are evaluating using Copilot in a scenario where there is significant function reuse for many input signals. In our initial explorations, the increase of ROM relative to a reasonable handwritten code would be a factor of 10 (if not more) and this makes it infeasible to use Copilot code in an embedded system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Request or advice for a feature question Further information is requested
Development

No branches or pull requests

2 participants