<!-- 
RSS generated by JIRA (9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66) at Wed Feb 07 21:36:18 UTC 2024

It is possible to restrict the fields that are returned in this document by specifying the 'field' parameter in your request.
For example, to request only the issue key and summary append 'field=key&field=summary' to the URL of your request.
-->
<rss version="0.92" >
<channel>
    <title>MongoDB Jira</title>
    <link>https://jira.mongodb.org</link>
    <description>This file is an XML representation of an issue</description>
    <language>en-us</language>    <build-info>
        <version>9.7.1</version>
        <build-number>970001</build-number>
        <build-date>13-04-2023</build-date>
    </build-info>


<item>
            <title>[CSHARP-257] Performance of a BsonArray creation and AddRange method when &quot;values&quot; is either T[], IList&lt;T&gt; or an ICollection</title>
                <link>https://jira.mongodb.org/browse/CSHARP-257</link>
                <project id="10041" key="CSHARP">C# Driver</project>
                    <description>&lt;p&gt;While profiling one of our applications, I encountered that BsonArray implementation was relatively suboptimal when it&apos;s initialized with a medium-large sized (100-1000 elements) arrays. So, I created a patch which supports reserving space in internal values array if the amount of elements is known beforehand (if IEnumerable&amp;lt;T&amp;gt; is also an ICollection), along with some iteration improvements for arrays and IList&amp;lt;T&amp;gt; implementations. In my test case this leads to around 40-50% improvement on array creation. I also applied the same improvements to relevant methods (Clone, DeepClone). While I recognized that attached patch is largely untested, I ask you to consider if these improvements are worth including, and either include the changes I propose, or invent some other scheme to improve the T[]-&amp;gt;BsonArray conversion process.&lt;/p&gt;</description>
                <environment></environment>
        <key id="18677">CSHARP-257</key>
            <summary>Performance of a BsonArray creation and AddRange method when &quot;values&quot; is either T[], IList&lt;T&gt; or an ICollection</summary>
                <type id="4" iconUrl="https://jira.mongodb.org/secure/viewavatar?size=xsmall&amp;avatarId=14710&amp;avatarType=issuetype">Improvement</type>
                                            <priority id="4" iconUrl="https://jira.mongodb.org/images/icons/priorities/minor.svg">Minor - P4</priority>
                        <status id="6" iconUrl="https://jira.mongodb.org/images/icons/statuses/closed.png" description="The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.">Closed</status>
                    <statusCategory id="3" key="done" colorName="success"/>
                                    <resolution id="9">Done</resolution>
                                        <assignee username="robert@mongodb.com">Robert Stam</assignee>
                                    <reporter username="onyxmaster">Aristarkh Zagorodnikov</reporter>
                        <labels>
                    </labels>
                <created>Fri, 24 Jun 2011 15:07:40 +0000</created>
                <updated>Thu, 2 Apr 2015 18:27:55 +0000</updated>
                            <resolved>Thu, 21 Jul 2011 02:41:18 +0000</resolved>
                                                    <fixVersion>1.2</fixVersion>
                                                        <votes>0</votes>
                                    <watches>0</watches>
                                                                                                                <comments>
                            <comment id="41983" author="onyxmaster" created="Tue, 12 Jul 2011 19:30:09 +0000"  >&lt;p&gt;I think that current implementation (Capacity constructor and property) is sufficiently good for most of the cases. If you need my opinion, I believe the issue can be closed.&lt;/p&gt;</comment>
                            <comment id="41982" author="onyxmaster" created="Tue, 12 Jul 2011 19:26:45 +0000"  >&lt;p&gt;Yes, while there are additional performance improvements for creation from an array of items, I think it&apos;s too much for the driver. I will keep this patch for our internal use (we maintain a set of patches for middleware we use), I just thought I should share this one even if it&apos;s not accepted, for future reference.&lt;/p&gt;</comment>
                            <comment id="41904" author="rstam" created="Tue, 12 Jul 2011 15:06:53 +0000"  >&lt;p&gt;Ouch. I think that&apos;s too many overloads... And I think it still won&apos;t be any faster than:&lt;/p&gt;

&lt;p&gt;var array = new BsonArray(capacity); &lt;br/&gt;
array.AddRange(values); &lt;/p&gt;

&lt;p&gt;although this two line version does require the caller to write an extra line of code.&lt;/p&gt;

