# Package com.singularsys.jep.misc.parallelparsing

package com.singularsys.jep.misc.parallelparsing

Classes for parallel parsing of equations. To allow parallel parsing you need a separate parser for each thread, and a thread-safe variable table.

## ConcurrentVariableTable

The `ConcurrentVariableTable` is a VariableTable backed by a `ConcurrentHashMap`. The `ConcurrentVariableTable.addVariable(String)` are atomic operations ensuring that variables are created only once.

## ParallelConfigurableParser

The `ParallelConfigurableParser` is a ConfigurableParser that is safe to use in multiple threads. The only parings method supported is `ParallelConfigurableParser.parse(Reader)` which creates new instances of the Tokenizer and GrammarParser for each invocation, so is safe to use in multiple threads.

The `ParallelConfigurableParser.restart(java.io.Reader)` and `ParallelConfigurableParser.continueParse()` methods are not implemented as they are not suitable for multiple threaded used. If you need to parse multiple expressions in parallel you can use the `ParallelConfigurableParser.newMultipleEquationParser(java.io.Reader)` method to create a `MultipleEquationParser` in each thread. The `MultipleEquationParser.continueParsing()` method returns the result of parsing the next expression in the input.

To provide compatibility with the Jep class when using the `ParallelConfigurableParser`, the `MultipleEquationParsingJep` class provides implementations of the `Jep.initMultiParse(java.io.Reader)` and `Jep.continueParsing()` methods that work in a single threaded environment by using a single `MultipleEquationParser`.

The Jep class can be created as follows:

     Jep jep = new Jep(
         new ParallelConfigurableParser(new StandardConfigurableParser()),
         new ConcurrentVariableTable());

A set of equations can be parsed in parallel as follows:

     List<String> equations = new ArrayList<>(
                    List.of("x=1","y=2","z=x+y"));
     // Service for executing threads
     ExecutorService service = Executors.newFixedThreadPool(20);
     // Used to wait for all threads to complete
     CountDownLatch latch = new CountDownLatch(equations.size());
     // The nodes created by the threads
     ConcurrentLinkedQueue<Node> nodes = new ConcurrentLinkedQueue<>();
     // Used to record failures
     AtomicInteger failures = new AtomicInteger();
     // loop over the equations, starting a new thread for each
     for (String eqn : equations) {
         service.execute(() -> {
             try {
                   // Parse the equation
                 var node = jep.parse(eqn);
                   // Add the node to the list
                 nodes.add(node);
             } catch (ParseException e) {
                 failures.incrementAndGet();
             }
             latch.countDown();
         });
     }
     // Wait for all threads to complete
     latch.await();
     // Print the parsed nodes. Order is random
     for (var node : nodes) {
         jep.println(node);
     }

## Creating a new parser per thread

An alternative to using the `ParallelConfigurableParser` is to use `ConfigurableParser#ConfigurableParser(Jep, ConfigurableParser)` constructor, to build a new ConfigurableParser instance for each thread that reuses the TokenMatchers, TokenFilters, and GrammarMatchers from an existing ConfigurableParser instance.

     Jep jep = new Jep(
         new StandardConfigurableParser(),
         new ConcurrentVariableTable());

     List<String> equations = new ArrayList<>(List.of("x=1", "y=2", "z=x+y"));
     // Service for executing threads
     ExecutorService service = Executors.newFixedThreadPool(numberOfThreads);
     // Used to wait for all threads to complete
     CountDownLatch latch = new CountDownLatch(equations.size());
     // The nodes created by the threads
     ConcurrentLinkedQueue<Node> nodes = new ConcurrentLinkedQueue<>();
     // Used to record failures
     AtomicInteger failures = new AtomicInteger();
     // loop over the equations, starting a new thread for each
     for (String eqn : equations) {
        service.execute(() -> {
           try {
               var myCp = new ConfigurableParser(jep, (ConfigurableParser) jep.getParser());
               // Parse the equation
               var node = myCp.parse(new StringReader(eqn));
               // Add the node to the list
               nodes.add(node);
           } catch (ParseException e) {
               failures.incrementAndGet();
           }
           latch.countDown();
        });
     }
     // Wait for all threads to complete
     latch.await();
     // Print the parsed nodes. Order is random
     for (var node : nodes) {
         jep.println(node);
     }

This is marginally faster. Slower alternatives build a new `StandardConfigurableParser` or `StandardParser` instance in each thread. A detailed comparison of the different methods can be found at `ParallelParsingTest`.

See Also:
  - `ConcurrentVariableTableTest`
  - `ParallelParsingTest`

- Related Packages
  Package
  Description
  com.singularsys.jep.misc
  Miscellaneous components.
- Classes
  Class
  Description
  ConcurrentVariableTable
  A variable table backed by a ConcurrentHashMap that is safe for concurrent access.
  MultipleEquationParser
  A parser which can be used to parse multiple expressions in sequence.
  MultipleEquationParsingJep
  A Jep which uses a `ParallelConfigurableParser` providing implementations of the `Jep.initMultiParse(java.io.Reader)` and `MultipleEquationParsingJep.continueParsing()` methods.
  ParallelConfigurableParser
  A version of the ConfigurableParser that can parse expressions in multiple threads.