-
-
Notifications
You must be signed in to change notification settings - Fork 21.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Span
struct (replacing StrRange
). Spans represent read-only access to a contiguous array, resembling std::span
.
#100293
Conversation
e3be56f
to
b8c9998
Compare
Looks like the builds are failing. |
b8c9998
to
9f5d7f0
Compare
There's a |
Oh yeah, I see it. |
9f5d7f0
to
0a2ce2d
Compare
To avoid the MSVC compiler issue (which I have previously determined to be a cache issue), I am touching |
0a2ce2d
to
3888e73
Compare
StrRange
-> BufferView
. Move find
, rfind
and contains
functions from CowData
to BufferView
.StrRange
-> Span
. Move find
, rfind
and contains
functions from CowData
to Span
.
I renamed |
825c23a
to
e20f020
Compare
d787cfb
to
191f22a
Compare
967b48e
to
49fda0d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd encourage adding tests as well. Seeing as you're making a new template, you'd be free to make several functions constexpr
for compile-time sanity checks:
TEST_CASE("[Span] Constant Validators") {
constexpr Span<uint8_t> span_empty;
static_assert(span_empty.size() == 0);
static_assert(span_empty.is_empty());
constexpr uint8_t byte = 0;
constexpr Span<uint8_t> span_byte = Span<uint8_t>(byte, 1);
static_assert(span_byte.size() == 1);
static_assert(!span_byte.is_empty());
}
I like this test! I'll add some more as well. |
61e2cdd
to
2e27f3c
Compare
@@ -31,6 +31,7 @@ | |||
#ifndef SORT_EFFECTS_RD_H | |||
#define SORT_EFFECTS_RD_H | |||
|
|||
#include "servers/rendering/renderer_rd/shader_rd.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick fix due to build failing because of erroneous cache restores.
I have #100293 (comment) that including a non-generated file explicitly solves such cache issues for good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Codestyle checks out & the use of more modern C++ concepts is a welcome addition
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is ready to go. But we need to settle the size_t
debate. Personally I lean towards just making it a uint64_t
and calling it a day.
I appreciate the reasons for size_t
, but I think the pain of having a changing size isn't going to be worth the slight optimization for 32 bit systems. If you look at download numbers, almost no one uses the 32 bit version of Godot. And, ultimately, we need to prioritize developer ergonomics in this case. Have a fixed size that we can count on and that matches our equivalent container types is IMO the most important consideration.
CC @lawnjelly @hpvb It would be great for you two to weigh in with your thoughts sometime soon so we can merge this and begin the process of migrating over to Span
|
|
Yes This is the same reason we prefer |
My thoughts here would be not to use Personally I would also default If you default to 64 bit, you then may end up having to do this (or the equivalent cast, either explicit or implicit):
As well as extra code, this now creates a bug on every use case - what happens if the size IS larger than 32 bit? Same problem occurs with a cast. Are you going to write error checking code in every loop? Or are you going to force every loop to 64 bit just in case? You also potentially have to make all your structures 64 bit just in case, and it can spread in the codebase, like a Nan. The actual real world cases for 64 bit in loops are few and far between (memory addresses, large videos, files, not a lot else), and imo in those cases it is better to use a 64 bit version explicitly (e.g. with the templated In general I have seen some opinions that 64 bit is free but afaik it isn't - quite apart from any CPU costs it also has the potential to bloat structures and data, on both 32 bit and 64 bit platforms. (sorry I've read back and realised I was mostly repeating what I said before in earlier comments from a couple months back .. 😁 )
BTW this is all in my opinion and what I personally would go for, since I was asked for it above. What you guys choose to use it up to you (it likely mostly won't affect me except in terms of forward ports). |
@lawnjelly When I wrote my comment I was forgetting that LocalVector uses uint32_t. I thought that we had standardized on uint64_t for both LocalVector and Vector. Looking through the codebase it seems that LocalVector is templated and can take another type for its size, but it appears that, in practice, it always uses uint32_t. The only concern I have for templating is the edge case where spans are parameters to methods that can take either Vectors or LocalVectors. If Span is templated, then those methods need to be templated too, which will cause a lot of pain |
I'll be damned! I always interpreted the specification as equivalent to a max possible size, but maybe I crossed some wires with In any case, I'm not totally against It's also worth noting that there already was a transition to 64-bit for CowData in #86730, which plenty of container types build on top of. It's implemented in a somewhat archaic manner with |
Just trying to guess what you mean here, do you mean if we default to We could just check this on the span constructor that it's within the 32 bit range. For functions that are likely to need 64 bit input just make them take It is possible on much modern hardware there may be less penalties from 64 bit in terms of CPU so maybe we could get away with it, but bear in mind even the size of counters is doubled, stack size, storage size etc. I come from the school of making use of every byte so it's hard to adjust to, especially with how rarely 64 bit is actually required. 😁 Remember 32 bit is enough to access 4Gb of We presumably also have a lot of existing code that uses 32 bit counters, and if swapping to 64 bit we should probably also consider swapping loops to e.g.:
otherwise each of these would be an overflow potential, and we should also evaluate whether there are implicit conversion costs when comparing 32 to 64 bit. So in summary, I personally think 64 bit counters throughout core is madness, but go for it if you're feeling lucky. 😉 User code is another matter. Bound languages are going to be so slow the 32/64 bit makes no odds, so I'm not surprised it was used in bound containers. |
Some thoughts: I think we could store a |
Alright, thanks for all the perspectives! For me, As for @lawnjelly's templates: I think that doesn't really solve any problems, because either 32-bit or 64-bit would have to be the default, and be used for
Generally, the speed of As a final note, I'd still prefer Summary (for now)(Edit: reading through this again, it's admittedly biased, but i hope it does the job 😅)
|
…ccess to a contiguous array, resembling `std::span`.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good on my end!
I'm open to revisiting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Barring my preference for 32 bit, it looks great, but I can see I'm outvoted on that and it's not a hill to die on.
As long as I get bragging rights when you later realise 32 bit was a better choice. 👍 😉
Thanks! |
It is currently difficult to run algorithms on
String
,Vector
,LocalVector
data ranges or plain C arrays without copying the data first. This leads to inefficiencies.This PR adds the
Span
class. A span represents a view into an array (it's apointer
andsize
).With
Span
, it will be easier to implement functions with more agnosticism as to the memory storage, helping to reduce unnecessary copies. Additionally,Span
will help bridge the gap betweenLocalVector
andVector
, helping to address godotengine/godot-proposals#5144.Span
is similar toStrView
which it replaces, but meant for more than just strings. It is furthermore similar toVectorView
which will be replaced in a future PR.Example
The
String.path_to
implementation currently has the following lines of code:godot/core/string/ustring.cpp
Lines 5108 to 5109 in c2e4ae7
Here, the String is first subselected through
substr
, causing a copy to be made.Then, it is split through a
split
call, through which multiple copies of regions of the string are made.None of the regions are modified in the process - the copies are made merely because it is not possible to avoid them.
An optimized implementation could look like this:
In this implementation, no copies of the string need to be made, because all
Span
s used here are views into the original string (spans::split
andsubspan
would need to be added as functions).Discussion
Span
is named afterstd::span
of C++20 std::span.Span
is const-only because a need for mutable spans is not obvious yet. It is easier to use if it is const by default. If mutable spans are needed in the future, it can be proposed then.