|
It depends on what I'm doing. In some scenarios, such as when you don't need to garbage collect but for at the end, such as CLI tools often do then yes, absolutely, because you've finished doing useful work and you don't need to make the user wait for the collection (even if the process is still running at that point you can have written out all of your output and everything.)
I write a lot of command line tools that do complicated things, like Deslang: From Code to CodeDOM and Back[^] that absolutely benefit from doing things this way.
I should add, that modern GCs collect in the background, and that should perhaps influence one's decision as it's probably less expensive overall to do one large collection than a bunch of little ones, particularly when asynchronicity is involved.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
If you do buddy with a set of freelist heads, one for each size, and your buddy combiner orders the freelist, you have an extra benefit of locality: most accesses would go to the lower end of the heap, making better use of virtual memory (less paging).
A background GC could unhook a freelist (maybe leaving a couple entries in the list for use while the GC was working), returning with one shorter list for the original freelist and one list of combined buddies to be put into the next higher size freelist.
The head end of the freelist may be rather unordered - this is where all the allocation and freeing is taking place. If the list is long - it hasn't been emptied for quite some time - the tail end may be perfectly sorted after the previous GC/combination round. If you do sorting e.g. by Smoothsort, handling the already sorted part has complexity O(n), so most likely, the long freelist will not required much effort.
You find buddies by traversing a sorted list, so the list of buddy pairs will also be sorted. If the next higher freelist is also mostly sorted, all buddy pairs is inserted into this is list in a single traversal.
I would do real timing tests with a synthetic heap load (modeled after a relevant usage scenario) to see if it really is worthwhile the resource cost of an asynchronous GC thread - strongly suspecting that a finely tuned incremental but synchronous buddy manager can do it both at a lower total resource cost and with so small delays that it would be a much better solution.
Final remark:
"you've finished doing useful work and you don't need to make the user wait for the collection". In most systems, each process has its own heap. Multiple processes allocating from one common global heap requires a lot of resource consuming synchronization. Most CLI programs are run in their own processes. So when they complete, noone cares about what their heap looks like at that time. There is no reason to do any garbage collection at that time. The entire data segment holding the heap is released en bloc.
In an embedded system, you often have a single systemwide heap. But few embedded system have CLI interfaces for running arbitrary programs that start up and terminate as a function of user operations. Even if the embedded system has some sort of UI, user actions are usually limited to activating specific built-in operations in the embedded code, not separate CLI oriented programs. But of course, there may be exceptions
Religious freedom is the freedom to say that two plus two make five.
|
|
|
|
|
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 that foreach 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.
modified 19-Jan-24 11:05am.
|
|
|
|
|
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
modified 18-Jan-24 21:50pm.
|
|
|
|
|
You "think"?
We do not think, we measure. If you say that you think, you think that you think.
Measure or shut the elephant op.
Bastard Programmer from Hell
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
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.
|
|
|
|
|
IList<t> uses methods as well. Virtual calls and everything. There is no direct array access through IList<t> afaik
So the primary difference between IEnumerable<t> and IList<t> 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
|
|
|
|
|
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
|
|
|
|
|
Upvoted and sorry.
It is a decision. Do you want the best in speed? Or do you just need to get things done and be readable?
Bastard Programmer from Hell
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
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.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
|
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
|
|
|
|
|
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.
|
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
Yes, covered in that blog post. However, a C# facade library can address that limitation. Performace wise, VB.Net is just as fast as C# on both frameworks. My latest JSON Streaming[^] article has the benchmarks to prove it. That too uses ReadOnlySpan and ref strut with async/await . I mention the VB.Net limitation in the article. Sadly, I think that the article was too much for most readers.
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 great for your situation. In my current scenario this code was generated by a tool, and specifically designed to be able to produce dependency free code. I might actually consider the facade idea though for when it is opted to rely on the runtimes - right now the VB code can't under the newer frameworks unless you turn off spans in the compiled runtime itself - the build - not at runtime - it's conditionally compiled in. So that facade may fix that issue.
And yet otherwise in my tests, the spanless string approach i use (Substring instead of Splice) doesn't yield noticeably less performance. That leads me to suspect I'm not using it to its fullest - an encouraging thought in the big picture because it means I can get even more speed out of it. I'm not sure that's possible though because no matter how I think about approaching it a copy is always necessary by the time you hit the Value property off FAMatch . It's a head scratcher.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|
|
Quote: And yet otherwise in my tests, the spanless string approach i use (Substring instead of Splice) doesn't yield noticeably less performance. That leads me to suspect I'm not using it to its fullest - an encouraging thought in the big picture because it means I can get even more speed out of it. I'm not sure that's possible though because no matter how I think about approaching it a copy is always necessary by the time you hit the Value property off FAMatch. It's a head scratcher.
Without knowing specifics, it is difficult to comment.
That article is about dealing with gigabytes of data using streams efficiently keeping allocations to a minimum. There was a lot of research, trial & error done to find the best optimal solution. I even looked at the source code of Microsoft's latest (At the time) .Net Core. Renting ReadOnlyMemory<t>[^] was not suitable as all memory blocks needed to be of the same size otherwise nulls fill the gaps. This is not documented anywhere! And I did look. That was a real headscratcher at the time.
Don't get me started on ref strut in an asynchronous environment...
I'm sure if you take a step back, do a bit of research, experimenting, digging into the Microsoft code, you will find a solution.
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 feel like it might be chasing ghosts, particularly since I already get really great performance out of the thing, especially compared to .NET Regex even though that always uses ReadOnlySpan. I still beat it by 3x in the best case.
Microsoft Regex "Lexer": [■■■■■■■■■■] 100% Found 220000 matches in 35ms
Microsoft Regex compiled "Lexer": [■■■■■■■■■■] 100% Found 220000 matches in 20ms
FAStringRunner (proto): [■■■■■■■■■■] 100% Found 220000 matches in 7ms
FATextReaderRunner: (proto) [■■■■■■■■■■] 100% Found 220000 matches in 13ms
FAStringDfaTableRunner: [■■■■■■■■■■] 100% Found 220000 matches in 10ms
FATextReaderDfaTableRunner: [■■■■■■■■■■] 100% Found 220000 matches in 14ms
FAStringStateRunner (NFA): [■■■■■■■■■■] 100% Found 220000 matches in 145ms
FAStringStateRunner (Compact NFA): [■■■■■■■■■■] 100% Found 220000 matches in 43ms
FATextReaderStateRunner (Compact NFA): [■■■■■■■■■■] 100% Found 220000 matches in 48ms
FAStringStateRunner (DFA): [■■■■■■■■■■] 100% Found 220000 matches in 11ms
FATextReaderStateRunner (DFA): [■■■■■■■■■■] 100% Found 220000 matches in 16ms
FAStringRunner (Compiled): [■■■■■■■■■■] 100% Found 220000 matches in 7ms
FATextReaderRunner (Compiled): [■■■■■■■■■■] 100% Found 220000 matches in 12ms
7ms is about what I get compared to microsoft's 20 if I'm making the fairest comparison possible (apples vs apples) 'cept mine doesn't backtrack or support a bunch of fluff. (though it lacks anchors )
If I can't get another 10% out of this I don't think it's worth the trouble.
Check out my IoT graphics library here:
https://honeythecodewitch.com/gfx
And my IoT UI/User Experience library here:
https://honeythecodewitch.com/uix
|
|
|
|