-
-
Notifications
You must be signed in to change notification settings - Fork 21.8k
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
base: master
Are you sure you want to change the base?
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. |
49fda0d
to
f42523f
Compare
f42523f
to
f5794e1
Compare
9979811
to
d7465a5
Compare
Nice, looks like my include fix from #100434 has fixed the |
Hrm... Wondering again if it wouldn't be possible to just have |
d7465a5
to
bf0325b
Compare
I've updated the PR according to feedback from the godot core meeting:
The PR is ready for review again. |
bf0325b
to
a7dee20
Compare
core/templates/span.h
Outdated
template <typename T> | ||
class Span { | ||
const T *_ptr = nullptr; | ||
uint32_t _len = 0; |
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.
This is free to make this 64bit as you'll have 32 bit padding right now
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.
On 32-bit systems the current implementation should be size 64 align 32, and on 64-bit systems it should be size 96 align 64, no?
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.
on 32 bit, you're just 64, but I'm not sure 4.x is compiled to 32 bits ? And on 64 you have 64bits of pointer, 32 bits of integer, and then you need to pad to be aligned to the max alignment of your fields (which is 64 due to the pointer)
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.
then you need to pad to be aligned to the max alignment of your fields (which is 64 due to the pointer)
Padding comes before the struct, not after. If you have a Span
as a variable, then a uint32_t
as the next one, it can fit right after _len
without padding because it aligns to the 32 bit left by Span
. If I were to make it uint64_t
it would take 32 more bits.
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.
No, that's not right. Padding is at the end of the structure (or between the elements) never at the start. And if you declare a Span on the stack, it will pad, the next uin32_t variable will not be directly after your _len field. Except if you #pragma pack(push, 1)
so that the compiler does not put padding bytes, but this is not the case here.
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.
Another problem to think about in advance here by the way is that we might have a caller that uses something like:
for (u32 n=0; n<span.size(); n++)
This will end up happening, this is partly why variable size types are such a nightmare.
So it will afaik end up promoting n to 64 bit each time in e.g. a tight loop, just to make this comparison. (This might be free, or might not..)
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.
For these reasons actually I would reconsider fixing size
as u32
(similar to LocalVector
), and using padding for 64 bit, as it will make usage far easier to reason about and remove platform effects.
In 99.9% of cases, u32
gives 4gb range for 8 bit values, and more when addressing larger T
sizes. For those cases that require more than that, they could use e.g. a 64 bit span?
Just my personal thoughts though.
Another knock on effect:
The moment you make size
able to hold 64 bit, everywhere that stores this has to account for this possibility, or else have error handling for > 32 bit. This means knock on structures end up having to hold 64 bit instead of 32 bit.
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'm on your side, as I tend to prefer fixed size to make the layout clearer and not platform dependant. This i why I wrote that :
I like uint64_t better BUT ...
But size_t seems to be used a lot in the codebase already....
So it will afaik end up promoting n to 64 bit each time in e.g. a tight loop, just to make this comparison. (This might be free, or might not..)
When I look at assembly for this (and take this with a grain of salt, as some different usage may cause different behaviour), at my surprise, it is totally not free, as it forces one additional move ( godbolt ). It makes sense when you think about it, as it have to do the inc on a 32 bit register for overflow reasons, then move it to a 64 bits register for the comparison. Not a big deal, but it has a cost, so we should take it into consideration.
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.
But size_t seems to be used a lot in the codebase already....
size_t
is (correctly) used where OS and API calls return a size_t
, however historically we haven't (to my knowledge) used it much internally, for containers etc. But bear in mind I'm more familiar with 3.x.
I can see it used in lru.h
(which may be new), but we should consider whether it should be used there.
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.
Let me summarize my current thoughts.
32 bit uint
would put 64-bit systems at a disadvantage because > 4gb buffers (at 1 byte objects) would not be supported. Such buffers are admittedly rare, but do happen in my line of work (which is not games admittedly).
64 bit uint
would be perfect on 64 bit systems, but slightly slow and potentially unsafe on 32 bit systems.
size_t
is somewhat fishy because it's different sizes on different systems.
I'm still on the size_t
side because it's fast, safe, and while we have no guarantees C++ side, we do know it to be correct for all major platforms we expect Godot to ship on. It's also used by all C++ size types (sizeof(array)
, malloc
, ...) and STL container types (std::span
, std::array
...).
StrRange
-> Span
. Move find
, rfind
and contains
functions from CowData
to Span
.Span
struct (replacing StrRange
). Spans represent read-only access to a contiguous array, resembling std::span
.
a7dee20
to
61e2cdd
Compare
…ccess to a contiguous array, resembling `std::span`.
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
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.