Closed dpwiz closed 1 year ago
This is great, I think it makes a lot of sense to have some concept of unit values that are efficiently backed by an IntSet
or equivalent.
Rep
are fragile and might scare off beginners, and the unsafeCoerce
is, well, you know. Maybe we just flat-out have a derivable Flag
/Singleton
type class that the Tags
/Flags
Store would require on its members? I think if we do that, it might be a good idea to collect all this functionality in a Apecs.Flag
module, so that it's clear that these things are supposed to be used together, and we can put some usage examples in the Haddock. I'd be interested to hear your thoughts on this.My aim was to minimize ritual required to use this.
This PR has it in a most straight-forward way. Nothing is required (not even Rep
test) since nothing is checked.
It should be safe unless you do more reflection/inspection/pointer equality/etc.
The alternative PR has it safe-r, but requires some deriving or typing the obvious boilerplate. Benchmarks show no noticeable difference, being in ns-range.
There can be another way of course. I was just banging on the keyboard and making stores to see if I can beat cached IntMap
in some non-exotic case. That thing is pretty damn fast :sweat_smile:
I had a dive into google just now to find out how components are named in different engines/packages.
The store may as well be named Set
to follow its Map/IntMap
counterpart, I just wanted to experiment with a different scheme.
One may argue that specifying the underlying structure is more relevant to the storage definition than its intended use:
type Storage Fizz = Tags Fizz
vs type Storage Fizz = Set Fizz
.
The class name what would serve as a guard can be named:
Unit
: witnessing equivalence to ()
. Will be a minor head-scratcher for Haskell-newbies.Tag
*: for its intended usage.IsSomething
: having a prefix may be helpful for e.g. relating it to a store name.Thanks for the research, let's go with Tag
then.
The store may as well be named Set to follow its Map/IntMap counterpart
Agreed
The alternative PR has it safe-r, but requires some deriving or typing the obvious boilerplate.
Eh, I think the boilerplate is fine, having deriving (Generic, Tag)
shouldn't be too big of an issue. Maybe we could do something like deriving (Tag, Component) via AsTag MyTag
, and then it pays for itself in terms of line count.
There can be another way of course. I was just banging on the keyboard and making stores to see if I can beat cached IntMap in some non-exotic case.
Hmm, that's a good point, it probably won't be faster than a cache anyway... I suppose we could make a tag-specific cache that doesn't allocate the element vector, but that seems a bit excessive...
Set-based store beats Map-based by roughly 7x.
Surprisingly, using Filter
component changes almost nothing.
tags
unsafe
1: OK (0.72s)
158 ns ± 7.5 ns
100: OK (0.17s)
314 ns ± 29 ns
1k: OK (0.66s)
311 ns ± 11 ns
10k: OK (0.18s)
330 ns ± 31 ns
generic
1: OK (0.20s)
183 ns ± 18 ns
100: OK (0.18s)
332 ns ± 26 ns
1k: OK (0.35s)
322 ns ± 20 ns
10k: OK (0.18s)
328 ns ± 31 ns
map
1: OK (5.07s)
2.22 μs ± 123 ns
100: OK (5.16s)
2.24 μs ± 127 ns
1k: OK (5.15s)
2.24 μs ± 124 ns
10k: OK (5.15s)
2.23 μs ± 143 ns
filter-map
1: OK (5.12s)
2.22 μs ± 136 ns
100: OK (5.11s)
2.21 μs ± 124 ns
1k: OK (5.11s)
2.22 μs ± 131 ns
10k: OK (5.11s)
2.22 μs ± 134 ns
Alllllright.... The culprit was the Cache
wrapper.
1) The IntMap can hold its own candle. 2) The Filter actually works, beating Set.
map
1: OK (0.16s)
146 ns ± 14 ns
100: OK (0.18s)
341 ns ± 25 ns
1k: OK (0.19s)
357 ns ± 24 ns
10k: OK (0.18s)
336 ns ± 33 ns
filter-map
1: OK (0.24s)
110 ns ± 9.3 ns
100: OK (0.57s)
267 ns ± 7.2 ns
1k: OK (0.15s)
274 ns ± 23 ns
10k: OK (0.15s)
271 ns ± 24 ns
Both of the Tag stores are obsoleted by a documentation update :sweat_smile:
Both of the Tag stores are obsoleted by a documentation update sweat_smile
I don't get it, in what way?
The benchmark shows no gain over uncached Map and using Filter is actually faster than both Set implementations.
Kinda like
Filter
pseudo-component (skips lookup), but as a Store for arbitrary unit-like components.