You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by ni...@apache.org on 2017/08/06 17:59:07 UTC

[09/33] lucenenet git commit: Ported Lucene.Net.Benchmark + tests

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Stats/Points.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Stats/Points.cs b/src/Lucene.Net.Benchmark/ByTask/Stats/Points.cs
new file mode 100644
index 0000000..ed54d92
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Stats/Points.cs
@@ -0,0 +1,108 @@
+using Lucene.Net.Benchmarks.ByTask.Tasks;
+using Lucene.Net.Benchmarks.ByTask.Utils;
+using System.Collections.Generic;
+
+namespace Lucene.Net.Benchmarks.ByTask.Stats
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Test run data points collected as the test proceeds.
+    /// </summary>
+    public class Points
+    {
+        // stat points ordered by their start time. 
+        // for now we collect points as TaskStats objects.
+        // later might optimize to collect only native data.
+        private List<TaskStats> points = new List<TaskStats>();
+
+        private int nextTaskRunNum = 0;
+
+        private TaskStats currentStats;
+
+        /// <summary>
+        /// Create a Points statistics object.
+        /// </summary>
+        public Points(Config config)
+        {
+        }
+
+        /// <summary>
+        /// Gets the current task stats.
+        /// The actual task stats are returned, so caller should not modify this task stats.
+        /// </summary>
+        public virtual IList<TaskStats> TaskStats
+        {
+            get { return points; }
+        }
+
+        /// <summary>
+        /// Mark that a task is starting.
+        /// Create a task stats for it and store it as a point.
+        /// </summary>
+        /// <param name="task">The starting task.</param>
+        /// <param name="round">The new task stats created for the starting task.</param>
+        /// <returns></returns>
+        public virtual TaskStats MarkTaskStart(PerfTask task, int round)
+        {
+            lock (this)
+            {
+                TaskStats stats = new TaskStats(task, NextTaskRunNum(), round);
+                this.currentStats = stats;
+                points.Add(stats);
+                return stats;
+            }
+        }
+
+        public virtual TaskStats CurrentStats
+        {
+            get { return currentStats; }
+        }
+
+        // return next task num
+        private int NextTaskRunNum()
+        {
+            lock (this)
+            {
+                return nextTaskRunNum++;
+            }
+        }
+
+        /// <summary>
+        /// mark the end of a task
+        /// </summary>
+        public virtual void MarkTaskEnd(TaskStats stats, int count)
+        {
+            lock (this)
+            {
+                int numParallelTasks = nextTaskRunNum - 1 - stats.TaskRunNum;
+                // note: if the stats were cleared, might be that this stats object is 
+                // no longer in points, but this is just ok.
+                stats.MarkEnd(numParallelTasks, count);
+            }
+        }
+
+        /// <summary>
+        /// Clear all data, prepare for more tests.
+        /// </summary>
+        public virtual void ClearData()
+        {
+            points.Clear();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Stats/Report.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Stats/Report.cs b/src/Lucene.Net.Benchmark/ByTask/Stats/Report.cs
new file mode 100644
index 0000000..59fd725
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Stats/Report.cs
@@ -0,0 +1,70 @@
+namespace Lucene.Net.Benchmarks.ByTask.Stats
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Textual report of current statistics.
+    /// </summary>
+    public class Report
+    {
+        private string text;
+        private int size;
+        private int outOf;
+        private int reported;
+
+        public Report(string text, int size, int reported, int outOf)
+        {
+            this.text = text;
+            this.size = size;
+            this.reported = reported;
+            this.outOf = outOf;
+        }
+
+        /// <summary>
+        /// Gets total number of stats points when this report was created.
+        /// </summary>
+        public virtual int OutOf
+        {
+            get { return outOf; }
+        }
+
+        /// <summary>
+        /// Gets number of lines in the report.
+        /// </summary>
+        public virtual int Count
+        {
+            get { return size; }
+        }
+
+        /// <summary>
+        /// Gets the report text.
+        /// </summary>
+        public virtual string Text
+        {
+            get { return text; }
+        }
+
+        /// <summary>
+        /// Gets number of stats points represented in this report.
+        /// </summary>
+        public virtual int Reported
+        {
+            get { return reported; }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Stats/TaskStats.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Stats/TaskStats.cs b/src/Lucene.Net.Benchmark/ByTask/Stats/TaskStats.cs
new file mode 100644
index 0000000..4d32c7b
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Stats/TaskStats.cs
@@ -0,0 +1,237 @@
+using Lucene.Net.Benchmarks.ByTask.Tasks;
+using Lucene.Net.Support;
+using System;
+using System.Diagnostics;
+using System.Text;
+
+namespace Lucene.Net.Benchmarks.ByTask.Stats
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Statistics for a task run. 
+    /// <para/>
+    /// The same task can run more than once, but, if that task records statistics, 
+    /// each run would create its own TaskStats.
+    /// </summary>
+    public class TaskStats
+    {
+        /// <summary>Task for which data was collected.</summary>
+        private PerfTask task;
+
+        /// <summary>Round in which task run started.</summary>
+        private int round;
+
+        /// <summary>Task start time.</summary>
+        private long start;
+
+        /// <summary>Task elapsed time.  elapsed >= 0 indicates run completion!</summary>
+        private long elapsed = -1;
+
+        /// <summary>Max tot mem during task.</summary>
+        private long maxTotMem;
+
+        /// <summary>Max used mem during task.</summary>
+        private long maxUsedMem;
+
+        /// <summary>Serial run number of this task run in the perf run.</summary>
+        private int taskRunNum;
+
+        /// <summary>Number of other tasks that started to run while this task was still running.</summary>
+        private int numParallelTasks;
+
+        /// <summary>
+        /// Number of work items done by this task.
+        /// For indexing that can be number of docs added.
+        /// For warming that can be number of scanned items, etc. 
+        /// For repeating tasks, this is a sum over repetitions.
+        /// </summary>
+        private int count;
+
+        /// <summary>
+        /// Number of similar tasks aggregated into this record.   
+        /// Used when summing up on few runs/instances of similar tasks.
+        /// </summary>
+        private int numRuns = 1;
+
+        /// <summary>
+        /// Create a run data for a task that is starting now.
+        /// To be called from Points.
+        /// </summary>
+        internal TaskStats(PerfTask task, int taskRunNum, int round)
+        {
+            this.task = task;
+            this.taskRunNum = taskRunNum;
+            this.round = round;
+            maxTotMem = GC.GetTotalMemory(false); //Runtime.getRuntime().totalMemory();
+            maxUsedMem = maxTotMem; // - Runtime.getRuntime().freeMemory(); // LUCENENET TODO: available RAM
+            start = Stopwatch.GetTimestamp();
+        }
+
+        /// <summary>
+        /// Mark the end of a task.
+        /// </summary>
+        internal void MarkEnd(int numParallelTasks, int count)
+        {
+            elapsed = Support.Time.CurrentTimeMilliseconds();
+            long totMem = GC.GetTotalMemory(false); //Runtime.getRuntime().totalMemory();
+            if (totMem > maxTotMem)
+            {
+                maxTotMem = totMem;
+            }
+            long usedMem = totMem; //- Runtime.getRuntime().freeMemory(); // LUCENENET TODO: available RAM
+            if (usedMem > maxUsedMem)
+            {
+                maxUsedMem = usedMem;
+            }
+            this.numParallelTasks = numParallelTasks;
+            this.count = count;
+        }
+
+        private int[] countsByTime;
+        private long countsByTimeStepMSec;
+
+        public virtual void SetCountsByTime(int[] counts, long msecStep)
+        {
+            countsByTime = counts;
+            countsByTimeStepMSec = msecStep;
+        }
+
+        [WritableArray]
+        public virtual int[] GetCountsByTime()
+        {
+            return countsByTime; 
+        }
+
+        public virtual long CountsByTimeStepMSec
+        {
+            get { return countsByTimeStepMSec; }
+        }
+
+        /// <summary>Gets the taskRunNum.</summary>
+        public virtual int TaskRunNum
+        {
+            get { return taskRunNum; }
+        }
+
+        /// <seealso cref="object.ToString()"/>
+        public override string ToString()
+        {
+            StringBuilder res = new StringBuilder(task.GetName());
+            res.Append(" ");
+            res.Append(count);
+            res.Append(" ");
+            res.Append(elapsed);
+            return res.ToString();
+        }
+
+        /// <summary>Gets the count.</summary>
+        public virtual int Count
+        {
+            get { return count; }
+        }
+
+        /// <summary>Gets elapsed time.</summary>
+        public virtual long Elapsed
+        {
+            get { return elapsed; }
+        }
+
+        /// <summary>Gets the maxTotMem.</summary>
+        public virtual long MaxTotMem
+        {
+            get { return maxTotMem; }
+        }
+
+        /// <summary>Gets the maxUsedMem.</summary>
+        public virtual long MaxUsedMem
+        {
+            get { return maxUsedMem; }
+        }
+
+        /// <summary>Gets the numParallelTasks.</summary>
+        public virtual int NumParallelTasks
+        {
+            get { return numParallelTasks; }
+        }
+
+        /// <summary>Gets the task.</summary>
+        public virtual PerfTask Task
+        {
+            get { return task; }
+        }
+
+        /// <summary>Gets the numRuns.</summary>
+        public virtual int NumRuns
+        {
+            get { return numRuns; }
+        }
+
+        /// <summary>
+        /// Add data from another stat, for aggregation.
+        /// </summary>
+        /// <param name="stat2">The added stat data.</param>
+        public virtual void Add(TaskStats stat2)
+        {
+            numRuns += stat2.NumRuns;
+            elapsed += stat2.Elapsed;
+            maxTotMem += stat2.MaxTotMem;
+            maxUsedMem += stat2.MaxUsedMem;
+            count += stat2.Count;
+            if (round != stat2.round)
+            {
+                round = -1; // no meaning if aggregating tasks of different round. 
+            }
+
+            if (countsByTime != null && stat2.countsByTime != null)
+            {
+                if (countsByTimeStepMSec != stat2.countsByTimeStepMSec)
+                {
+                    throw new InvalidOperationException("different by-time msec step");
+                }
+                if (countsByTime.Length != stat2.countsByTime.Length)
+                {
+                    throw new InvalidOperationException("different by-time msec count");
+                }
+                for (int i = 0; i < stat2.countsByTime.Length; i++)
+                {
+                    countsByTime[i] += stat2.countsByTime[i];
+                }
+            }
+        }
+
+#if FEATURE_CLONEABLE
+        /// <seealso cref="ICloneable.Clone()"/>
+#endif
+        public virtual object Clone()
+        {
+            TaskStats c = (TaskStats)base.MemberwiseClone();
+            if (c.countsByTime != null)
+            {
+                c.countsByTime = (int[])c.countsByTime.Clone();
+            }
+            return c;
+        }
+
+        /// <summary>Gets the round number.</summary>
+        public virtual int Round
+        {
+            get { return round; }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/AddDocTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/AddDocTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/AddDocTask.cs
new file mode 100644
index 0000000..8e92f48
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/AddDocTask.cs
@@ -0,0 +1,93 @@
+using Lucene.Net.Benchmarks.ByTask.Feeds;
+using Lucene.Net.Documents;
+using System.Globalization;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Add a document, optionally of a certain size.
+    /// <para/>
+    /// Other side effects: none.
+    /// <para/>
+    /// Takes optional param: document size.
+    /// </summary>
+    public class AddDocTask : PerfTask
+    {
+        public AddDocTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        private int docSize = 0;
+
+        /// <summary>
+        /// Volatile data passed between <see cref="Setup()"/>, <see cref="DoLogic()"/>, <see cref="TearDown()"/>.
+        /// The doc is created at <see cref="Setup()"/> and added at <see cref="DoLogic()"/>. 
+        /// </summary>
+        protected Document m_doc = null;
+
+        public override void Setup()
+        {
+            base.Setup();
+            DocMaker docMaker = RunData.DocMaker;
+            if (docSize > 0)
+            {
+                m_doc = docMaker.MakeDocument(docSize);
+            }
+            else
+            {
+                m_doc = docMaker.MakeDocument();
+            }
+        }
+
+        public override void TearDown()
+        {
+            m_doc = null;
+            base.TearDown();
+        }
+
+        protected override string GetLogMessage(int recsCount)
+        {
+            return string.Format(CultureInfo.InvariantCulture, "added {0:N9} docs", recsCount);
+        }
+
+        public override int DoLogic()
+        {
+            RunData.IndexWriter.AddDocument(m_doc);
+            return 1;
+        }
+
+        /// <summary>
+        /// Set the params (docSize only).
+        /// </summary>
+        /// <param name="params">docSize, or 0 for no limit.</param>
+        public override void SetParams(string @params)
+        {
+            base.SetParams(@params);
+            docSize = (int)float.Parse(@params, CultureInfo.InvariantCulture);
+        }
+
+        /// <seealso cref="PerfTask.SupportsParams"/>
+        public override bool SupportsParams
+        {
+            get { return true; }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/AddFacetedDocTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/AddFacetedDocTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/AddFacetedDocTask.cs
new file mode 100644
index 0000000..6ae761c
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/AddFacetedDocTask.cs
@@ -0,0 +1,95 @@
+using Lucene.Net.Benchmarks.ByTask.Feeds;
+using Lucene.Net.Facet;
+using System.Collections.Generic;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Add a faceted document.
+    /// </summary>
+    /// <remarks>
+    /// Config properties:
+    /// <list type="bullet">
+    ///     <item>
+    ///         <term>with.facets</term>
+    ///         <description>
+    ///             &lt;tells whether to actually add any facets to the document| Default: true&gt;
+    ///             <para/>
+    ///             This config property allows to easily compare the performance of adding docs
+    ///             with and without facets. Note that facets are created even when this is
+    ///             <c>false</c>, just that they are not added to the document (nor to the taxonomy).
+    ///         </description>
+    ///     </item>
+    /// </list>
+    /// <para/>
+    /// See <see cref="AddDocTask"/> for general document parameters and configuration.
+    /// <para/>
+    /// Makes use of the <see cref="FacetSource"/> in effect - see <see cref="PerfRunData"/> for
+    /// facet source settings.
+    /// </remarks>
+    public class AddFacetedDocTask : AddDocTask
+    {
+        private FacetsConfig config;
+
+        public AddFacetedDocTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        public override void Setup()
+        {
+            base.Setup();
+            if (config == null)
+            {
+                bool withFacets = RunData.Config.Get("with.facets", true);
+                if (withFacets)
+                {
+                    FacetSource facetsSource = RunData.FacetSource;
+                    config = new FacetsConfig();
+                    facetsSource.Configure(config);
+                }
+            }
+        }
+
+        protected override string GetLogMessage(int recsCount)
+        {
+            if (config == null)
+            {
+                return base.GetLogMessage(recsCount);
+            }
+            return base.GetLogMessage(recsCount) + " with facets";
+        }
+
+        public override int DoLogic()
+        {
+            if (config != null)
+            {
+                List<FacetField> facets = new List<FacetField>();
+                RunData.FacetSource.GetNextFacets(facets);
+                foreach (FacetField ff in facets)
+                {
+                    m_doc.Add(ff);
+                }
+                m_doc = config.Build(RunData.TaxonomyWriter, m_doc);
+            }
+            return base.DoLogic();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/AddIndexesTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/AddIndexesTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/AddIndexesTask.cs
new file mode 100644
index 0000000..f05ca32
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/AddIndexesTask.cs
@@ -0,0 +1,104 @@
+using Lucene.Net.Index;
+using Lucene.Net.Store;
+using System;
+using System.IO;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Adds an input index to an existing index, using
+    /// <see cref="IndexWriter.AddIndexes(Store.Directory[])"/> or
+    /// <see cref="IndexWriter.AddIndexes(IndexReader[])"/>. The location of the input
+    /// index is specified by the parameter <see cref="ADDINDEXES_INPUT_DIR"/> and is
+    /// assumed to be a directory on the file system.
+    /// <para/>
+    /// Takes optional parameter <see cref="useAddIndexesDir"/> which specifies which
+    /// AddIndexes variant to use (defaults to <c>true</c>, to use <c>AddIndexes(Directory)</c>).
+    /// </summary>
+    public class AddIndexesTask : PerfTask
+    {
+        public static readonly string ADDINDEXES_INPUT_DIR = "addindexes.input.dir";
+
+        public AddIndexesTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        private bool useAddIndexesDir = true;
+        private FSDirectory inputDir;
+
+        public override void Setup()
+        {
+            base.Setup();
+            string inputDirProp = RunData.Config.Get(ADDINDEXES_INPUT_DIR, null);
+            if (inputDirProp == null)
+            {
+                throw new ArgumentException("config parameter " + ADDINDEXES_INPUT_DIR + " not specified in configuration");
+            }
+            inputDir = FSDirectory.Open(new DirectoryInfo(inputDirProp));
+        }
+
+        public override int DoLogic()
+        {
+            IndexWriter writer = RunData.IndexWriter;
+            if (useAddIndexesDir)
+            {
+                writer.AddIndexes(inputDir);
+            }
+            else
+            {
+                IndexReader r = DirectoryReader.Open(inputDir);
+                try
+                {
+                    writer.AddIndexes(r);
+                }
+                finally
+                {
+                    r.Dispose();
+                }
+            }
+            return 1;
+        }
+
+        /// <summary>
+        /// Set the params (useAddIndexesDir only)
+        /// </summary>
+        /// <param name="params">
+        /// <c>useAddIndexesDir=true</c> for using <see cref="IndexWriter.AddIndexes(Store.Directory[])"/> or <c>false</c> for
+        /// using <see cref="IndexWriter.AddIndexes(IndexReader[])"/>. Defaults to <c>true</c>.
+        /// </param>
+        public override void SetParams(string @params)
+        {
+            base.SetParams(@params);
+            useAddIndexesDir = bool.Parse(@params);
+        }
+
+        public override bool SupportsParams
+        {
+            get { return true; }
+        }
+
+        public override void TearDown()
+        {
+            inputDir.Dispose();
+            base.TearDown();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/AnalyzerFactoryTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/AnalyzerFactoryTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/AnalyzerFactoryTask.cs
new file mode 100644
index 0000000..56b0114
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/AnalyzerFactoryTask.cs
@@ -0,0 +1,580 @@
+using Lucene.Net.Analysis.Util;
+using Lucene.Net.Benchmarks.ByTask.Utils;
+using Lucene.Net.Support.IO;
+using Lucene.Net.Util;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Text.RegularExpressions;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Analyzer factory construction task.  The name given to the constructed factory may
+    /// be given to <see cref="NewAnalyzerTask"/>, which will call <see cref="AnalyzerFactory.Create()"/>.
+    /// </summary>
+    /// <remarks>
+    /// Params are in the form argname:argvalue or argname:"argvalue" or argname:'argvalue';
+    /// use backslashes to escape '"' or "'" inside a quoted value when it's used as the enclosing
+    /// quotation mark,
+    /// <para/>
+    /// Specify params in a comma separated list of the following, in order:
+    /// <list type="number">
+    ///     <item><description>
+    ///         <list type="bullet">
+    ///             <item><description><b>Required</b>: <c>name:<i>analyzer-factory-name</i></c></description></item>
+    ///             <item><description>Optional: <c>positionIncrementGap:<i>int value</i></c> (default: 0)</description></item>
+    ///             <item><description>Optional: <c>offsetGap:<i>int value</i></c> (default: 1)</description></item>
+    ///         </list>
+    ///     </description></item>
+    ///     <item><description>zero or more CharFilterFactory's, followed by</description></item>
+    ///     <item><description>exactly one TokenizerFactory, followed by</description></item>
+    ///     <item><description>zero or more TokenFilterFactory's</description></item>
+    /// </list>
+    /// <para/>
+    /// Each component analysis factory map specify <tt>luceneMatchVersion</tt> (defaults to
+    /// <see cref="LuceneVersion.LUCENE_CURRENT"/>) and any of the args understood by the specified
+    /// *Factory class, in the above-describe param format.
+    /// <para/>
+    /// Example:
+    /// <code>
+    ///     -AnalyzerFactory(name:'strip html, fold to ascii, whitespace tokenize, max 10k tokens',
+    ///                      positionIncrementGap:100,
+    ///                      HTMLStripCharFilter,
+    ///                      MappingCharFilter(mapping:'mapping-FoldToASCII.txt'),
+    ///                      WhitespaceTokenizer(luceneMatchVersion:LUCENE_43),
+    ///                      TokenLimitFilter(maxTokenCount:10000, consumeAllTokens:false))
+    ///     [...]
+    ///     -NewAnalyzer('strip html, fold to ascii, whitespace tokenize, max 10k tokens')
+    /// </code>
+    /// <para/>
+    /// <see cref="AnalyzerFactory"/> will direct analysis component factories to look for resources
+    /// under the directory specified in the "work.dir" property.
+    /// </remarks>
+    public class AnalyzerFactoryTask : PerfTask
+    {
+        private static readonly string LUCENE_ANALYSIS_PACKAGE_PREFIX = "Lucene.Net.Analysis.";
+        private static readonly Regex ANALYSIS_COMPONENT_SUFFIX_PATTERN
+            = new Regex("(?s:(?:(?:Token|Char)?Filter|Tokenizer)(?:Factory)?)$", RegexOptions.Compiled);
+        private static readonly Regex TRAILING_DOT_ZERO_PATTERN = new Regex(@"\.0$", RegexOptions.Compiled);
+
+        private enum ArgType { ANALYZER_ARG, ANALYZER_ARG_OR_CHARFILTER_OR_TOKENIZER, TOKENFILTER }
+
+        string factoryName = null;
+        int? positionIncrementGap = null;
+        int? offsetGap = null;
+        private IList<CharFilterFactory> charFilterFactories = new List<CharFilterFactory>();
+        private TokenizerFactory tokenizerFactory = null;
+        private IList<TokenFilterFactory> tokenFilterFactories = new List<TokenFilterFactory>();
+
+        public AnalyzerFactoryTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        public override int DoLogic()
+        {
+            return 1;
+        }
+
+        /// <summary>
+        /// Sets the params.
+        /// Analysis component factory names may optionally include the "Factory" suffix.
+        /// </summary>
+        /// <param name="params">
+        /// analysis pipeline specification: name, (optional) positionIncrementGap,
+        /// (optional) offsetGap, 0+ CharFilterFactory's, 1 TokenizerFactory,
+        /// and 0+ TokenFilterFactory's
+        /// </param>
+        public override void SetParams(string @params)
+        {
+            base.SetParams(@params);
+            ArgType expectedArgType = ArgType.ANALYZER_ARG;
+
+            StreamTokenizer stok = new StreamTokenizer(new StringReader(@params));
+            stok.CommentChar('#');
+            stok.QuoteChar('"');
+            stok.QuoteChar('\'');
+            stok.IsEOLSignificant = false;
+            stok.OrdinaryChar('(');
+            stok.OrdinaryChar(')');
+            stok.OrdinaryChar(':');
+            stok.OrdinaryChar(',');
+            try
+            {
+                while (stok.NextToken() != StreamTokenizer.TT_EOF)
+                {
+                    switch (stok.TokenType)
+                    {
+                        case ',':
+                            {
+                                // Do nothing
+                                break;
+                            }
+                        case StreamTokenizer.TT_WORD:
+                            {
+                                if (expectedArgType.Equals(ArgType.ANALYZER_ARG))
+                                {
+                                    string argName = stok.StringValue;
+                                    if (!argName.Equals("name", StringComparison.OrdinalIgnoreCase)
+                                        && !argName.Equals("positionIncrementGap", StringComparison.OrdinalIgnoreCase)
+                                        && !argName.Equals("offsetGap", StringComparison.OrdinalIgnoreCase))
+                                    {
+                                        throw new Exception
+                                            ("Line #" + GetLineNumber(stok) + ": Missing 'name' param to AnalyzerFactory: '" + @params + "'");
+                                    }
+                                    stok.NextToken();
+                                    if (stok.TokenType != ':')
+                                    {
+                                        throw new Exception
+                                            ("Line #" + GetLineNumber(stok) + ": Missing ':' after '" + argName + "' param to AnalyzerFactory");
+                                    }
+
+                                    stok.NextToken();
+                                    string argValue = stok.StringValue;
+                                    switch (stok.TokenType)
+                                    {
+                                        case StreamTokenizer.TT_NUMBER:
+                                            {
+                                                argValue = stok.NumberValue.ToString(CultureInfo.InvariantCulture);
+                                                // Drop the ".0" from numbers, for integer arguments
+                                                argValue = TRAILING_DOT_ZERO_PATTERN.Replace(argValue, "", 1);
+                                                // Intentional fallthrough
+
+                                                if (argName.Equals("name", StringComparison.OrdinalIgnoreCase))
+                                                {
+                                                    factoryName = argValue;
+                                                    expectedArgType = ArgType.ANALYZER_ARG_OR_CHARFILTER_OR_TOKENIZER;
+                                                }
+                                                else
+                                                {
+                                                    int intArgValue = 0;
+                                                    try
+                                                    {
+                                                        intArgValue = int.Parse(argValue, CultureInfo.InvariantCulture);
+                                                    }
+                                                    catch (FormatException e)
+                                                    {
+                                                        throw new Exception
+                                                            ("Line #" + GetLineNumber(stok) + ": Exception parsing " + argName + " value '" + argValue + "'", e);
+                                                    }
+                                                    if (argName.Equals("positionIncrementGap", StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        positionIncrementGap = intArgValue;
+                                                    }
+                                                    else if (argName.Equals("offsetGap", StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        offsetGap = intArgValue;
+                                                    }
+                                                }
+                                                break;
+                                            }
+                                        case '"':
+                                        case '\'':
+                                        case StreamTokenizer.TT_WORD:
+                                            {
+                                                if (argName.Equals("name", StringComparison.OrdinalIgnoreCase))
+                                                {
+                                                    factoryName = argValue;
+                                                    expectedArgType = ArgType.ANALYZER_ARG_OR_CHARFILTER_OR_TOKENIZER;
+                                                }
+                                                else
+                                                {
+                                                    int intArgValue = 0;
+                                                    try
+                                                    {
+                                                        intArgValue = int.Parse(argValue, CultureInfo.InvariantCulture);
+                                                    }
+                                                    catch (FormatException e)
+                                                    {
+                                                        throw new Exception
+                                                            ("Line #" + GetLineNumber(stok) + ": Exception parsing " + argName + " value '" + argValue + "'", e);
+                                                    }
+                                                    if (argName.Equals("positionIncrementGap", StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        positionIncrementGap = intArgValue;
+                                                    }
+                                                    else if (argName.Equals("offsetGap", StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        offsetGap = intArgValue;
+                                                    }
+                                                }
+                                                break;
+                                            }
+                                        case StreamTokenizer.TT_EOF:
+                                            {
+                                                throw new Exception("Unexpected EOF: " + stok.ToString());
+                                            }
+                                        default:
+                                            {
+                                                throw new Exception
+                                                    ("Line #" + GetLineNumber(stok) + ": Unexpected token: " + stok.ToString());
+                                            }
+                                    }
+                                }
+                                else if (expectedArgType.Equals(ArgType.ANALYZER_ARG_OR_CHARFILTER_OR_TOKENIZER))
+                                {
+                                    string argName = stok.StringValue;
+
+                                    if (argName.Equals("positionIncrementGap", StringComparison.OrdinalIgnoreCase)
+                                        || argName.Equals("offsetGap", StringComparison.OrdinalIgnoreCase))
+                                    {
+                                        stok.NextToken();
+                                        if (stok.TokenType != ':')
+                                        {
+                                            throw new Exception
+                                                ("Line #" + GetLineNumber(stok) + ": Missing ':' after '" + argName + "' param to AnalyzerFactory");
+                                        }
+                                        stok.NextToken();
+                                        int intArgValue = (int)stok.NumberValue;
+                                        switch (stok.TokenType)
+                                        {
+                                            case '"':
+                                            case '\'':
+                                            case StreamTokenizer.TT_WORD:
+                                                {
+                                                    intArgValue = 0;
+                                                    try
+                                                    {
+                                                        intArgValue = int.Parse(stok.StringValue.Trim(), CultureInfo.InvariantCulture);
+                                                    }
+                                                    catch (FormatException e)
+                                                    {
+                                                        throw new Exception
+                                                            ("Line #" + GetLineNumber(stok) + ": Exception parsing " + argName + " value '" + stok.StringValue + "'", e);
+                                                    }
+                                                    // Intentional fall-through
+
+                                                    if (argName.Equals("positionIncrementGap", StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        positionIncrementGap = intArgValue;
+                                                    }
+                                                    else if (argName.Equals("offsetGap", StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        offsetGap = intArgValue;
+                                                    }
+                                                    break;
+                                                }
+                                            case StreamTokenizer.TT_NUMBER:
+                                                {
+                                                    if (argName.Equals("positionIncrementGap", StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        positionIncrementGap = intArgValue;
+                                                    }
+                                                    else if (argName.Equals("offsetGap", StringComparison.OrdinalIgnoreCase))
+                                                    {
+                                                        offsetGap = intArgValue;
+                                                    }
+                                                    break;
+                                                }
+                                            case StreamTokenizer.TT_EOF:
+                                                {
+                                                    throw new Exception("Unexpected EOF: " + stok.ToString());
+                                                }
+                                            default:
+                                                {
+                                                    throw new Exception
+                                                        ("Line #" + GetLineNumber(stok) + ": Unexpected token: " + stok.ToString());
+                                                }
+                                        }
+                                        break;
+                                    }
+                                    try
+                                    {
+                                        Type clazz;
+                                        clazz = LookupAnalysisClass(argName, typeof(CharFilterFactory));
+                                        CreateAnalysisPipelineComponent(stok, clazz);
+                                    }
+                                    catch (ArgumentException /*e*/)
+                                    {
+                                        try
+                                        {
+                                            Type clazz;
+                                            clazz = LookupAnalysisClass(argName, typeof(TokenizerFactory));
+                                            CreateAnalysisPipelineComponent(stok, clazz);
+                                            expectedArgType = ArgType.TOKENFILTER;
+                                        }
+                                        catch (ArgumentException e2)
+                                        {
+                                            throw new Exception("Line #" + GetLineNumber(stok) + ": Can't find class '"
+                                                                       + argName + "' as CharFilterFactory or TokenizerFactory", e2);
+                                        }
+                                    }
+                                }
+                                else
+                                { // expectedArgType = ArgType.TOKENFILTER
+                                    string className = stok.StringValue;
+                                    Type clazz;
+                                    try
+                                    {
+                                        clazz = LookupAnalysisClass(className, typeof(TokenFilterFactory));
+                                    }
+                                    catch (ArgumentException e)
+                                    {
+                                        throw new Exception
+                                            ("Line #" + GetLineNumber(stok) + ": Can't find class '" + className + "' as TokenFilterFactory", e);
+                                    }
+                                    CreateAnalysisPipelineComponent(stok, clazz);
+                                }
+                                break;
+                            }
+                        default:
+                            {
+                                throw new Exception("Line #" + GetLineNumber(stok) + ": Unexpected token: " + stok.ToString());
+                            }
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                if (e.Message.StartsWith("Line #", StringComparison.Ordinal))
+                {
+                    throw e;
+                }
+                else
+                {
+                    throw new Exception("Line #" + GetLineNumber(stok) + ": ", e);
+                }
+            }
+
+            AnalyzerFactory analyzerFactory = new AnalyzerFactory
+                (charFilterFactories, tokenizerFactory, tokenFilterFactories);
+            analyzerFactory.SetPositionIncrementGap(positionIncrementGap.GetValueOrDefault());
+            analyzerFactory.SetOffsetGap(offsetGap.GetValueOrDefault());
+            RunData.AnalyzerFactories[factoryName] = analyzerFactory;
+        }
+
+        /// <summary>
+        /// Instantiates the given analysis factory class after pulling params from
+        /// the given stream tokenizer, then stores the result in the appropriate
+        /// pipeline component list.        
+        /// </summary>
+        /// <param name="stok">Stream tokenizer from which to draw analysis factory params.</param>
+        /// <param name="clazz">Analysis factory class to instantiate.</param>
+        private void CreateAnalysisPipelineComponent(StreamTokenizer stok, Type clazz)
+        {
+            IDictionary<string, string> argMap = new Dictionary<string, string>();
+            bool parenthetical = false;
+            try
+            {
+                while (stok.NextToken() != StreamTokenizer.TT_EOF)
+                {
+                    switch (stok.TokenType)
+                    {
+                        case ',':
+                            {
+                                if (parenthetical)
+                                {
+                                    // Do nothing
+                                    break;
+                                }
+                                else
+                                {
+                                    // Finished reading this analysis factory configuration
+                                    goto WHILE_LOOP_BREAK;
+                                }
+                            }
+                        case '(':
+                            {
+                                if (parenthetical)
+                                {
+                                    throw new Exception
+                                        ("Line #" + GetLineNumber(stok) + ": Unexpected opening parenthesis.");
+                                }
+                                parenthetical = true;
+                                break;
+                            }
+                        case ')':
+                            {
+                                if (parenthetical)
+                                {
+                                    parenthetical = false;
+                                }
+                                else
+                                {
+                                    throw new Exception
+                                        ("Line #" + GetLineNumber(stok) + ": Unexpected closing parenthesis.");
+                                }
+                                break;
+                            }
+                        case StreamTokenizer.TT_WORD:
+                            {
+                                if (!parenthetical)
+                                {
+                                    throw new Exception("Line #" + GetLineNumber(stok) + ": Unexpected token '" + stok.StringValue + "'");
+                                }
+                                string argName = stok.StringValue;
+                                stok.NextToken();
+                                if (stok.TokenType != ':')
+                                {
+                                    throw new Exception
+                                        ("Line #" + GetLineNumber(stok) + ": Missing ':' after '" + argName + "' param to " + clazz.Name);
+                                }
+                                stok.NextToken();
+                                string argValue = stok.StringValue;
+                                switch (stok.TokenType)
+                                {
+                                    case StreamTokenizer.TT_NUMBER:
+                                        {
+                                            argValue = stok.NumberValue.ToString(CultureInfo.InvariantCulture);
+                                            // Drop the ".0" from numbers, for integer arguments
+                                            argValue = TRAILING_DOT_ZERO_PATTERN.Replace(argValue, "", 1);
+                                            // Intentional fall-through
+                                            argMap[argName] = argValue;
+                                            break;
+                                        }
+                                    case '"':
+                                    case '\'':
+                                    case StreamTokenizer.TT_WORD:
+                                        {
+                                            argMap[argName] = argValue;
+                                            break;
+                                        }
+                                    case StreamTokenizer.TT_EOF:
+                                        {
+                                            throw new Exception("Unexpected EOF: " + stok.ToString());
+                                        }
+                                    default:
+                                        {
+                                            throw new Exception
+                                                ("Line #" + GetLineNumber(stok) + ": Unexpected token: " + stok.ToString());
+                                        }
+                                }
+                                break;
+                            }
+                    }
+                }
+                WHILE_LOOP_BREAK: { }
+
+                if (!argMap.ContainsKey("luceneMatchVersion"))
+                {
+#pragma warning disable 612, 618
+                    argMap["luceneMatchVersion"] = LuceneVersion.LUCENE_CURRENT.ToString();
+#pragma warning restore 612, 618
+                }
+                AbstractAnalysisFactory instance;
+                try
+                {
+                    instance = (AbstractAnalysisFactory)Activator.CreateInstance(clazz, argMap);
+                }
+                catch (Exception e)
+                {
+                    throw new Exception("Line #" + GetLineNumber(stok) + ": ", e);
+                }
+                if (instance is IResourceLoaderAware)
+                {
+                    DirectoryInfo baseDir = new DirectoryInfo(RunData.Config.Get("work.dir", "work"));
+                    ((IResourceLoaderAware)instance).Inform(new FilesystemResourceLoader(baseDir));
+                }
+                if (typeof(CharFilterFactory).IsAssignableFrom(clazz))
+                {
+                    charFilterFactories.Add((CharFilterFactory)instance);
+                }
+                else if (typeof(TokenizerFactory).IsAssignableFrom(clazz))
+                {
+                    tokenizerFactory = (TokenizerFactory)instance;
+                }
+                else if (typeof(TokenFilterFactory).IsAssignableFrom(clazz))
+                {
+                    tokenFilterFactories.Add((TokenFilterFactory)instance);
+                }
+            }
+            catch (Exception e)
+            {
+                if (e.Message.StartsWith("Line #", StringComparison.Ordinal))
+                {
+                    throw e;
+                }
+                else
+                {
+                    throw new Exception("Line #" + GetLineNumber(stok) + ": ", e);
+                }
+            }
+        }
+
+        /// <summary>
+        /// This method looks up a class with its fully qualified name (FQN), or a short-name
+        /// class-simplename, or with a package suffix, assuming "Lucene.Net.Analysis."
+        /// as the namespace prefix (e.g. "standard.ClassicTokenizerFactory" ->
+        /// "Lucene.Net.Analysis.Standard.ClassicTokenizerFactory").
+        /// </summary>
+        /// <remarks>
+        /// If <paramref name="className"/> contains a period, the class is first looked up as-is, assuming that it
+        /// is an FQN.  If this fails, lookup is retried after prepending the Lucene analysis
+        /// package prefix to the class name.
+        /// <para/>
+        /// If <paramref name="className"/> does not contain a period, the analysis SPI *Factory.LookupClass()
+        /// methods are used to find the class.
+        /// </remarks>
+        /// <param name="className">The namespace qualified name or the short name of the class.</param>
+        /// <param name="expectedType">The superclass <paramref name="className"/> is expected to extend. </param>
+        /// <returns>The loaded type.</returns>
+        /// <exception cref="TypeLoadException">If lookup fails.</exception>
+        public virtual Type LookupAnalysisClass(string className, Type expectedType)
+        {
+            if (className.Contains("."))
+            {
+                // First, try className == FQN
+                Type result = Type.GetType(className);
+                if (result == null)
+                {
+                    // Second, retry lookup after prepending the Lucene analysis package prefix
+                    result = Type.GetType(LUCENE_ANALYSIS_PACKAGE_PREFIX + className);
+
+                    if (result == null)
+                    {
+                        throw new TypeLoadException("Can't find class '" + className
+                                                 + "' or '" + LUCENE_ANALYSIS_PACKAGE_PREFIX + className + "'");
+                    }
+                }
+                return result;
+            }
+            // No dot - use analysis SPI lookup
+            string analysisComponentName = ANALYSIS_COMPONENT_SUFFIX_PATTERN.Replace(className, "", 1);
+            if (typeof(CharFilterFactory).IsAssignableFrom(expectedType))
+            {
+                return CharFilterFactory.LookupClass(analysisComponentName);
+            }
+            else if (typeof(TokenizerFactory).IsAssignableFrom(expectedType))
+            {
+                return TokenizerFactory.LookupClass(analysisComponentName);
+            }
+            else if (typeof(TokenFilterFactory).IsAssignableFrom(expectedType))
+            {
+                return TokenFilterFactory.LookupClass(analysisComponentName);
+            }
+
+            throw new TypeLoadException("Can't find class '" + className + "'");
+        }
+
+        /// <seealso cref="PerfTask.SupportsParams"/>
+        public override bool SupportsParams
+        {
+            get { return true; }
+        }
+
+        /// <summary>Returns the current line in the algorithm file</summary>
+        public virtual int GetLineNumber(StreamTokenizer stok)
+        {
+            return AlgLineNum + stok.LineNumber;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/BenchmarkHighlighter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/BenchmarkHighlighter.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/BenchmarkHighlighter.cs
new file mode 100644
index 0000000..a20160c
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/BenchmarkHighlighter.cs
@@ -0,0 +1,32 @@
+using Lucene.Net.Analysis;
+using Lucene.Net.Documents;
+using Lucene.Net.Index;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Abstract class for benchmarking highlighting performance
+    /// </summary>
+    public abstract class BenchmarkHighlighter
+    {
+        public abstract int DoHighlight(IndexReader reader, int doc, string field,
+            Document document, Analyzer analyzer, string text);
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/ClearStatsTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/ClearStatsTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/ClearStatsTask.cs
new file mode 100644
index 0000000..16a1859
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/ClearStatsTask.cs
@@ -0,0 +1,44 @@
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Clear statistics data.
+    /// <para/>
+    /// Other side effects: None.
+    /// </summary>
+    public class ClearStatsTask : PerfTask
+    {
+        public ClearStatsTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        public override int DoLogic()
+        {
+            RunData.Points.ClearData();
+            return 0;
+        }
+
+        /// <seealso cref="PerfTask.ShouldNotRecordStats"/>
+        protected override bool ShouldNotRecordStats
+        {
+            get { return true; }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs
new file mode 100644
index 0000000..30e31d9
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseIndexTask.cs
@@ -0,0 +1,67 @@
+using Lucene.Net.Index;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Close index writer.
+    /// <para/>
+    /// Other side effects: index writer object in perfRunData is nullified.
+    /// <para/>
+    /// Takes optional param "doWait": if false, then close(false) is called.
+    /// </summary>
+    public class CloseIndexTask : PerfTask
+    {
+        public CloseIndexTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        bool doWait = true;
+
+        public override int DoLogic()
+        {
+            IndexWriter iw = RunData.IndexWriter;
+            if (iw != null)
+            {
+                // If infoStream was set to output to a file, close it.
+                InfoStream infoStream = iw.Config.InfoStream;
+                if (infoStream != null)
+                {
+                    infoStream.Dispose();
+                }
+                iw.Dispose(doWait);
+                RunData.IndexWriter = null;
+            }
+            return 1;
+        }
+
+        public override void SetParams(string @params)
+        {
+                base.SetParams(@params);
+                doWait = bool.Parse(@params);
+        }
+
+        public override bool SupportsParams
+        {
+            get { return true; }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseReaderTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseReaderTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseReaderTask.cs
new file mode 100644
index 0000000..7a8c61c
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseReaderTask.cs
@@ -0,0 +1,49 @@
+using Lucene.Net.Index;
+using Lucene.Net.Support;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Close index reader.
+    /// <para/>
+    /// Other side effects: index reader in perfRunData is nullified.
+    /// <para/>
+    /// This would cause read related tasks to reopen their own reader. 
+    /// </summary>
+    public class CloseReaderTask : PerfTask
+    {
+        public CloseReaderTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        public override int DoLogic()
+        {
+            IndexReader reader = RunData.GetIndexReader();
+            RunData.SetIndexReader(null);
+            if (reader.RefCount != 1)
+            {
+                SystemConsole.WriteLine("WARNING: CloseReader: reference count is currently " + reader.RefCount);
+            }
+            reader.DecRef();
+            return 1;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseTaxonomyIndexTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseTaxonomyIndexTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseTaxonomyIndexTask.cs
new file mode 100644
index 0000000..7d94a9a
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseTaxonomyIndexTask.cs
@@ -0,0 +1,42 @@
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Close taxonomy index.
+    /// <para/>
+    /// Other side effects: taxonomy writer object in perfRunData is nullified.
+    /// </summary>
+    public class CloseTaxonomyIndexTask : PerfTask
+    {
+        public CloseTaxonomyIndexTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        public override int DoLogic()
+        {
+            IOUtils.Dispose(RunData.TaxonomyWriter);
+            RunData.TaxonomyWriter = null;
+
+            return 1;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseTaxonomyReaderTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseTaxonomyReaderTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseTaxonomyReaderTask.cs
new file mode 100644
index 0000000..fc1ab27
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/CloseTaxonomyReaderTask.cs
@@ -0,0 +1,47 @@
+using Lucene.Net.Facet.Taxonomy;
+using Lucene.Net.Support;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Close taxonomy reader.
+    /// <para/>
+    /// Other side effects: taxonomy reader in perfRunData is nullified.
+    /// </summary>
+    public class CloseTaxonomyReaderTask : PerfTask
+    {
+        public CloseTaxonomyReaderTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        public override int DoLogic()
+        {
+            TaxonomyReader taxoReader = RunData.GetTaxonomyReader();
+            RunData.SetTaxonomyReader(null);
+            if (taxoReader.RefCount != 1)
+            {
+                SystemConsole.WriteLine("WARNING: CloseTaxonomyReader: reference count is currently " + taxoReader.RefCount);
+            }
+            taxoReader.Dispose();
+            return 1;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/CommitIndexTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/CommitIndexTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/CommitIndexTask.cs
new file mode 100644
index 0000000..b914371
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/CommitIndexTask.cs
@@ -0,0 +1,62 @@
+using Lucene.Net.Index;
+using System.Collections.Generic;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Commits the <see cref="IndexWriter"/>.
+    /// </summary>
+    public class CommitIndexTask : PerfTask
+    {
+        IDictionary<string, string> commitUserData;
+
+        public CommitIndexTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        public override bool SupportsParams
+        {
+            get { return true; }
+        }
+
+        public override void SetParams(string @params)
+        {
+            base.SetParams(@params);
+            commitUserData = new Dictionary<string, string>();
+            commitUserData[OpenReaderTask.USER_DATA] = @params;
+        }
+
+        public override int DoLogic()
+        {
+            IndexWriter iw = RunData.IndexWriter;
+            if (iw != null)
+            {
+                if (commitUserData != null)
+                {
+                    iw.SetCommitData(commitUserData);
+                }
+                iw.Commit();
+            }
+
+            return 1;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/CommitTaxonomyIndexTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/CommitTaxonomyIndexTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/CommitTaxonomyIndexTask.cs
new file mode 100644
index 0000000..4b8cc2c
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/CommitTaxonomyIndexTask.cs
@@ -0,0 +1,48 @@
+using Lucene.Net.Facet.Taxonomy;
+using System;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Commits the Taxonomy Index.
+    /// </summary>
+    public class CommitTaxonomyIndexTask : PerfTask
+    {
+        public CommitTaxonomyIndexTask(PerfRunData runData)
+            : base(runData)
+        {
+        }
+
+        public override int DoLogic()
+        {
+            ITaxonomyWriter taxonomyWriter = RunData.TaxonomyWriter;
+            if (taxonomyWriter != null)
+            {
+                taxonomyWriter.Commit();
+            }
+            else
+            {
+                throw new InvalidOperationException("TaxonomyWriter is not currently open");
+            }
+
+            return 1;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b515271d/src/Lucene.Net.Benchmark/ByTask/Tasks/ConsumeContentSourceTask.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Benchmark/ByTask/Tasks/ConsumeContentSourceTask.cs b/src/Lucene.Net.Benchmark/ByTask/Tasks/ConsumeContentSourceTask.cs
new file mode 100644
index 0000000..36a5a14
--- /dev/null
+++ b/src/Lucene.Net.Benchmark/ByTask/Tasks/ConsumeContentSourceTask.cs
@@ -0,0 +1,48 @@
+using Lucene.Net.Benchmarks.ByTask.Feeds;
+using System.Threading;
+
+namespace Lucene.Net.Benchmarks.ByTask.Tasks
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    ///  Consumes a <see cref="Feeds.ContentSource"/>.
+    /// </summary>
+    public class ConsumeContentSourceTask : PerfTask
+    {
+        private readonly ContentSource source;
+        private ThreadLocal<DocData> dd = new ThreadLocal<DocData>();
+
+        public ConsumeContentSourceTask(PerfRunData runData)
+            : base(runData)
+        {
+            source = runData.ContentSource;
+        }
+
+        protected override string GetLogMessage(int recsCount)
+        {
+            return "read " + recsCount + " documents from the content source";
+        }
+
+        public override int DoLogic()
+        {
+            dd.Value = source.GetNextDocData(dd.Value);
+            return 1;
+        }
+    }
+}