&lt;p&gt;I don&apos;t think I want to complicate BsonArray this way.&lt;/p&gt;</comment>
                            <comment id="41853" author="onyxmaster" created="Tue, 12 Jul 2011 10:27:09 +0000"  >&lt;p&gt;Latest version of the patch, common cases (T[], List&amp;lt;T&amp;gt;) are always faster, uncommon (pure IEnumerable&amp;lt;T&amp;gt;, such as LINQ results) are a little bit slower (this can be fixed by reverting the AddRange(IEnumerable&amp;lt;T&amp;gt;)). I didn&apos;t add tests since I think this patch still needs evaluation.&lt;br/&gt;
Also I&apos;ve attached the test project that I use to evaluate the changes, it&apos;s short and should be pretty straightforward.&lt;/p&gt;</comment>
                            <comment id="41502" author="rstam" created="Sat, 9 Jul 2011 23:35:42 +0000"  >&lt;p&gt;Excellent suggestion. I&apos;ve gone ahead and added the Capacity property.&lt;/p&gt;</comment>
                            <comment id="41493" author="onyxmaster" created="Sat, 9 Jul 2011 21:47:12 +0000"  >&lt;p&gt;Yes, I agree that this would work, but I ask you to add the Capacity property along with the constructor, to allow for more efficient appending too.&lt;/p&gt;</comment>
                            <comment id="41477" author="rstam" created="Sat, 9 Jul 2011 16:45:05 +0000"  >&lt;p&gt;I can apparently get the same (or better) performance improvements by only adding a new constructor that lets you specify the initial capacity and replacing:&lt;/p&gt;

&lt;p&gt;var array = new BsonArray(values);&lt;/p&gt;

&lt;p&gt;with&lt;/p&gt;

&lt;p&gt;var array = new BsonArray(capacity);&lt;br/&gt;
array.AddRange(values);&lt;/p&gt;

&lt;p&gt;in the client code. The best thing about this simpler approach is that it is faster for all sizes of the values enumerable.&lt;/p&gt;

&lt;p&gt;It does mean that the caller has to make a small code change if they want to squeak that last bit of performance out of creating new BsonArrays, but it&apos;s a much safer approach because it doesn&apos;t slow down the most common cases of small BsonArrays (0 to 4 elements). The caller can also guess at an initial capacity even if the values enumerable can&apos;t provide a count.&lt;/p&gt;</comment>
                            <comment id="41465" author="onyxmaster" created="Sat, 9 Jul 2011 10:10:52 +0000"  >&lt;p&gt;Thank you for looking at this. I&apos;ll unshelve my tests and post the code and the results here. Maybe there was an error in my measurements.&lt;/p&gt;</comment>
                            <comment id="41376" author="rstam" created="Fri, 8 Jul 2011 19:24:46 +0000"  >&lt;p&gt;I&apos;m experimenting with this and benchmarking and I get slower results with the new code when the number of values passed in is 4 or less. After that I start seeing improved performance. Will keep looking at it to see if there&apos;s a way to not slow down the simple cases.&lt;/p&gt;</comment>
                            <comment id="39215" author="onyxmaster" created="Sat, 25 Jun 2011 07:36:33 +0000"  >&lt;p&gt;Actually, added optimizations (except Clone() and DeepClone()) are similar to ones that exist in the BCL List&amp;lt;T&amp;gt;, but are a bit more different, because the internal container is not a simple array, but a List&amp;lt;T&amp;gt; itself.&lt;br/&gt;
