Skip to content

Commit dc940bc

Browse files
committed
In JavaScriptEngineSwitcher.ChakraCore was optimized a memory usage in version for Unix
1 parent 1eae3fa commit dc940bc

File tree

17 files changed

+584
-24
lines changed

17 files changed

+584
-24
lines changed

Licenses/dotnet-corefx-license.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) .NET Foundation and Contributors
4+
5+
All rights reserved.
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.

NuGet/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.nuspec

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This package does not contain the native implementations of ChakraCore. Therefor
2020
* JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64
2121
* JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64</description>
2222
<summary>JavaScriptEngineSwitcher.ChakraCore contains adapter `ChakraCoreJsEngine` (wrapper for the ChakraCore).</summary>
23-
<releaseNotes>ChakraCore was updated to version 1.11.1.</releaseNotes>
23+
<releaseNotes>Optimized a memory usage in version for Unix.</releaseNotes>
2424
<copyright>Copyright (c) 2013-2018 Andrey Taritsyn - http://www.taritsyn.ru</copyright>
2525
<language>en-US</language>
2626
<tags>JavaScriptEngineSwitcher JavaScript ECMAScript ChakraCore</tags>
@@ -29,10 +29,12 @@ This package does not contain the native implementations of ChakraCore. Therefor
2929
<dependency id="JavaScriptEngineSwitcher.Core" version="2.4.10" />
3030
</group>
3131
<group targetFramework=".NETFramework4.5">
32+
<dependency id="System.Buffers" version="4.0.0" />
3233
<dependency id="JavaScriptEngineSwitcher.Core" version="2.4.10" />
3334
</group>
3435
<group targetFramework=".NETStandard1.3">
3536
<dependency id="NETStandard.Library" version="1.6.0" />
37+
<dependency id="System.Buffers" version="4.0.0" />
3638
<dependency id="System.Reflection.TypeExtensions" version="4.1.0" />
3739
<dependency id="System.Threading.Thread" version="4.0.0" />
3840
<dependency id="JavaScriptEngineSwitcher.Core" version="2.4.10" />

NuGet/JavaScriptEngineSwitcher.ChakraCore/build-package.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ xcopy "%dotnet_project_bin_dir%\netstandard1.3\%project_name%.xml" lib\netstanda
3030
xcopy "%dotnet_project_bin_dir%\netstandard1.3\ru-ru\%project_name%.resources.dll" lib\netstandard1.3\ru-ru\
3131

3232
copy "%licenses_dir%\chakra-samples-license.txt" chakra-samples-license.txt /Y
33+
copy "%licenses_dir%\dotnet-corefx-license.txt" dotnet-corefx-license.txt /Y
3334
copy "%licenses_dir%\jsrt-dotnet-license.txt" jsrt-dotnet-license.txt /Y
3435

3536
%nuget_package_manager% pack "..\%project_name%\%project_name%.nuspec"

NuGet/JavaScriptEngineSwitcher.ChakraCore/readme.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
=============
3131
RELEASE NOTES
3232
=============
33-
ChakraCore was updated to version 1.11.1.
33+
Optimized a memory usage in version for Unix.
3434

3535
=============
3636
DOCUMENTATION

