Skip to content

Commit 480c901

Browse files
konardclaude
andcommitted
Implement cached sequence walkers for optimized isElement function calls
This commit implements cached versions of sequence walkers to optimize performance by caching the results of isElement function calls, addressing issue #32. ## Changes: ### New Classes: - `CachedSequenceWalkerBase<TLinkAddress>`: Abstract base class that extends `SequenceWalkerBase` with caching functionality for isElement calls - `CachedLeftSequenceWalker<TLinkAddress>`: Cached version of LeftSequenceWalker - `CachedRightSequenceWalker<TLinkAddress>`: Cached version of RightSequenceWalker - `CachedLeveledSequenceWalker<TLinkAddress>`: Cached version of LeveledSequenceWalker ### Features: - Dictionary-based caching to store isElement results per link address - `ClearCache()` method to reset cache when needed - `GetCacheSize()` method for cache inspection and debugging - Same constructors as original walkers for drop-in compatibility - All original functionality preserved with performance optimization ### Tests: - `CachedWalkersTests`: Unit tests validating caching behavior and constructor functionality - Tests verify that cached calls reduce isElement function invocations - Tests ensure cache management methods work correctly ## Performance Benefits: - Reduces repeated isElement function calls for the same link addresses - Especially beneficial for sequence traversals that revisit elements - Maintains identical behavior to original walkers while improving performance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 4316067 commit 480c901

File tree

5 files changed

+688
-0
lines changed

5 files changed

+688
-0
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Numerics;
5+
using Xunit;
6+
using Platform.Collections;
7+
using Platform.Collections.Stacks;
8+
using Platform.Data.Doublets.Memory;
9+
using Platform.Data.Doublets.Memory.United.Generic;
10+
using Platform.Data.Doublets.Sequences.Walkers;
11+
using Platform.Memory;
12+
using TLinkAddress = System.UInt16;
13+
14+
namespace Platform.Data.Doublets.Sequences.Tests
15+
{
16+
public class CachedWalkersTests
17+
{
18+
[Fact]
19+
public void CachedSequenceWalkerBaseTest()
20+
{
21+
var linksConstants = new LinksConstants<TLinkAddress>(enableExternalReferencesSupport: true);
22+
var storageFileName = new IO.TemporaryFile().Filename;
23+
var storageMemory = new FileMappedResizableDirectMemory(storageFileName);
24+
var links = new UnitedMemoryLinks<TLinkAddress>(storageMemory, UnitedMemoryLinks<TLinkAddress>.DefaultLinksSizeStep, linksConstants, IndexTreeType.Default);
25+
var stack = new DefaultStack<TLinkAddress>();
26+
var isElementCallCount = 0;
27+
28+
bool IsElement(TLinkAddress link)
29+
{
30+
isElementCallCount++;
31+
return links.IsPartialPoint(link);
32+
}
33+
34+
var cachedWalker = new CachedLeftSequenceWalker<TLinkAddress>(links, stack, IsElement);
35+
36+
// Create some test elements
37+
var element1 = links.Create();
38+
39+
// Reset call count before testing
40+
isElementCallCount = 0;
41+
42+
// Test caching by calling IsElement multiple times on same element through reflection
43+
var isElementMethod = cachedWalker.GetType().GetMethod("IsElement",
44+
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
45+
46+
var result1 = isElementMethod?.Invoke(cachedWalker, new object[] { element1 });
47+
var result2 = isElementMethod?.Invoke(cachedWalker, new object[] { element1 });
48+
49+
// Should have been called only once due to caching
50+
Assert.Equal(1, isElementCallCount);
51+
Assert.Equal(result1, result2);
52+
53+
// Cache should contain one element
54+
Assert.Equal(1, cachedWalker.GetCacheSize());
55+
56+
// Clear cache
57+
cachedWalker.ClearCache();
58+
Assert.Equal(0, cachedWalker.GetCacheSize());
59+
60+
links.Dispose();
61+
}
62+
63+
[Fact]
64+
public void CachedWalkerConstructorTest()
65+
{
66+
var linksConstants = new LinksConstants<TLinkAddress>(enableExternalReferencesSupport: true);
67+
var storageFileName = new IO.TemporaryFile().Filename;
68+
var storageMemory = new FileMappedResizableDirectMemory(storageFileName);
69+
var links = new UnitedMemoryLinks<TLinkAddress>(storageMemory, UnitedMemoryLinks<TLinkAddress>.DefaultLinksSizeStep, linksConstants, IndexTreeType.Default);
70+
var stack = new DefaultStack<TLinkAddress>();
71+
72+
// Test that constructors work without throwing exceptions
73+
var cachedLeftWalker = new CachedLeftSequenceWalker<TLinkAddress>(links, stack);
74+
var cachedRightWalker = new CachedRightSequenceWalker<TLinkAddress>(links, stack);
75+
var cachedLeveledWalker = new CachedLeveledSequenceWalker<TLinkAddress>(links);
76+
77+
Assert.NotNull(cachedLeftWalker);
78+
Assert.NotNull(cachedRightWalker);
79+
Assert.NotNull(cachedLeveledWalker);
80+
81+
// All should start with empty cache
82+
Assert.Equal(0, cachedLeftWalker.GetCacheSize());
83+
Assert.Equal(0, cachedRightWalker.GetCacheSize());
84+
Assert.Equal(0, cachedLeveledWalker.GetCacheSize());
85+
86+
links.Dispose();
87+
}
88+
}
89+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Numerics;
4+
using System.Runtime.CompilerServices;
5+
using Platform.Collections.Stacks;
6+
7+
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
8+
9+
namespace Platform.Data.Doublets.Sequences.Walkers
10+
{
11+
/// <summary>
12+
/// <para>
13+
/// Represents the cached left sequence walker.
14+
/// </para>
15+
/// <para></para>
16+
/// </summary>
17+
/// <seealso cref="CachedSequenceWalkerBase{TLinkAddress}"/>
18+
public class CachedLeftSequenceWalker<TLinkAddress> : CachedSequenceWalkerBase<TLinkAddress> where TLinkAddress : struct, IUnsignedNumber<TLinkAddress>, IComparisonOperators<TLinkAddress, TLinkAddress, bool>
19+
{
20+
/// <summary>
21+
/// <para>
22+
/// Initializes a new <see cref="CachedLeftSequenceWalker"/> instance.
23+
/// </para>
24+
/// <para></para>
25+
/// </summary>
26+
/// <param name="links">
27+
/// <para>A links.</para>
28+
/// <para></para>
29+
/// </param>
30+
/// <param name="stack">
31+
/// <para>A stack.</para>
32+
/// <para></para>
33+
/// </param>
34+
/// <param name="isElement">
35+
/// <para>A is element.</para>
36+
/// <para></para>
37+
/// </param>
38+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
39+
public CachedLeftSequenceWalker(ILinks<TLinkAddress> links, IStack<TLinkAddress> stack, Func<TLinkAddress, bool> isElement) : base(links, stack, isElement) { }
40+
41+
/// <summary>
42+
/// <para>
43+
/// Initializes a new <see cref="CachedLeftSequenceWalker"/> instance.
44+
/// </para>
45+
/// <para></para>
46+
/// </summary>
47+
/// <param name="links">
48+
/// <para>A links.</para>
49+
/// <para></para>
50+
/// </param>
51+
/// <param name="stack">
52+
/// <para>A stack.</para>
53+
/// <para></para>
54+
/// </param>
55+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
56+
public CachedLeftSequenceWalker(ILinks<TLinkAddress> links, IStack<TLinkAddress> stack) : base(links, stack, links.IsPartialPoint) { }
57+
58+
/// <summary>
59+
/// <para>
60+
/// Gets the next element after pop using the specified element.
61+
/// </para>
62+
/// <para></para>
63+
/// </summary>
64+
/// <param name="element">
65+
/// <para>The element.</para>
66+
/// <para></para>
67+
/// </param>
68+
/// <returns>
69+
/// <para>The link</para>
70+
/// <para></para>
71+
/// </returns>
72+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
73+
protected override TLinkAddress GetNextElementAfterPop(TLinkAddress element) => _links.GetSource(element);
74+
75+
/// <summary>
76+
/// <para>
77+
/// Gets the next element after push using the specified element.
78+
/// </para>
79+
/// <para></para>
80+
/// </summary>
81+
/// <param name="element">
82+
/// <para>The element.</para>
83+
/// <para></para>
84+
/// </param>
85+
/// <returns>
86+
/// <para>The link</para>
87+
/// <para></para>
88+
/// </returns>
89+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
90+
protected override TLinkAddress GetNextElementAfterPush(TLinkAddress element) => _links.GetTarget(element);
91+
92+
/// <summary>
93+
/// <para>
94+
/// Walks the contents using the specified element.
95+
/// </para>
96+
/// <para></para>
97+
/// </summary>
98+
/// <param name="element">
99+
/// <para>The element.</para>
100+
/// <para></para>
101+
/// </param>
102+
/// <returns>
103+
/// <para>An enumerable of t link</para>
104+
/// <para></para>
105+
/// </returns>
106+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
107+
protected override IEnumerable<TLinkAddress> WalkContents(TLinkAddress element)
108+
{
109+
var links = _links;
110+
var parts = links.GetLink(element);
111+
var start = links.Constants.SourcePart;
112+
for (var i = parts.Count - 1; i >= start; i--)
113+
{
114+
var part = parts[i];
115+
if (IsElement(part))
116+
{
117+
yield return part;
118+
}
119+
}
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)