Some code disassembled by .NET Reflector from the .NET 4.0 BCL follows;&lt;br/&gt;
public List(IEnumerable&amp;lt;T&amp;gt; collection)&lt;br/&gt;
{&lt;br/&gt;
    if (collection == null)&lt;/p&gt;
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
    }&lt;br/&gt;
    ICollection&amp;lt;T&amp;gt; is2 = collection as ICollection&amp;lt;T&amp;gt;;&lt;br/&gt;
    if (is2 != null)&lt;br/&gt;
    {
        int count = is2.Count;
        this._items = new T[count];
        is2.CopyTo(this._items, 0);
        this._size = count;
    }&lt;br/&gt;
    else&lt;br/&gt;
    {&lt;br/&gt;
        this._size = 0;&lt;br/&gt;
        this._items = new T&lt;span class=&quot;error&quot;&gt;&amp;#91;4&amp;#93;&lt;/span&gt;;&lt;br/&gt;
        using (IEnumerator&amp;lt;T&amp;gt; enumerator = collection.GetEnumerator())&lt;br/&gt;
        {&lt;br/&gt;
            while (enumerator.MoveNext())&lt;br/&gt;
            {
                this.Add(enumerator.Current);
            }&lt;br/&gt;
        }&lt;br/&gt;
    }&lt;br/&gt;
}&lt;br/&gt;
&lt;br/&gt;
(this one is called from AddRange) &lt;br/&gt;
public void InsertRange(int index, IEnumerable&amp;lt;T&amp;gt; collection)&lt;br/&gt;
{&lt;br/&gt;
    if (collection == null)&lt;br/&gt;
    {        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);    }
&lt;p&gt;    if (index &amp;gt; this._size)&lt;/p&gt;
    {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
    }
&lt;p&gt;    ICollection&amp;lt;T&amp;gt; is2 = collection as ICollection&amp;lt;T&amp;gt;;&lt;br/&gt;
    if (is2 != null)&lt;br/&gt;
    {&lt;br/&gt;
        int count = is2.Count;&lt;br/&gt;
        if (count &amp;gt; 0)&lt;br/&gt;
        {&lt;br/&gt;
            this.EnsureCapacity(this._size + count);&lt;br/&gt;
            if (index &amp;lt; this._size)&lt;/p&gt;
            {
                Array.Copy(this._items, index, this._items, index + count, this._size - index);
            }
&lt;p&gt;            if (this == is2)&lt;/p&gt;
            {
                Array.Copy(this._items, 0, this._items, index, index);
                Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index));
            }
&lt;p&gt;            else&lt;/p&gt;
            {
                T[] array = new T[count];
                is2.CopyTo(array, 0);
                array.CopyTo(this._items, index);
            }
&lt;p&gt;            this._size += count;&lt;br/&gt;
        }&lt;br/&gt;
    }&lt;br/&gt;
    else&lt;br/&gt;
    {&lt;br/&gt;
        using (IEnumerator&amp;lt;T&amp;gt; enumerator = collection.GetEnumerator())&lt;br/&gt;
        {&lt;br/&gt;
            while (enumerator.MoveNext())&lt;/p&gt;
            {
                this.Insert(index++, enumerator.Current);
            }
&lt;p&gt;        }&lt;br/&gt;
    }&lt;br/&gt;
    this._version++;&lt;br/&gt;
}&lt;/p&gt;
</comment>
                            <comment id="39187" author="onyxmaster" created="Fri, 24 Jun 2011 23:27:43 +0000"  >&lt;p&gt;I would also like to note that if the constructor accepting capacity is implemented, then a similar property should be added too (proposed patch doesn&apos;t include one).&lt;/p&gt;</comment>
                            <comment id="39165" author="onyxmaster" created="Fri, 24 Jun 2011 21:48:25 +0000"  >&lt;p&gt;I noticed a minor inefficiency change (used property instead of a local variable that already contained the needed value), so I uploaded an improved version.&lt;/p&gt;</comment>
                            <comment id="39163" author="onyxmaster" created="Fri, 24 Jun 2011 21:47:39 +0000"  >&lt;p&gt;Fixed minor inefficiency&lt;/p&gt;</comment>
                            <comment id="39161" author="onyxmaster" created="Fri, 24 Jun 2011 21:45:29 +0000"  >&lt;p&gt;Smaller number of elements just get smaller benefits, 4-8 element arrays for example are only ~8-10% faster. There is about 5% penalty only if you provide a &quot;pure&quot; IEnumerable&amp;lt;T&amp;gt; with a small amount of elements (16-element Linq result fit into this category, but if one uses Linq, the performance is already suffering due to delegate invocation and, in complex cases, intermediate buffering). On the other hand, the typical use case where performance matters &amp;#8211; feeding a T[] or a List&amp;lt;T&amp;gt; to constructor or an AddRange, is always faster due to preallocation (the only extra operations are two attempted casts and two null checks for worst case) and direct &quot;for&quot; loop iteration instead of foreach for indexable data (no extra operations apart the ones introduced for preallocation).&lt;/p&gt;

