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

[WIP] Codegen draft #21

Closed
wants to merge 1 commit into from
Closed

[WIP] Codegen draft #21

wants to merge 1 commit into from

Conversation

fterrazzoni
Copy link
Contributor

@fterrazzoni fterrazzoni commented Jul 23, 2017

Work in progress / broken / hardcore code quality

BABL does not try to be efficient today, whereas its static structure makes it possible to do much better. Also, BABL is not really GC-friendly, because it tends to generate many temporary objects during rendering. For instance, a new Babl::Rendering::Context is instantiated every time a property is accessed.

This PR is an attempt to improve performances. It does so by generating Ruby code on the fly during the compilation of the BABL template. The generated Ruby class contains everything needed, which means that the Node tree can be GC-ed after that.

Design goals are to :

  • Reduce memory pressure
  • Keep the generated code as small as possible

Benchmark :

Branch : master (2017-08-21)

16.760000   0.680000  17.440000 ( 17.467316)
Allocations: 19225178

Branch: codegenfailed (2017-08-21)

 4.610000   0.230000   4.840000 (  4.853330)
Allocations: 4070534

Run time and the number of allocations is divided by 4.

Current status :

  • This PoC is "working" but the compilation time is really slow for some BABL templates. I plan to rewrite the (crappy) inlining mechanism differently, because it scales poorly.

@antoinedc antoinedc force-pushed the codegenfailed branch 4 times, most recently from 4dfb7f4 to a125289 Compare July 23, 2017 17:17
@fterrazzoni
Copy link
Contributor Author

fterrazzoni commented Jul 23, 2017

Example on this BABL template

{
    list: pin(:size) { |size|
        each.object(
            id: _.integer,
            static_val: { a: 1 },
            blabla: _.nullable.object(:status),
            outer_list_size: size,
            prop4: _,
            nested: _.each.nav(:sub).object(
                prop1: _.string,
                prop2: _.nullable.number,
                prop3: _.boolean,
                prop4: _,
                prop5: _,
                prop6: _,
                parent_id: [parent.parent.parent.nav(:id)]
            )
        )
    }
}
def x156(v0)
{:list => (x0(v0,(R0)).map { |l0| ({:id => (begin; l1 = x1((R1),((l0)),(:id)); ((::Integer === ((l1)) ? ((l1)) : (Babl::Nodes::Shared::ErrorHandling.raise_message(
    'Expected type integer:' + ((l1)).inspect, (R1)
)
))); end),
:static_val => ({:a => (1)}),
:blabla => (begin; l2 = x1((R2),((l0)),(:blabla)); (((                        l4 = x15((R6),((l2)),(R5),(l2))
                        x14((l4))
))?((nil)):(((true))?(({:status => (begin; l3 = x1((R4),((l2)),(:status)); (x10(((l3)),(R4))); end)})):((raise Errors::RenderingError, R3
)))); end),
:outer_list_size => (begin; l5 = x1((R7),(v0),(:size)); (x10(((l5)),(R7))); end),
:prop4 => (begin; l6 = x1((R8),((l0)),(:prop4)); (x10(((l6)),(R8))); end),
:nested => (begin; l7 = x1((R9),((l0)),(:nested)); (x0((l7),(R9)).map { |l8| (begin; l9 = x1((R10),((l8)),(:sub)); ({:prop1 => (begin; l10 = x1((R11),((l9)),(:prop1)); ((::String === ((l10)) ? ((l10)) : (Babl::Nodes::Shared::ErrorHandling.raise_message(
    'Expected type string:' + ((l10)).inspect, (R11)
)
))); end),
:prop2 => (begin; l11 = x1((R12),((l9)),(:prop2)); (((                        l12 = x15((R13),((l11)),(R5),(l11))
                        x14((l12))
))?((nil)):(((true))?(((::Numeric === ((l11)) ? ((l11)) : (Babl::Nodes::Shared::ErrorHandling.raise_message(
    'Expected type number:' + ((l11)).inspect, (R12)
)
)))):((raise Errors::RenderingError, R3
)))); end),
:prop3 => (begin; l13 = x1((R14),((l9)),(:prop3)); ((::TrueClass === ((l13))||::FalseClass === ((l13)) ? ((l13)) : (Babl::Nodes::Shared::ErrorHandling.raise_message(
    'Expected type boolean:' + ((l13)).inspect, (R14)
)
))); end),
:prop4 => (begin; l14 = x1((R15),((l9)),(:prop4)); (x10(((l14)),(R15))); end),
:prop5 => (begin; l15 = x1((R16),((l9)),(:prop5)); (x10(((l15)),(R16))); end),
:prop6 => (begin; l16 = x1((R17),((l9)),(:prop6)); (x10(((l16)),(R17))); end),
:parent_id => ([(begin; l17 = x1((R1),((l0)),(:id)); (x10(((l17)),(R1))); end)])}); end) }); end)}) })}
end