src/JavaScriptEngineSwitcher.ChakraCore.Net4/JavaScriptEngineSwitcher.ChakraCore.Net40.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<DefineConstants>TRACE;NET40</DefineConstants>
3030
<ErrorReport>prompt</ErrorReport>
3131
<WarningLevel>4</WarningLevel>
32+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
3233
</PropertyGroup>
3334
<PropertyGroup>
3435
<SignAssembly>true</SignAssembly>
@@ -199,6 +200,11 @@
199200
<Compile Include="..\JavaScriptEngineSwitcher.ChakraCore\ScriptDispatcher.cs">
200201
<Link>ScriptDispatcher.cs</Link>
201202
</Compile>
203+
<Compile Include="Polyfills\System\Buffers\ArrayPool.cs" />
204+
<Compile Include="Polyfills\System\Buffers\DefaultArrayPool.cs" />
205+
<Compile Include="Polyfills\System\Buffers\DefaultArrayPoolBucket.cs" />
206+
<Compile Include="Polyfills\System\Buffers\Utilities.cs" />
207+
<Compile Include="Polyfills\System\Threading\Volatile.cs" />
202208
</ItemGroup>
203209
<ItemGroup>
204210
<EmbeddedResource Include="..\JavaScriptEngineSwitcher.ChakraCore\Resources\Strings.resx">
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#if NET40
2+
// Licensed to the .NET Foundation under one or more agreements.
3+
// The .NET Foundation licenses this file to you under the MIT license.
4+
// See the LICENSE file in the project root for more information.
5+
6+
using System.Runtime.CompilerServices;
7+
using System.Threading;
8+
9+
using JavaScriptEngineSwitcher.ChakraCore.Polyfills.System.Threading;
10+
11+
namespace JavaScriptEngineSwitcher.ChakraCore.Polyfills.System.Buffers
12+
{
13+
/// <summary>
14+
/// Provides a resource pool that enables reusing instances of type <see cref="T:T[]"/>
15+
/// </summary>
16+
/// <remarks>
17+
/// <para>
18+
/// Renting and returning buffers with an <see cref="ArrayPool{T}"/> can increase performance
19+
/// in situations where arrays are created and destroyed frequently, resulting in significant
20+
/// memory pressure on the garbage collector.
21+
/// </para>
22+
/// <para>
23+
/// This class is thread-safe. All members may be used by multiple threads concurrently.
24+
/// </para>
25+
/// </remarks>
26+
internal abstract class ArrayPool<T>
27+
{
28+
/// <summary>
29+
/// The lazily-initialized shared pool instance
30+
/// </summary>
31+
private static ArrayPool<T> s_sharedInstance = null;
32+
33+
/// <summary>
34+
/// Retrieves a shared <see cref="ArrayPool{T}"/> instance
35+
/// </summary>
36+
/// <remarks>
37+
/// The shared pool provides a default implementation of <see cref="ArrayPool{T}"/>
38+
/// that's intended for general applicability. It maintains arrays of multiple sizes, and
39+
/// may hand back a larger array than was actually requested, but will never hand back a smaller
40+
/// array than was requested. Renting a buffer from it with <see cref="Rent"/> will result in an
41+
/// existing buffer being taken from the pool if an appropriate buffer is available or in a new
42+
/// buffer being allocated if one is not available.
43+
/// </remarks>
44+
public static ArrayPool<T> Shared
45+
{
46+
[MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)]
47+
get
48+
{
49+
return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated();
50+
}
51+
}
52+
53+
54+
/// <summary>
55+
/// Ensures that <see cref="s_sharedInstance"/> has been initialized to a pool and returns it
56+
/// </summary>
57+
[MethodImpl(MethodImplOptions.NoInlining)]
58+
private static ArrayPool<T> EnsureSharedCreated()
59+
{
60+
Interlocked.CompareExchange(ref s_sharedInstance, new DefaultArrayPool<T>(), null);
61+
return s_sharedInstance;
62+
}
63+
64+
/// <summary>
65+
/// Retrieves a buffer that is at least the requested length
66+
/// </summary>
67+
/// <remarks>
68+
/// This buffer is loaned to the caller and should be returned to the same pool via
69+
/// <see cref="Return"/> so that it may be reused in subsequent usage of <see cref="Rent"/>.
70+
/// It is not a fatal error to not return a rented buffer, but failure to do so may lead to
71+
/// decreased application performance, as the pool may need to create a new buffer to replace
72+
/// the one lost.
73+
/// </remarks>
74+
/// <param name="minimumLength">The minimum length of the array needed</param>
75+
/// <returns>An <see cref="T:T[]"/> that is at least <paramref name="minimumLength"/> in length</returns>
76+
public abstract T[] Rent(int minimumLength);
77+
78+
/// <summary>
79+
/// Returns to the pool an array that was previously obtained via <see cref="Rent"/> on the same
80+
/// <see cref="ArrayPool{T}"/> instance
81+
/// </summary>
82+
/// <remarks>
83+
/// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer
84+
/// and must not use it. The reference returned from a given call to <see cref="Rent"/> must only be
85+
/// returned via <see cref="Return"/> once. The default <see cref="ArrayPool{T}"/>
86+
/// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer
87+
/// if it's determined that the pool already has enough buffers stored.
88+
/// </remarks>
89+
/// <param name="array">The buffer previously obtained from <see cref="Rent"/> to return to the pool</param>
90+
public abstract void Return(T[] array);
91+
}
92+
}
93+
#endif
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#if NET40
2+
// Licensed to the .NET Foundation under one or more agreements.
3+
// The .NET Foundation licenses this file to you under the MIT license.
4+
// See the LICENSE file in the project root for more information.
5+
6+
using System;
7+
8+
namespace JavaScriptEngineSwitcher.ChakraCore.Polyfills.System.Buffers
9+
{
10+
internal sealed partial class DefaultArrayPool<T> : ArrayPool<T>
11+
{
12+
/// <summary>
13+
/// The default maximum length of each array in the pool (2^20)
14+
/// </summary>
15+
private const int DefaultMaxArrayLength = 1024 * 1024;
16+
17+
/// <summary>
18+
/// The default maximum number of arrays per bucket that are available for rent
19+
/// </summary>
20+
private const int DefaultMaxNumberOfArraysPerBucket = 50;
21+
22+
/// <summary>
23+
/// Lazily-allocated empty array used when arrays of length 0 are requested
24+
/// </summary>
25+
private static T[] s_emptyArray; // we support contracts earlier than those with Array.Empty<T>()
26+
27+
private readonly Bucket[] _buckets;
28+
29+
/// <summary>
30+
/// Gets an ID for the pool to use with events
31+
/// </summary>
32+
private int Id
33+
{
34+
get { return GetHashCode(); }
35+
}
36+
37+
38+
internal DefaultArrayPool()
39+
: this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket)
40+
{ }
41+
42+
internal DefaultArrayPool(int maxArrayLength, int maxArraysPerBucket)
43+
{
44+
if (maxArrayLength <= 0)
45+
{
46+
throw new ArgumentOutOfRangeException("maxArrayLength");
47+
}
48+
49+
if (maxArraysPerBucket <= 0)
50+
{
51+
throw new ArgumentOutOfRangeException("maxArraysPerBucket");
52+
}
53+
54+
// Our bucketing algorithm has a min length of 2^4 and a max length of 2^30.
55+
// Constrain the actual max used to those values.
56+
const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000;
57+
if (maxArrayLength > MaximumArrayLength)
58+
{
59+
maxArrayLength = MaximumArrayLength;
60+
}
61+
else if (maxArrayLength < MinimumArrayLength)
62+
{
63+
maxArrayLength = MinimumArrayLength;
64+
}
65+
66+
// Create the buckets.
67+
int poolId = Id;
68+
int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength);
69+
var buckets = new Bucket[maxBuckets + 1];
70+
71+
for (int i = 0; i < buckets.Length; i++)
72+
{
73+
buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId);
74+
}
75+
76+
_buckets = buckets;
77+
}
78+
79+
80+
public override T[] Rent(int minimumLength)
81+
{
82+
// Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though
83+
// pooling such an array isn't valuable) as it's a valid length array, and we want the pool
84+
// to be usable in general instead of using `new`, even for computed lengths.
85+
if (minimumLength < 0)
86+
{
87+
throw new ArgumentOutOfRangeException("minimumLength");
88+
}
89+
else if (minimumLength == 0)
90+
{
91+
// No need for events with the empty array. Our pool is effectively infinite
92+
// and we'll never allocate for rents and never store for returns.
93+
return s_emptyArray ?? (s_emptyArray = new T[0]);
94+
}
95+
96+
T[] buffer = null;
97+
98+
int index = Utilities.SelectBucketIndex(minimumLength);
99+
if (index < _buckets.Length)
100+
{
101+
// Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the
102+
// next higher bucket and try that one, but only try at most a few buckets.
103+
const int MaxBucketsToTry = 2;
104+
int i = index;
105+
106+
do
107+
{
108+
// Attempt to rent from the bucket. If we get a buffer from it, return it.
109+
buffer = _buckets[i].Rent();
110+
if (buffer != null)
111+
{
112+
return buffer;
113+
}
114+
}
115+
while (++i < _buckets.Length && i != index + MaxBucketsToTry);
116+
117+
// The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding
118+
// to the appropriate bucket.
119+
buffer = new T[_buckets[index]._bufferLength];
120+
}
121+
else
122+
{
123+
// The request was for a size too large for the pool. Allocate an array of exactly the requested
124+
// length. When it's returned to the pool, we'll simply throw it away.
125+
buffer = new T[minimumLength];
126+
}
127+
128+
return buffer;
129+
}
130+
131+
public override void Return(T[] array)
132+
{
133+
if (array == null)
134+
{
135+
throw new ArgumentNullException("array");
136+
}
137+
else if (array.Length == 0)
138+
{
139+
// Ignore empty arrays. When a zero-length array is rented, we return a singleton
140+
// rather than actually taking a buffer out of the lowest bucket.
141+
return;
142+
}
143+
144+
// Determine with what bucket this array length is associated
145+
int bucket = Utilities.SelectBucketIndex(array.Length);
146+
147+
// If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool
148+
if (bucket < _buckets.Length)
149+
{
150+
// Return the buffer to its bucket. In the future, we might consider having Return return false
151+
// instead of dropping a bucket, in which case we could try to return to a lower-sized bucket,
152+
// just as how in Rent we allow renting from a higher-sized bucket.
153+
_buckets[bucket].Return(array);
154+
}
155+
}
156+
}
157+
}
158+
#endif

0 commit comments

Comments
 (0)