&lt;p&gt;As for the &quot;capacity&quot; constructor, my patch provides one and uses it in Clone() and DeepClone() implementations, as I recognize that adding data to a preallocated BsonArray directly instead of creating an intermediate buffer is the most effective way to create BsonArray filled with data that is generated/calculated on the go. Still, there are often cases that the input data is pre-processed and is contained in an array, list (IList&amp;lt;T&amp;gt;) or a collection (i.e. HashSet&amp;lt;T&amp;gt;), or is added partially and the final size is not known &amp;#8211; in this case the existing constructor/AddRange methods are less effective than they could.&lt;/p&gt;

&lt;p&gt;As for the implementation complication &amp;#8211; the BsonArray(IEnumerable&amp;lt;T&amp;gt;) constructors are completely unchanged, the AddRange methods are now uniform (and can be implemented in one line if you consider adding BsonArray return value to proposed AddRange&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt;, Func&amp;lt;T, BsonValue&amp;gt;)), Clone() and DeepClone() are just implemented a little bit more efficiently.&lt;/p&gt;</comment>
                            <comment id="39073" author="rstam" created="Fri, 24 Jun 2011 15:22:51 +0000"  >&lt;p&gt;How does this patch affect performance when the BsonArray is created with a small number of elements?&lt;/p&gt;

&lt;p&gt;Perhaps a simpler change is to just added a constructor to create a BsonArray with a certain capacity and the let the client code assign directly to the elements. Something like:&lt;/p&gt;

&lt;p&gt;var capacity = 1000;&lt;br/&gt;
var array = new BsonArray(capacity);&lt;br/&gt;
for (int i = 0; i &amp;lt; capacity; i++) {&lt;br/&gt;
    array&lt;span class=&quot;error&quot;&gt;&amp;#91;i&amp;#93;&lt;/span&gt; = ComputeValue&lt;img class=&quot;emoticon&quot; src=&quot;https://jira.mongodb.org/images/icons/emoticons/information.png&quot; height=&quot;16&quot; width=&quot;16&quot; align=&quot;absmiddle&quot; alt=&quot;&quot; border=&quot;0&quot;/&gt;;&lt;br/&gt;
}&lt;/p&gt;

&lt;p&gt;This would avoid complicating the implementation of BsonArray constructors and not impact performance of creation of small BsonArrays.&lt;/p&gt;</comment>
                    </comments>
                    <attachments>
                            <attachment id="12128" name="BsonArray.cs-new.patch" size="7248" author="onyxmaster" created="Fri, 24 Jun 2011 21:47:39 +0000"/>
                            <attachment id="12124" name="BsonArray.cs.patch" size="7255" author="onyxmaster" created="Fri, 24 Jun 2011 15:07:40 +0000"/>
                            <attachment id="12241" name="CSHARP-257.patch" size="41175" author="onyxmaster" created="Tue, 12 Jul 2011 10:27:09 +0000"/>
                            <attachment id="12242" name="perftest.zip" size="2388" author="onyxmaster" created="Tue, 12 Jul 2011 10:27:09 +0000"/>
                    </attachments>
                <subtasks>
                    </subtasks>
                <customfields>
                                                                                                                                                                                                                        <customfield id="customfield_10011" key="com.atlassian.jira.plugin.system.customfieldtypes:radiobuttons">
                        <customfieldname>Backwards Compatibility</customfieldname>
                        <customfieldvalues>
                                <customfieldvalue key="10038"><![CDATA[Fully Compatible]]></customfieldvalue>

                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                <customfield id="customfield_15850" key="com.atlassian.jira.plugins.jira-development-integration-plugin:devsummary">
                        <customfieldname>Development</customfieldname>
                        <customfieldvalues>
                            
                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    <customfield id="customfield_12550" key="com.pyxis.greenhopper.jira:gh-lexo-rank">
                        <customfieldname>Rank</customfieldname>
                        <customfieldvalues>
                            <customfieldvalue>2|hrh8rr:</customfieldvalue>

                        </customfieldvalues>
                    </customfield>
                                                                <customfield id="customfield_10558" key="com.pyxis.greenhopper.jira:gh-global-rank">
                        <customfieldname>Rank (Obsolete)</customfieldname>
                        <customfieldvalues>
                            <customfieldvalue>14220</customfieldvalue>
                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            </customfields>
    </item>
</channel>
</rss>