-
Notifications
You must be signed in to change notification settings - Fork 13
Implement CString as a wrapper around a C-String to avoid using std::string #107
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
base: master
Are you sure you want to change the base?
Conversation
|
Your call. If you like to have it in the master please fix the conflicts that occurred after merging #105 . |
|
Rebased to master to fix the conflicts. Had to squash the commits into 1 as there were to many conflicts and new functions so most commits would have been pointless. |
AndreasGocht
left a comment
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.
There are a few design decisions I do not understand. For example:
- Why do you have a CString and a PythonCString class? Arent Python C-Strings the only thing we are dealing with?
- Why does
make_region_namestill return astd::string?
I have to admit, I am not happy having now four different string representations:
std::string- classical c strings
CStringPythonCString
I think we should either move to only PythonCStrings for anything string related or we stay with the current approach.
Best,
Andreas
| SCOREP_User_RegionSetGroup(region_handle.value, | ||
| std::string(module, 0, module.find('.')).c_str()); | ||
| const auto& region_name = make_region_name(module, function_name); | ||
| region_handle = create_user_region(region_name, module, file_name, line_number); |
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.
Please either rename to scorep_create_user_region or use the initial code here, what I would prefer, as we would have all the Score-P calls on one level.
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.
The point is that we duplicate the code on how a user region is created and that code is slightly more than the 2 SCOREP_* calls if you want to avoid useless memory allocations: std::string(module, 0, module.find('.')) copies the string even when module was a std::string already and no dot is found which is what this PR strives to avoid. Encapsulating duplicate code is what functions are for.
And why scorep_create_user_region? What is not clear from the current name? And why the scorep_ prefix which makes it look like a Score-P call while it is a scorepy call creating a user region. Maybe create_scorep_user_region if you want to be more explicit?
|
|
||
| /// Pair of a C-String and it's length useful for PyArg_ParseTuple with 's#' | ||
| /// Implicitely converts to CString. | ||
| struct PythonCString |
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.
Why not merging CString and PythonCString to PythongCString?
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.
PythonCString is a workaround for the situation where you need a const char* and size pair. CString is a NULL-terminated sequence of chars with a length. According to rules of encapsulation you must not have write access to the members of CString as that could destroy invariants, in this case that len is the length of s. This doesn't hold for PythonCString which is simply for getting a CString from Python using the PyArg_ParseTuple function and unsafe to use further. Hence to be used only directly at the API boundary.
| assert(module_name); | ||
| // TODO: Use string_view/CString comparison? | ||
| if (std::string(name) != "_unsetprofile" && std::string(module_name, 0, 6) != "scorep") | ||
| const CString name = get_string_from_python(*code.co_name); |
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.
What about an Initalisert, which takes the frame?
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.
Can you elaborate?
| #endif | ||
| }; | ||
|
|
||
| static region_handle create_user_region(const std::string& region_name, const CString module, |
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.
why std::string for region_name?
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.
Because this is what you have when calling the function. I could add an overloaded constructor to CString so it can be implicitely (or better explicitely) converted from a std::string if you like.
|
|
||
| // Extract main module name if module is like "mainmodule.submodule.subsubmodule" | ||
| const char* dot_pos = module.find('.'); | ||
| if (dot_pos) |
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 would prefer to move this to PythonCString.
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.
Can you explain what you want to move exactly? Keep in mind that allocations should be avoided hence a string copy must only be done when required, in this case when a dot was found
| /// Combine the arguments into a region name | ||
| /// Return value is a statically allocated string to avoid memory (re)allocations | ||
| inline const std::string& make_region_name(const std::string& module_name, const std::string& name) | ||
| inline const std::string& make_region_name(const CString module_name, const CString name) |
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.
As written I would vote for implementing this returning PythonCString
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.
The result is used to search a map with keys of std::string. So if this should return a CString then CString would need to be implicitely convertible from const std::string& to work with that. Shall I do that?
| return reinterpret_cast<detail::ReplaceArgsToPyObject_t<TFunc>*>(func); | ||
| } | ||
|
|
||
| inline CString get_string_from_python(PyObject& o) |
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.
Add this as function, or initialiser of PythonCString
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 can do that. However the intention of CString was to keep it generic. This separates concerns. Adding this function would tie it to Python
What is wrong with this function?
Because that is what is need for the map where it is used. See #107 (comment) for an alternative
Correct me if I'm wrong but there are no "classical c strings" used anymore (except as sinks in |
This avoids unnecessary memory allocations by wrapping the raw pointer (and the length).
I did some experiments using the 2 Benchmark PRs and the simpleFunc BM.
Master:
CString w/o length:
CString w/ length:
CString w/ length and mem* functions:
So yes it does get faster although the effect in this limited scenario is limited.