Inline intermediate any/all classifications

This is another significant milestone.

The next logical step with classification optimization is to inline all of
those intermediate classifications generated from any and all blocks, since
there are so many of them.  This means having the parent classification
absorb all dependencies; not output dependencies for the classification; not
compile the assignments for those classifications; and to inline them at the
match site.  They’re used only once, since they’re generated for each
individual block.

We need to keep the actual classification generation around (and just inline
them) for now, probably until TAMER, because we depend upon their symbol for
determining their dimensionality, which we need for the optimization work we
just did---we must inline them into the proper group (matrix, vector, or
scalar).

The optimization work done up to this point had inlining in mind---only a
little bit of work was needed to make sure that every classification can
simply be stripped of its assignment and be a valid expression that can be
inlined in place of the original reference.

The result of that was predictably significant for the `ui/package` program
that I've been testing with:

  - 4,514 classifications were inlined;
  - The file size dropped to 7.5MiB (from 8.2MiB previously---remember that
    we started at 16MiB); and
  - GC ticks were cut in half, from 67->31.

Unfortunately, this optimization added nearly 1m of time to the compilation
of that program.  Speaking from the future: the UI build optimizations in
liza-proguic were introduced to offset this difference (and provide a net
gain in performance).
master
Mike Gerwitz 2021-01-29 17:00:01 -05:00
parent 97caefab1b
commit 7dbb653624
3 changed files with 98 additions and 16 deletions

View File

@ -539,7 +539,8 @@
-->
<template mode="compile" priority="7"
match="lv:classify[ count( lv:match ) = 1
and lv:match/@value='TRUE' ]">
and lv:match/@value='TRUE'
and not( lv:match/@preproc:inline ) ]">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<variable name="src" as="xs:string"
@ -631,7 +632,13 @@
<template match="lv:classify" mode="compile" priority="5">
<param name="symtable-map" as="map(*)" tunnel="yes" />
<sequence select="compiler:compile-classify( $symtable-map, . )" />
<sequence select="compiler:compile-classify-assign( $symtable-map, . )" />
</template>
<template mode="compile" priority="8"
match="lv:classify[ @preproc:inline='true' ]">
<!-- emit nothing; it'll be inlined at the match site -->
</template>
@ -672,21 +679,44 @@
$outer, ',', $inner, ')' )" />
</function>
<function name="compiler:compile-classify-assign" as="xs:string">
<param name="symtable-map" as="map(*)" />
<param name="classify" as="element( lv:classify )" />
<sequence select="string-join(
( $compiler:nl,
compiler:compile-classify(
$symtable-map, $classify ) ) )" />
</function>
<function name="compiler:compile-classify-inline" as="xs:string">
<param name="symtable-map" as="map(*)" />
<param name="classify" as="element( lv:classify )" />
<!-- keep only the JS expression, grouping to ensure that surrounding
expressions (scalars, specifically, that lack grouping) maintain
their precedence -->
<sequence select="concat(
'(',
compiler:compile-classify(
$symtable-map, $classify )[ 2 ],
')' )" />
</function>
<!--
Generate code to perform a classification
Based on the criteria provided by the classification, generate and store the
result of a boolean expression performing the classification using global
arguments.
@return generated classification expression
This return a sequence of (assignment prefix, compiled js, assignment
suffix); the caller should keep the assignment prefix and suffix for
normal compilation, but should keep only the JS portion (which is a
standalone expression) for inlining.
-->
<function name="compiler:compile-classify" as="xs:string+">
<param name="symtable-map" as="map(*)" />
<param name="classify" as="element( lv:classify )" />
<value-of select="$compiler:nl" />
<variable name="dest" as="xs:string"
select="compiler:class-yields-var( $classify )" />
@ -769,8 +799,12 @@
$js-vec,
$js-matrix ) )" />
<!-- sequence of (assignment prefix, js, assignment suffix); it's up to
the caller to determine which of these to keep -->
<sequence select="concat( $var, '=', $reduce,
'(', $yield-to, '=', $js, ');' )" />
'(', $yield-to, '=' ),
$js,
');'" />
</function>
@ -1122,14 +1156,32 @@
<param name="symtable-map" as="map(*)" />
<param name="match" as="element( lv:match )" />
<variable name="sym" as="element( preproc:sym )"
select="$symtable-map( $match/@on )" />
<choose>
<when test="$match/@preproc:inline='true'">
<variable name="classify" as="element( lv:classify )?"
select="( $match/parent::lv:classify
/preceding-sibling::lv:classify[ @yields=$match/@on ] )[1]" />
<if test="empty( $classify )">
<message terminate="yes"
select="concat( 'internal error: inline: ',
'cannot locate class `', $match/@on, '''' )" />
</if>
<variable name="var" as="xs:string"
select="if ( $sym/@type = 'const' ) then 'C' else 'A'" />
<sequence select="compiler:compile-classify-inline(
$symtable-map, $classify )" />
</when>
<sequence select="concat( $var, '[''', $match/@on, ''']' )" />
<otherwise>
<variable name="sym" as="element( preproc:sym )"
select="$symtable-map( $match/@on )" />
<variable name="var" as="xs:string"
select="if ( $sym/@type = 'const' ) then 'C' else 'A'" />
<sequence select="concat( $var, '[''', $match/@on, ''']' )" />
</otherwise>
</choose>
</function>

View File

@ -312,6 +312,10 @@
<next-match />
</template>
<template mode="preproc:depgen" priority="9"
match="lv:classify[ preproc:inline='true' ]">
<!-- ignore; dependencies will be inlined -->
</template>
<template mode="preproc:depgen" priority="7"
match="lv:classify">
@ -460,6 +464,29 @@
</template>
<!--
Inlined matches will not be counted as dependencies themselves, but their
dependencies are our own
-->
<template match="lv:match[ @preproc:inline='true' ]"
mode="preproc:depgen" priority="7">
<variable name="self" as="element( lv:match )" select="." />
<variable name="classify" as="element( lv:classify )?"
select="( parent::lv:classify
/preceding-sibling::lv:classify[ @yields=$self/@on ] )[1]" />
<if test="empty( $classify )">
<message terminate="yes"
select="concat( 'internal error: inline depgen: ',
'cannot locate class `', @on, '''' )" />
</if>
<apply-templates mode="preproc:depgen"
select="$classify/element()" />
</template>
<template match="lv:match[ @value ]" mode="preproc:depgen" priority="5">
<!-- process the @value -->
<call-template name="preproc:depgen-c-normal">

View File

@ -422,6 +422,7 @@
<lv:classify as="{$id}" yields="{$yields}"
preproc:generated="true"
preproc:generated-from="{$parent-name}"
preproc:inline="true"
desc="(generated from predicate group of {$parent-name}">
<if test="local-name() = 'any'">
<attribute name="any" select="'true'" />
@ -431,7 +432,9 @@
</lv:classify>
<!-- this will remain in its place -->
<lv:match on="{$yields}" value="TRUE" preproc:generated="true" />
<lv:match on="{$yields}" value="TRUE"
preproc:generated="true"
preproc:inline="true" />
</template>