Skip to content
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 const references detected by clang-tidy #84445

Merged

Conversation

Rubonnek
Copy link
Member

@Rubonnek Rubonnek commented Nov 4, 2023

I stumbled upon a couple of lines where data is unnecessarily copied data during variable initialization (for which I opened #84379) and decided to scan the codebase with clang-tidy to see what it could find.

If this pull request conflicts with another one, priority should be given to the other pull request since this particular patch can be automatically generated with:

find . -name 'thirdparty' -prune -o -iname '*.cpp' -print -exec clang-tidy --checks=performance-unnecessary-copy-initialization -p $(pwd) --fix --fix-errors {} \;
git diff -U0  | grepdiff -E 'const.*&' --output-matching=hunk | sed -e 's/const \(.*\)& /const \1 \&/g' | git apply --cached --unidiff-zero
git reset platform/macos/export/export_plugin.cpp

And selectively choosing what to commit

@Rubonnek Rubonnek requested review from a team as code owners November 4, 2023 11:32
@Rubonnek Rubonnek added this to the 4.x milestone Nov 4, 2023
@@ -586,7 +586,7 @@ void EditorExportPlatformMacOS::_make_icon(const Ref<EditorExportPreset> &p_pres
};

for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) {
Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy?
const Ref<Image> &copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is referring to a copy, with some confusion on what happens with reference copying.

I'd at least remove the comment but I'd investigate whether that's relevant anymore to the code it's in.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just checked and the comment is still relevant. There's a single EditorExportPlatformMacOS::_make_icon call from which p_icon comes from and there's a specific case where referencing p_icon is not needed. Properly patching this merits its own pull request. I'll undo this change from this PR.

@Rubonnek Rubonnek force-pushed the add-const-references-clang-tidy branch from 7fa5cb9 to 4718e78 Compare November 4, 2023 14:14
@darksylinc
Copy link
Contributor

darksylinc commented Nov 4, 2023

Be careful, because Godot uses its own structures. This is not the STL.

For example this code:

String path = fields[0];

Does not actually copy. The String class is implemented using CoW (Copy on Write) which means a copy is almost free unless "path" is modified. If path is modified only then an actual copy occurs.

Changing it to:

const String &path = fields[0];

Just adds an extra indirection for no reason.

Something more controversial happens with:

Ref<Mesh> mesh = p_meshes[i];
// vs
const Ref<Mesh> &mesh = p_meshes[i];

The Ref<T> class is just a reference-counted object. Whether an extra indirection is better or incrementing and decrementing the reference count is better depends on profiling. It may also cause different behavior because consider the following:

void null_some_data()
{
   some_data = nullptr; // Decrease reference count. It may reach 0.
}


Ref<Mesh> mesh = some_data[i];
null_some_data();
mesh->do_something(); // valid

// vs

const Ref<Mesh> &mesh = some_data[i];
null_some_data();
mesh->do_something(); // ERROR! This is a nullptr.

@AThousandShips
Copy link
Member

AThousandShips commented Nov 4, 2023

The data here are largely (with some exceptions) COW data types (Vector<T>, String, etc.), or reference types (Ref<T>), which means copying and creating references are largely equivalent

However for performance critical paths there is a small, but potentially important, difference:
COW has synchronization for copying, which can be performance negative, as any copying of a COW type invokes atomics

Additionally: The primary benefit of replacing with const references is that they can be optimized by the compiler, assuming other optimization assumptions hold

@Rubonnek
Copy link
Member Author

Rubonnek commented Nov 5, 2023

What about set up some clang-tidy rules to check for CI instead of manully checking?

In the past we've opted not to mainly due to false positives and these usually happen when considering header guards, however there's other reasons as well.

One false positive I caught in this particular PR was that the tool suggested the following change:

index 2e0ca7b536..900f9b2714 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1758,7 +1758,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 #endif
 
                String default_driver = driver_hints.get_slice(",", 0);
-               String default_driver_macos = default_driver;
+               const String &default_driver_macos = default_driver;
 #if defined(GLES3_ENABLED) && defined(EGL_STATIC) && defined(MACOS_ENABLED)
                default_driver_macos = "opengl3_angle"; // Default to ANGLE if it's built-in.
 #endif

You can get the above suggestion by scanning with:

clang-tidy --checks=performance-unnecessary-copy-initialization --fix main/main.cpp

At first glance it might seem fine, but upon closer inspection default_driver_macos should not be a const reference due to the header guards right below and the usage of the variable in the lines that follow. If we were to always check in CI with the tool, that patch will always show up assuming the surrounding code never gets refactored.

Another reason for not always checking the source with static analyzers is because we stumble upon Path Explosion. The case above is a small example of how clang-tidy didn't consider all possible cases. Now imagine if it did, and it did so across thousands of possible header values. The number of possible outcomes it has to handle increases quite quickly, meaning that the memory and CPU consumption to statically analyze a single compilation unit can vary wildly. This in turn means it's hard to gauge what resources are required to statically analyze any compilation unit in a CI pipeline. Which is another reason to avoid implementing it since there's a cost associated with it, and the return on investment does not seem to be worth the cost.

I think that covers most of our reasons. If we were to zoom out even more on this problem, we may end up talking about abstract topics such as the Halting Problem, Godel's Incompleteness, and other fundamental problems considered under specific contexts, at which point the answer to whether we should implement the CI check gets blurred by unnecessary details.

@jsjtxietian
Copy link
Contributor

Very valid point, thanks!

@AThousandShips
Copy link
Member

Second this, and adding that these kinds of static checks can easily confuse and mess with new contributors etc.

@AThousandShips AThousandShips removed request for a team November 6, 2023 12:51
@Rubonnek Rubonnek force-pushed the add-const-references-clang-tidy branch from c386d29 to 9154139 Compare November 17, 2023 20:47
@AThousandShips
Copy link
Member

Will take a look, but as suggested above many of these can be converted into simple uses, will recommend some tomorrow

@Rubonnek
Copy link
Member Author

Will take a look, but as suggested above many of these can be converted into simple uses, will recommend some tomorrow

I should add there are many that I think add readability and perhaps should be kept, but I don't have a strong opinion about it. I don't mind simplifying further.

Copy link
Member

@AThousandShips AThousandShips left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got around to it now, leaving it to your discression but most of these are direct improvements and several improve readability by not requiring you to check where the data is from (and are readable in what they represent)

core/object/object.cpp Outdated Show resolved Hide resolved
core/object/object.cpp Outdated Show resolved Hide resolved
editor/debugger/script_editor_debugger.cpp Show resolved Hide resolved
editor/doc_tools.cpp Show resolved Hide resolved
editor/editor_build_profile.cpp Show resolved Hide resolved
scene/resources/font.cpp Show resolved Hide resolved
scene/resources/font.cpp Show resolved Hide resolved
scene/resources/font.cpp Show resolved Hide resolved
scene/resources/visual_shader.cpp Outdated Show resolved Hide resolved
scene/resources/visual_shader.cpp Outdated Show resolved Hide resolved
@Rubonnek Rubonnek marked this pull request as draft November 18, 2023 20:32
@Rubonnek Rubonnek force-pushed the add-const-references-clang-tidy branch 4 times, most recently from 832613f to 3933c29 Compare November 18, 2023 22:41
@Rubonnek Rubonnek force-pushed the add-const-references-clang-tidy branch from 3933c29 to d05f741 Compare November 18, 2023 23:01
@Rubonnek
Copy link
Member Author

@AThousandShips the patches for most of your suggestions were included in #85071.

And thanks for the thorough review -- thanks to that I realized we could use a couple more const char * overloads to the Stringclass. I may add those to #84379 if I find the time later.

@Rubonnek Rubonnek marked this pull request as ready for review November 18, 2023 23:07
Copy link
Member

@AThousandShips AThousandShips left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These look good to me

@Rubonnek Rubonnek force-pushed the add-const-references-clang-tidy branch from d05f741 to a3cb1b0 Compare December 16, 2023 18:37
@AThousandShips AThousandShips removed the request for review from a team December 16, 2023 18:47
Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine to me.

@akien-mga akien-mga modified the milestones: 4.x, 4.3 Jan 4, 2024
@akien-mga akien-mga merged commit 6c390b6 into godotengine:master Jan 4, 2024
15 checks passed
@akien-mga
Copy link
Member

Thanks!

@Rubonnek Rubonnek deleted the add-const-references-clang-tidy branch January 18, 2024 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants