-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Eliminate redundant copies of EMPTY_UNIT object, saving 2.5 KB. #235
Eliminate redundant copies of EMPTY_UNIT object, saving 2.5 KB. #235
Conversation
There are currently 11 copies of the `EMPTY_UNIT` object in the `Calculator.exe` binary, which not only wastes space/footprint in the binary itself, but also means that each copy must be separately initialized, which effects performance. The reason for this is that the object is defined in a shared header file, which then is included by multiple .cpp files, causing each translation unit (.obj) to get a full complete copy of the object. By marking the object as `extern __declspec(selectany)` we can instruct the linker to pick any of the global objects, as we do not need to have 11 unique versions of the EMPTY_UNIT object, we only need 1. The net result is that the `Calculator.exe` binary size is reduced by 2,560 bytes (or 2.5 KB) with no change in behavior, other than the small performance benefit of not initializing 10 redundant copies of the object.
This one could probably be constexpr. |
Yes - in general, prefer standard C++ over Microsoft extensions. |
constexpr already implies inline, so no need to specify that separetly. |
Thanks for the feedback on the pull-request! I tried making it I end up with various errors that are all similar to the following:
It looks like there is some work/proposals (link) in the C++ community to make std::string and friends constexpr, but so far nothing yet. |
std::wstring_view is what we should be using for the Unit constructors, and are compatible with constexpr. Could you update the constructors to accept wstring_views, instead? |
I think the Unit class is supposed to own the strings though, otherwise what will keep the underlying strings alive alive if it just uses a std::wstring_view? (Or maybe I am missing something?) |
Yep, you're right. My mistake. :) |
src/CalcManager/UnitConverter.h
Outdated
@@ -51,7 +51,7 @@ namespace UnitConversionManager | |||
// null checks. | |||
// | |||
// unitId, name, abbreviation, isConversionSource, isConversionTarget, isWhimsical | |||
const Unit EMPTY_UNIT = Unit{ -1, L"", L"", true, true, false }; | |||
extern __declspec(selectany) const Unit EMPTY_UNIT = Unit{ -1, L"", L"", true, true, false }; |
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.
Hey @jefgen, I looked at your change and I think the right fix is to replace extern __declspec(selectany)
with inline
. In this context, inline
means, essentially, if there would be multiple definitions of a variable or function marked inline, the linker should use just one definition. This is equivalent to the intended behavior with your change. I ran a diff between all three options and here are size results:
- Release build, master branch:
4,150,272
bytes - Release build, extern __declspec(selectany):
4,147,712
bytes - Release build, inline:
4,147,712
bytes
As you can see, the difference in size reduction is the same (Please verify for yourself).
Given that the size reduction is the same, we should prefer inline
because it is standard C++ and avoids any issues later, such as using a macro for __declspec(selectany)
.
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.
Thanks for taking a look at the changes @danbelcher-MSFT!
I rebuilt using inline
instead of extern __declspec(selectany)
and I get the same results when examining the output from the linker. (Only one copy of the object instead of 11).
I've pushed an additional commit with the change. Though I'm not sure if you'd rather I force push a new commit, or perhaps just squash merge the two commits when merging?
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.
Don't worry about it. We're taking responsibility of updating the commit message when we close the PR out. Thanks for the consideration though!
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.
Thanks, Jeff!
Description of the changes:
There are currently 11 copies of the
EMPTY_UNIT
object in theCalculator.exe
binary, which not only wastes space/footprint in thebinary itself, but also means that each copy must be separately
initialized, which effects performance.
The reason for this is that the object is defined in a shared header
file, which then is included by multiple .cpp files, causing each
translation unit (.obj) to get a full complete copy of the object.
By marking the object as
inline
we caninstruct the linker to define the object once, as we do
not need to have 11 unique versions of the EMPTY_UNIT object,
we only need 1.
The net result is that the
Calculator.exe
binary size is reduced by2,560 bytes (or 2.5 KB) with no change in behavior, other than
the small performance benefit of not initializing 10 redundant copies
of the object.
How changes were validated: