In .NET enumeration is slow
-
I just switched IEnumerable to IList and removed foreach (preferring for) and cut my execution time in my test from 65ms to about 45ms. I've put a stripped down version of the code here. The first argument of each emphasized routine was IEnumerable, is now IList with no foreach. This, ladies and gents, is why I don't like LINQ.
// here this._fa is the target machine we will be parsing.
// parse this or otherwise build it and use it here.
IList initial = FA.FillEpsilonClosure(this._fa);
IList next = new List();
IList states = new List(initial);
// start out with an empty capture buffer
this.capture.Clear();
// first move:
if (this.current == -2)
{
this.Advance();
}
// store the current position
long cursor_pos = this.position;
int line = this.line;
int column = this.column;
while(true) {
// try to transition from states on
// the current codepoint under the
// cursor
next.Clear();
FA.FillMove(states, this.current, next);
if (next.Count > 0)
{
// found at least one transition
// capture the current
// char, advance the input
// position:
this.Advance();
// move to the next states
states.Clear();
FA.FillEpsilonClosure(next, states);
} else {
// no matching transition
// is any current state accepting?
int acc = FA.GetFirstAcceptSymbol(states);
if(acc>-1) {
// accept
return FAMatch.Create(
acc,
this.capture.ToString(),
cursor_pos,
line,
column);
}
// not accepting - error
// keep capturing input until we find a
// valid move or there's no more input
while (this.current != -1 &&
FA.FillMove(initial, this.current).Count == 0)
{
this.Advance();
}
if (capture.Length == 0)
{
// end of input
return FAMatch.Create(-2, null, 0, 0, 0);
}
// error
return FAMatch.Create(-1,
capture.ToString(),
cursor_pos,
line,
column);}
}
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
I just switched IEnumerable to IList and removed foreach (preferring for) and cut my execution time in my test from 65ms to about 45ms. I've put a stripped down version of the code here. The first argument of each emphasized routine was IEnumerable, is now IList with no foreach. This, ladies and gents, is why I don't like LINQ.
// here this._fa is the target machine we will be parsing.
// parse this or otherwise build it and use it here.
IList initial = FA.FillEpsilonClosure(this._fa);
IList next = new List();
IList states = new List(initial);
// start out with an empty capture buffer
this.capture.Clear();
// first move:
if (this.current == -2)
{
this.Advance();
}
// store the current position
long cursor_pos = this.position;
int line = this.line;
int column = this.column;
while(true) {
// try to transition from states on
// the current codepoint under the
// cursor
next.Clear();
FA.FillMove(states, this.current, next);
if (next.Count > 0)
{
// found at least one transition
// capture the current
// char, advance the input
// position:
this.Advance();
// move to the next states
states.Clear();
FA.FillEpsilonClosure(next, states);
} else {
// no matching transition
// is any current state accepting?
int acc = FA.GetFirstAcceptSymbol(states);
if(acc>-1) {
// accept
return FAMatch.Create(
acc,
this.capture.ToString(),
cursor_pos,
line,
column);
}
// not accepting - error
// keep capturing input until we find a
// valid move or there's no more input
while (this.current != -1 &&
FA.FillMove(initial, this.current).Count == 0)
{
this.Advance();
}
if (capture.Length == 0)
{
// end of input
return FAMatch.Create(-2, null, 0, 0, 0);
}
// error
return FAMatch.Create(-1,
capture.ToString(),
cursor_pos,
line,
column);}
}
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
Depending on the project, I might prefer to loose 20ms execution time (especially in background threads that run for 10 secs or more) and have better readability of the source code. This, ladies and gentlemen, is why I dislike micro optimizations. If you want something that is fast, then use a language without a garbage-collector. Delphi goes quicker through a list of pointers than .NET with a for-loop gets its object-references.
Bastard Programmer from Hell :suss: "If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
-
Depending on the project, I might prefer to loose 20ms execution time (especially in background threads that run for 10 secs or more) and have better readability of the source code. This, ladies and gentlemen, is why I dislike micro optimizations. If you want something that is fast, then use a language without a garbage-collector. Delphi goes quicker through a list of pointers than .NET with a for-loop gets its object-references.
Bastard Programmer from Hell :suss: "If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
It's not 20ms. it's a 30% improvement in overall execution time. If I increase the test size to run for 90 seconds, it would run for about 60 after the optimization. If you get even 20% off the execution in critical code paths it's generally worth the optimization. I mean, of course it depends on the circumstances, and is less true of business development, or development with large teams or teams with with a lot of turnover, where you can't afford the additional maintenance overhead, limited knowledge transferability and cognitive load of optimized code. That is not a microoptimization. 30% off total execution time is a significant savings. Adding, I used to use garbage collection in my ISAPI applications because it prevented nasty heap fragmentation due to all the string processing required of web servers. It made things faster. GC isn't always a losing performance proposition. When the situation calls for it, it can increase overall performance.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
I just switched IEnumerable to IList and removed foreach (preferring for) and cut my execution time in my test from 65ms to about 45ms. I've put a stripped down version of the code here. The first argument of each emphasized routine was IEnumerable, is now IList with no foreach. This, ladies and gents, is why I don't like LINQ.
// here this._fa is the target machine we will be parsing.
// parse this or otherwise build it and use it here.
IList initial = FA.FillEpsilonClosure(this._fa);
IList next = new List();
IList states = new List(initial);
// start out with an empty capture buffer
this.capture.Clear();
// first move:
if (this.current == -2)
{
this.Advance();
}
// store the current position
long cursor_pos = this.position;
int line = this.line;
int column = this.column;
while(true) {
// try to transition from states on
// the current codepoint under the
// cursor
next.Clear();
FA.FillMove(states, this.current, next);
if (next.Count > 0)
{
// found at least one transition
// capture the current
// char, advance the input
// position:
this.Advance();
// move to the next states
states.Clear();
FA.FillEpsilonClosure(next, states);
} else {
// no matching transition
// is any current state accepting?
int acc = FA.GetFirstAcceptSymbol(states);
if(acc>-1) {
// accept
return FAMatch.Create(
acc,
this.capture.ToString(),
cursor_pos,
line,
column);
}
// not accepting - error
// keep capturing input until we find a
// valid move or there's no more input
while (this.current != -1 &&
FA.FillMove(initial, this.current).Count == 0)
{
this.Advance();
}
if (capture.Length == 0)
{
// end of input
return FAMatch.Create(-2, null, 0, 0, 0);
}
// error
return FAMatch.Create(-1,
capture.ToString(),
cursor_pos,
line,
column);}
}
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
Welcome to my bandwagon. I've been saying this for ages. Don't use
foreach
unless you have to -- or where it doesn't matter. Having said that... I hypothesize thatforeach
has improved. To test this hypothesis, last summer (?) I was testing and measuring some comparisons and I didn't see much difference. I was unable to form conclusions at that time because I wasn't convinced that the tests were valid. I'll have another look later. P.S. And besides, you mean iteration, not enumeration -- I blame Microsoft for misnaming the thing. -
Welcome to my bandwagon. I've been saying this for ages. Don't use
foreach
unless you have to -- or where it doesn't matter. Having said that... I hypothesize thatforeach
has improved. To test this hypothesis, last summer (?) I was testing and measuring some comparisons and I didn't see much difference. I was unable to form conclusions at that time because I wasn't convinced that the tests were valid. I'll have another look later. P.S. And besides, you mean iteration, not enumeration -- I blame Microsoft for misnaming the thing.I've said it before, but this is one of the few times I've used it in a critical codepath. :) PS: I'm using enumeration because we're talking about .NET. If I started talking about iterators in .NET parlance that's a C# compiler feature. Iterators and iterating are terms I'd use if we were talking about C++. You may not agree with my terminology but I tend to choose it with some deliberation.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
I just switched IEnumerable to IList and removed foreach (preferring for) and cut my execution time in my test from 65ms to about 45ms. I've put a stripped down version of the code here. The first argument of each emphasized routine was IEnumerable, is now IList with no foreach. This, ladies and gents, is why I don't like LINQ.
// here this._fa is the target machine we will be parsing.
// parse this or otherwise build it and use it here.
IList initial = FA.FillEpsilonClosure(this._fa);
IList next = new List();
IList states = new List(initial);
// start out with an empty capture buffer
this.capture.Clear();
// first move:
if (this.current == -2)
{
this.Advance();
}
// store the current position
long cursor_pos = this.position;
int line = this.line;
int column = this.column;
while(true) {
// try to transition from states on
// the current codepoint under the
// cursor
next.Clear();
FA.FillMove(states, this.current, next);
if (next.Count > 0)
{
// found at least one transition
// capture the current
// char, advance the input
// position:
this.Advance();
// move to the next states
states.Clear();
FA.FillEpsilonClosure(next, states);
} else {
// no matching transition
// is any current state accepting?
int acc = FA.GetFirstAcceptSymbol(states);
if(acc>-1) {
// accept
return FAMatch.Create(
acc,
this.capture.ToString(),
cursor_pos,
line,
column);
}
// not accepting - error
// keep capturing input until we find a
// valid move or there's no more input
while (this.current != -1 &&
FA.FillMove(initial, this.current).Count == 0)
{
this.Advance();
}
if (capture.Length == 0)
{
// end of input
return FAMatch.Create(-2, null, 0, 0, 0);
}
// error
return FAMatch.Create(-1,
capture.ToString(),
cursor_pos,
line,
column);}
}
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
Of course it's slower. The IEnumerable interface expects a class with methods you have to call to maintain which item in the IEnumerable implementor you're looking at. Calling methods adds overhead, and plenty of it compared to the overhead of an index variable, which you know is just pointer math. Enumerable being slower is not surprising at all. Just don't use it where you don't have to, and that includes LINQ because it's heavily dependent on the IEnumerable interfaces.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles. Dave Kreskowiak
-
Of course it's slower. The IEnumerable interface expects a class with methods you have to call to maintain which item in the IEnumerable implementor you're looking at. Calling methods adds overhead, and plenty of it compared to the overhead of an index variable, which you know is just pointer math. Enumerable being slower is not surprising at all. Just don't use it where you don't have to, and that includes LINQ because it's heavily dependent on the IEnumerable interfaces.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles. Dave Kreskowiak
IList uses methods as well. Virtual calls and everything. There is no direct array access through IList afaik So the primary difference between IEnumerable and IList is the creation of a new object to traverse the former. Microsoft appears to believe that object creation is very cheap in .NET, and everything I've read from them suggests they practically think it's free. It's not. That was 30% gain in performance.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
I just switched IEnumerable to IList and removed foreach (preferring for) and cut my execution time in my test from 65ms to about 45ms. I've put a stripped down version of the code here. The first argument of each emphasized routine was IEnumerable, is now IList with no foreach. This, ladies and gents, is why I don't like LINQ.
// here this._fa is the target machine we will be parsing.
// parse this or otherwise build it and use it here.
IList initial = FA.FillEpsilonClosure(this._fa);
IList next = new List();
IList states = new List(initial);
// start out with an empty capture buffer
this.capture.Clear();
// first move:
if (this.current == -2)
{
this.Advance();
}
// store the current position
long cursor_pos = this.position;
int line = this.line;
int column = this.column;
while(true) {
// try to transition from states on
// the current codepoint under the
// cursor
next.Clear();
FA.FillMove(states, this.current, next);
if (next.Count > 0)
{
// found at least one transition
// capture the current
// char, advance the input
// position:
this.Advance();
// move to the next states
states.Clear();
FA.FillEpsilonClosure(next, states);
} else {
// no matching transition
// is any current state accepting?
int acc = FA.GetFirstAcceptSymbol(states);
if(acc>-1) {
// accept
return FAMatch.Create(
acc,
this.capture.ToString(),
cursor_pos,
line,
column);
}
// not accepting - error
// keep capturing input until we find a
// valid move or there's no more input
while (this.current != -1 &&
FA.FillMove(initial, this.current).Count == 0)
{
this.Advance();
}
if (capture.Length == 0)
{
// end of input
return FAMatch.Create(-2, null, 0, 0, 0);
}
// error
return FAMatch.Create(-1,
capture.ToString(),
cursor_pos,
line,
column);}
}
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
You did the timing in debug mode, I assume? I always suspected that the compiler might optimize such things at release time, but no?
-
Of course it's slower. The IEnumerable interface expects a class with methods you have to call to maintain which item in the IEnumerable implementor you're looking at. Calling methods adds overhead, and plenty of it compared to the overhead of an index variable, which you know is just pointer math. Enumerable being slower is not surprising at all. Just don't use it where you don't have to, and that includes LINQ because it's heavily dependent on the IEnumerable interfaces.
Asking questions is a skill CodeProject Forum Guidelines Google: C# How to debug code Seriously, go read these articles. Dave Kreskowiak
Well, the first call (GetEnumerator) definitely has a penalty, but each retrieval after that (each call to the enumerator) may be as quick as an indexed access... or it may not be. Anyway, I agree with -- if you know you're iterating across an array, use array access instead. And don't use Linq.
-
You did the timing in debug mode, I assume? I always suspected that the compiler might optimize such things at release time, but no?
Nope, that was release build. My code actually warns me if I run the benchmarks in debug, because I do it by mistake so often. :laugh:
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
Well, the first call (GetEnumerator) definitely has a penalty, but each retrieval after that (each call to the enumerator) may be as quick as an indexed access... or it may not be. Anyway, I agree with -- if you know you're iterating across an array, use array access instead. And don't use Linq.
Just to be difficult, I'd argue that an Enumerator - even a special cased one like the implementation on System.String will be slower than indexed access. The reason being is that it's necessary to execute an additional call to MoveNext() for each advance, whereas with indexed access you are simply incrementing a value. You must then call Current to get the actual value. I haven't benchmarked it, but I'd be very surprised if this was not the case.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
I just switched IEnumerable to IList and removed foreach (preferring for) and cut my execution time in my test from 65ms to about 45ms. I've put a stripped down version of the code here. The first argument of each emphasized routine was IEnumerable, is now IList with no foreach. This, ladies and gents, is why I don't like LINQ.
// here this._fa is the target machine we will be parsing.
// parse this or otherwise build it and use it here.
IList initial = FA.FillEpsilonClosure(this._fa);
IList next = new List();
IList states = new List(initial);
// start out with an empty capture buffer
this.capture.Clear();
// first move:
if (this.current == -2)
{
this.Advance();
}
// store the current position
long cursor_pos = this.position;
int line = this.line;
int column = this.column;
while(true) {
// try to transition from states on
// the current codepoint under the
// cursor
next.Clear();
FA.FillMove(states, this.current, next);
if (next.Count > 0)
{
// found at least one transition
// capture the current
// char, advance the input
// position:
this.Advance();
// move to the next states
states.Clear();
FA.FillEpsilonClosure(next, states);
} else {
// no matching transition
// is any current state accepting?
int acc = FA.GetFirstAcceptSymbol(states);
if(acc>-1) {
// accept
return FAMatch.Create(
acc,
this.capture.ToString(),
cursor_pos,
line,
column);
}
// not accepting - error
// keep capturing input until we find a
// valid move or there's no more input
while (this.current != -1 &&
FA.FillMove(initial, this.current).Count == 0)
{
this.Advance();
}
if (capture.Length == 0)
{
// end of input
return FAMatch.Create(-2, null, 0, 0, 0);
}
// error
return FAMatch.Create(-1,
capture.ToString(),
cursor_pos,
line,
column);}
}
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
I use ICollection in the absence of any other requirements. [https://stackoverflow.com/questions/10113244/why-use-icollection-and-not-ienumerable-or-listt-on-many-many-one-many-relatio\](https://stackoverflow.com/questions/10113244/why-use-icollection-and-not-ienumerable-or-listt-on-many-many-one-many-relatio)
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
-
I just switched IEnumerable to IList and removed foreach (preferring for) and cut my execution time in my test from 65ms to about 45ms. I've put a stripped down version of the code here. The first argument of each emphasized routine was IEnumerable, is now IList with no foreach. This, ladies and gents, is why I don't like LINQ.
// here this._fa is the target machine we will be parsing.
// parse this or otherwise build it and use it here.
IList initial = FA.FillEpsilonClosure(this._fa);
IList next = new List();
IList states = new List(initial);
// start out with an empty capture buffer
this.capture.Clear();
// first move:
if (this.current == -2)
{
this.Advance();
}
// store the current position
long cursor_pos = this.position;
int line = this.line;
int column = this.column;
while(true) {
// try to transition from states on
// the current codepoint under the
// cursor
next.Clear();
FA.FillMove(states, this.current, next);
if (next.Count > 0)
{
// found at least one transition
// capture the current
// char, advance the input
// position:
this.Advance();
// move to the next states
states.Clear();
FA.FillEpsilonClosure(next, states);
} else {
// no matching transition
// is any current state accepting?
int acc = FA.GetFirstAcceptSymbol(states);
if(acc>-1) {
// accept
return FAMatch.Create(
acc,
this.capture.ToString(),
cursor_pos,
line,
column);
}
// not accepting - error
// keep capturing input until we find a
// valid move or there's no more input
while (this.current != -1 &&
FA.FillMove(initial, this.current).Count == 0)
{
this.Advance();
}
if (capture.Length == 0)
{
// end of input
return FAMatch.Create(-2, null, 0, 0, 0);
}
// error
return FAMatch.Create(-1,
capture.ToString(),
cursor_pos,
line,
column);}
}
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
Well, yes, and no. The answer is it depends on which Framework and version that you are using. This video will expand on this: Microsoft FINALLY fixed foreach loops in .NET 7 - YouTube[^]
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
-
I use ICollection in the absence of any other requirements. [https://stackoverflow.com/questions/10113244/why-use-icollection-and-not-ienumerable-or-listt-on-many-many-one-many-relatio\](https://stackoverflow.com/questions/10113244/why-use-icollection-and-not-ienumerable-or-listt-on-many-many-one-many-relatio)
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
If I don't care about access performance in general I will use
IEnumerable<T>
if I can rather than a collection. The reason being is (A) I don't like to impose functionality I'm not going to use and enumerating a collection is the same as enumerating with IEnumerable. (B) Lazy loading isn't really doable with collections in most circumstances because of the presence of count. (C) Collections provide methods to modify them. I certainly don't like suggesting I will modify something I won't, so if i can take the immutable version for a read only function i will. (D) unbounded collections are not supported by .NET collections. You must know the count ahead of time. My choice of switching to IList was improved index access performance. ICollection doesn't provide that.Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
Well, yes, and no. The answer is it depends on which Framework and version that you are using. This video will expand on this: Microsoft FINALLY fixed foreach loops in .NET 7 - YouTube[^]
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
That's interesting! I'm currently targeting .NET 6 but I will keep that in mind. Thanks.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
That's interesting! I'm currently targeting .NET 6 but I will keep that in mind. Thanks.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
It's a simple move from .Net 6.* to to .Net 8.* too... I feel sorry for those stuck in the .Net Framework world, they lose out on all of the performance improvements, in most cases, by simply switching and recompiling.
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
-
It's a simple move from .Net 6.* to to .Net 8.* too... I feel sorry for those stuck in the .Net Framework world, they lose out on all of the performance improvements, in most cases, by simply switching and recompiling.
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
Waaaa! I'm stuck in the framework right now.
The difficult we do right away... ...the impossible takes slightly longer.
-
It's a simple move from .Net 6.* to to .Net 8.* too... I feel sorry for those stuck in the .Net Framework world, they lose out on all of the performance improvements, in most cases, by simply switching and recompiling.
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
I recently made my Visual FA solution target the .NET Framework in addition to Core and Standard. So I have VisualFA.csproj and VisualFA.DNF.csproj. The latter is the same project but for DNF. All the source files are linked via "Add as link" from the first project so I only have one copy. I then use a conditional compilation constant to add or remove the use of spans in code since .NET framework and as far as I can tell, VB.NET don't support them.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
-
I recently made my Visual FA solution target the .NET Framework in addition to Core and Standard. So I have VisualFA.csproj and VisualFA.DNF.csproj. The latter is the same project but for DNF. All the source files are linked via "Add as link" from the first project so I only have one copy. I then use a conditional compilation constant to add or remove the use of spans in code since .NET framework and as far as I can tell, VB.NET don't support them.
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix
VB.Net supports both frameworks. If you look at my most recent articles, here on CodeProject, I support C# & VB.Net on .Net Core & .Net Frsmework. However, almost 12 months ago, there was a change. This Microsoft blog post explains: Update to the .NET language strategy - .NET Blog[^]
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
-
VB.Net supports both frameworks. If you look at my most recent articles, here on CodeProject, I support C# & VB.Net on .Net Core & .Net Frsmework. However, almost 12 months ago, there was a change. This Microsoft blog post explains: Update to the .NET language strategy - .NET Blog[^]
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
I realize it supports both frameworks. I'm saying it doesn't seem to support spans, and I don't think ReadOnlySpan is marked obsolete, but I haven't looked 'ReadOnlySpan(Of Char)' is obsolete: 'Types with embedded references are not supported in this version of your compiler.'. Does not compile. I get the above
Private Function _BlockEnd0(ByVal s As ReadOnlySpan(Of Char), ByVal cp As Integer, ByVal len As Integer, ByVal position As Integer, ByVal line As Integer, ByVal column As Integer) As FAMatch
Check out my IoT graphics library here: https://honeythecodewitch.com/gfx And my IoT UI/User Experience library here: https://honeythecodewitch.com/uix