def x0(v0,v1)
unless ::Enumerable === v0
    Babl::Nodes::Shared::ErrorHandling.raise_message(
        'Not enumerable : ' + v0.inspect + "\n",
        v1
    )
end
v0

end

def x1(v0,v1,v2)
begin
    ::Hash === v1 ? v1.fetch(v2) : v1.__send__(v2)
rescue ::StandardError => e
    Babl::Nodes::Shared::ErrorHandling.raise_enriched(e, v0)
end

end

def x10(v0,v1)
begin
    Babl::Nodes::TerminalValue.instance.render_object(v0)
rescue ::Babl::Nodes::TerminalValue::TerminalValueError => e
    Babl::Nodes::Shared::ErrorHandling.raise_enriched(e, v1 + e.babl_stack)
end

end

def x15(v0,v1,v2,v3)
begin
    v2.call(x14(v3))
rescue ::StandardError => e
    Babl::Nodes::Shared::ErrorHandling.raise_enriched(e, v0)
end

end

def x14(v0)
v0
end
def evaluate(v0)
    x156(v0)
end
R0 = []
R1 = [:"#", :id]
R2 = [:"#", :blabla]
R3 = "A least one switch() condition must be taken"
R4 = [:"#", :blabla, :status]
R5 = #<Proc:0x007f97d288eb88(&:nil?)>
R6 = [:"#", :blabla, :__block__]
R7 = [:size]
R8 = [:"#", :prop4]
R9 = [:"#", :nested]
R10 = [:"#", :nested, :"#", :sub]
R11 = [:"#", :nested, :"#", :sub, :prop1]
R12 = [:"#", :nested, :"#", :sub, :prop2]
R13 = [:"#", :nested, :"#", :sub, :prop2, :__block__]
R14 = [:"#", :nested, :"#", :sub, :prop3]
R15 = [:"#", :nested, :"#", :sub, :prop4]
R16 = [:"#", :nested, :"#", :sub, :prop5]
R17 = [:"#", :nested, :"#", :sub, :prop6]

@antoinedc antoinedc force-pushed the codegenfailed branch 5 times, most recently from 2a4824b to 02cac56 Compare August 7, 2017 21:18
@antoinedc antoinedc force-pushed the codegenfailed branch 3 times, most recently from fa4bd60 to ab59d4f Compare August 13, 2017 16:05
@fterrazzoni fterrazzoni force-pushed the codegenfailed branch 2 times, most recently from 4b5d434 to 8505041 Compare September 3, 2017 12:41
@fterrazzoni fterrazzoni changed the title Codegen draft [WIP] Codegen draft Sep 3, 2017
@fterrazzoni fterrazzoni force-pushed the codegenfailed branch 2 times, most recently from 5c3893d to 1f98936 Compare September 22, 2017 23:24
@fterrazzoni
Copy link
Contributor Author

Close: current rendering time is acceptable and BABL performs very well compared to the alternatives (RABL, etc). Efforts should be put on making compilation faster. Complex templates are pretty slow to compile today.

@fterrazzoni fterrazzoni closed this Jul 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant