[latex3-commits] [git/LaTeX3-latex3-latex3] main: Speed up all tl_map functions 2-8 fold (f31d4d77f)

Bruno Le Floch blflatex at gmail.com
Fri May 14 23:52:40 CEST 2021


Repository : https://github.com/latex3/latex3
On branch  : main
Link       : https://github.com/latex3/latex3/commit/f31d4d77fc859841589870ed773d105b4975aa4c

>---------------------------------------------------------------

commit f31d4d77fc859841589870ed773d105b4975aa4c
Author: Bruno Le Floch <blflatex at gmail.com>
Date:   Fri May 14 23:50:40 2021 +0200

    Speed up all tl_map functions 2-8 fold


>---------------------------------------------------------------

f31d4d77fc859841589870ed773d105b4975aa4c
 l3kernel/CHANGELOG.md |   3 ++
 l3kernel/l3tl.dtx     | 105 ++++++++++++++++++++++++++++++--------------------
 2 files changed, 66 insertions(+), 42 deletions(-)

diff --git a/l3kernel/CHANGELOG.md b/l3kernel/CHANGELOG.md
index eb1cc9499..bae75dd08 100644
--- a/l3kernel/CHANGELOG.md
+++ b/l3kernel/CHANGELOG.md
@@ -19,6 +19,9 @@ this project uses date-based 'snapshot' version identifiers.
 ### Fixed
 - Checking brace balance in all regex functions (issue #377)
 
+### Changed
+- Speed up mapping functions in l3tl
+
 ## [2021-05-11]
 
 ### Added
diff --git a/l3kernel/l3tl.dtx b/l3kernel/l3tl.dtx
index 4967c19a6..68b4e46c4 100644
--- a/l3kernel/l3tl.dtx
+++ b/l3kernel/l3tl.dtx
@@ -2465,37 +2465,56 @@
 %
 % \subsection{Mapping over token lists}
 %
-% \begin{macro}{\tl_map_function:nN}
-% \begin{macro}{\tl_map_function:NN, \tl_map_function:cN}
-% \begin{macro}{\@@_map_function:Nn}
-%   Expandable loop macro for token lists. These have the advantage of not
-%   needing to test if the argument is empty, because if it is, the stop
-%   marker is read immediately and the loop terminated.
+% \begin{macro}
+%   {
+%     \tl_map_function:nN, \tl_map_function:NN, \tl_map_function:cN,
+%     \@@_map_function:Nnnnnnnnn, \@@_map_function_end:w,
+%     \@@_use_none_delimit_by_s_stop:w
+%   }
+%   Expandable loop macro for token lists.  We use the internal scan
+%   mark \cs{s_@@_stop} (defined later), which is not allowed to show up
+%   in the token list |#1| since it is internal to \pkg{l3tl}.  This
+%   allows us a very fast test of whether some \meta{item} is the
+%   end-marker \cs{s_@@_stop}, namely call
+%   \cs{@@_use_none_delimit_by_s_stop:w} \meta{item} \meta{function}
+%   \cs{s_@@_stop}, which calls \meta{function} if the \meta{item} is
+%   the end-marker.  To speed up the loop even more, only test one out
+%   of eight items, and once we hit one of the eight end-markers,
+%   go more slowly through the last few items of the list using
+%   \cs{@@_map_function_end:w}.
 %    \begin{macrocode}
 \cs_new:Npn \tl_map_function:nN #1#2
   {
-    \@@_map_function:Nn #2 #1
-      \q_@@_recursion_tail
+    \@@_map_function:Nnnnnnnnn #2 #1
+      \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+      \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
     \prg_break_point:Nn \tl_map_break: { }
   }
 \cs_new:Npn \tl_map_function:NN
   { \exp_args:No \tl_map_function:nN }
-\cs_new:Npn \@@_map_function:Nn #1#2
+\cs_generate_variant:Nn \tl_map_function:NN { c }
+\cs_new:Npn \@@_map_function:Nnnnnnnnn #1#2#3#4#5#6#7#8#9
   {
-    \@@_if_recursion_tail_break:nN {#2} \tl_map_break:
-    #1 {#2} \@@_map_function:Nn #1
+    \@@_use_none_delimit_by_s_stop:w
+      #9 \@@_map_function_end:w \s_@@_stop
+    #1 {#2} #1 {#3} #1 {#4} #1 {#5} #1 {#6} #1 {#7} #1 {#8} #1 {#9}
+    \@@_map_function:Nnnnnnnnn #1
   }
-\cs_generate_variant:Nn \tl_map_function:NN { c }
+\cs_new:Npn \@@_map_function_end:w \s_@@_stop #1#2
+  {
+    \@@_use_none_delimit_by_s_stop:w #2 \tl_map_break: \s_@@_stop
+    #1 {#2}
+    \@@_map_function_end:w \s_@@_stop
+  }
+\cs_new:Npn \@@_use_none_delimit_by_s_stop:w #1 \s_@@_stop { }
 %    \end{macrocode}
 % \end{macro}
-% \end{macro}
-% \end{macro}
 %
 % \begin{macro}{\tl_map_inline:nn}
 % \begin{macro}{\tl_map_inline:Nn, \tl_map_inline:cn}
 %   The inline functions are straight forward by now. We use a little
 %   trick with the counter \cs{g__kernel_prg_map_int} to make
-%   them nestable. We can also make use of \cs{@@_map_function:Nn}
+%   them nestable. We can also make use of \cs{@@_map_function:Nnnnnnnnn}
 %   from before.
 %    \begin{macrocode}
 \cs_new_protected:Npn \tl_map_inline:nn #1#2
@@ -2503,9 +2522,11 @@
     \int_gincr:N \g__kernel_prg_map_int
     \cs_gset_protected:cpn
       { @@_map_ \int_use:N \g__kernel_prg_map_int :w } ##1 {#2}
-    \exp_args:Nc \@@_map_function:Nn
+    \exp_args:Nc \@@_map_function:Nnnnnnnnn
       { @@_map_ \int_use:N \g__kernel_prg_map_int :w }
-      #1 \q_@@_recursion_tail
+      #1
+      \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+      \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
     \prg_break_point:Nn \tl_map_break:
       { \int_gdecr:N \g__kernel_prg_map_int }
   }
@@ -2516,54 +2537,54 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}{\tl_map_tokens:nn}
-% \begin{macro}{\tl_map_tokens:Nn, \tl_map_tokens:cn}
-% \begin{macro}{\@@_map_tokens:nn}
+% \begin{macro}
+%   {
+%     \tl_map_tokens:nn, \tl_map_tokens:Nn, \tl_map_tokens:cn,
+%     \@@_map_tokens:nnnnnnnnn, \@@_map_tokens_end:w
+%   }
 %   Much like the function mapping.
 %    \begin{macrocode}
 \cs_new:Npn \tl_map_tokens:nn #1#2
   {
-    \@@_map_tokens:nn {#2} #1
-      \q_@@_recursion_tail
+    \@@_map_tokens:nnnnnnnnn {#2} #1
+      \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
+      \s_@@_stop \s_@@_stop \s_@@_stop \s_@@_stop
     \prg_break_point:Nn \tl_map_break: { }
   }
 \cs_new:Npn \tl_map_tokens:Nn
   { \exp_args:No \tl_map_tokens:nn }
 \cs_generate_variant:Nn \tl_map_tokens:Nn { c }
-\cs_new:Npn \@@_map_tokens:nn #1#2
+\cs_new:Npn \@@_map_tokens:nnnnnnnnn #1#2#3#4#5#6#7#8#9
   {
-    \@@_if_recursion_tail_break:nN {#2} \tl_map_break:
-    \use:n {#1} {#2}
-    \@@_map_tokens:nn {#1}
+    \@@_use_none_delimit_by_s_stop:w
+      #9 \@@_map_tokens_end:w \s_@@_stop
+    \use:n {#1} {#2} \use:n {#1} {#3} \use:n {#1} {#4} \use:n {#1} {#5}
+    \use:n {#1} {#6} \use:n {#1} {#7} \use:n {#1} {#8} \use:n {#1} {#9}
+    \@@_map_tokens:nnnnnnnnn {#1}
+  }
+\cs_new:Npn \@@_map_tokens_end:w \s_@@_stop \use:n #1#2
+  {
+    \@@_use_none_delimit_by_s_stop:w #2 \tl_map_break: \s_@@_stop
+    #1 {#2}
+    \@@_map_tokens_end:w \s_@@_stop
   }
 %    \end{macrocode}
 % \end{macro}
-% \end{macro}
-% \end{macro}
 %
 % \begin{macro}{\tl_map_variable:nNn}
 % \begin{macro}{\tl_map_variable:NNn, \tl_map_variable:cNn}
 % \begin{macro}{\@@_map_variable:Nnn}
-%   \cs{tl_map_variable:nNn} \meta{token list} \meta{tl~var}
-%   \meta{action} assigns \meta{tl~var} to each element and executes
+%   \cs{tl_map_variable:nNn} \Arg{token list} \meta{tl~var}
+%   \Arg{action} assigns \meta{tl~var} to each element and executes
 %   \meta{action}.  The assignment to \meta{tl~var} is done after the
 %   quark test so that this variable does not get set to a quark.
 %    \begin{macrocode}
 \cs_new_protected:Npn \tl_map_variable:nNn #1#2#3
-  {
-    \@@_map_variable:Nnn #2 {#3} #1
-      \q_@@_recursion_tail
-    \prg_break_point:Nn \tl_map_break: { }
-  }
+  { \tl_map_tokens:nn {#1} { \@@_map_variable:Nnn #2 {#3} } }
+\cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3
+  { \tl_set:Nn #1 {#3} #2 }
 \cs_new_protected:Npn \tl_map_variable:NNn
   { \exp_args:No \tl_map_variable:nNn }
-\cs_new_protected:Npn \@@_map_variable:Nnn #1#2#3
-  {
-    \@@_if_recursion_tail_break:nN {#3} \tl_map_break:
-    \tl_set:Nn #1 {#3}
-    \use:n {#2}
-    \@@_map_variable:Nnn #1 {#2}
-  }
 \cs_generate_variant:Nn \tl_map_variable:NNn { c }
 %    \end{macrocode}
 % \end{macro}





More information about the latex3-commits mailing list.