Skip to content

Commit 243b674

Browse files
committed
Add CString class to avoid allocations
1 parent 6a766ae commit 243b674

6 files changed

Lines changed: 82 additions & 27 deletions

File tree

src/scorepy/cInstrumenter.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "cInstrumenter.hpp"
2+
#include "cstring.h"
23
#include "events.hpp"
34
#include "pythonHelpers.hpp"
45
#include <algorithm>
@@ -114,12 +115,9 @@ bool CInstrumenter::on_event(PyFrameObject& frame, int what, PyObject*)
114115
case PyTrace_CALL:
115116
{
116117
const PyCodeObject& code = *frame.f_code;
117-
const char* name = PyUnicode_AsUTF8(code.co_name);
118-
const char* module_name = get_module_name(frame);
119-
assert(name);
120-
assert(module_name);
121-
// TODO: Use string_view/CString comparison?
122-
if (std::string(name) != "_unsetprofile" && std::string(module_name, 0, 6) != "scorep")
118+
const CString name = PyUnicode_AsUTF8(code.co_name);
119+
const CString module_name = get_module_name(frame);
120+
if (name != "_unsetprofile" && !module_name.starts_with("scorep"))
123121
{
124122
const int line_number = code.co_firstlineno;
125123
const auto& region_name = make_region_name(module_name, name);
@@ -131,12 +129,9 @@ bool CInstrumenter::on_event(PyFrameObject& frame, int what, PyObject*)
131129
case PyTrace_RETURN:
132130
{
133131
const PyCodeObject& code = *frame.f_code;
134-
const char* name = PyUnicode_AsUTF8(code.co_name);
135-
const char* module_name = get_module_name(frame);
136-
assert(name);
137-
assert(module_name);
138-
// TODO: Use string_view/CString comparison?
139-
if (std::string(name) != "_unsetprofile" && std::string(module_name, 0, 6) != "scorep")
132+
const CString name = PyUnicode_AsUTF8(code.co_name);
133+
const CString module_name = get_module_name(frame);
134+
if (name != "_unsetprofile" && !module_name.starts_with("scorep"))
140135
{
141136
const auto& region_name = make_region_name(module_name, name);
142137
region_end(region_name);

src/scorepy/cstring.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#pragma once
2+
3+
#include <cassert>
4+
#include <cstring>
5+
6+
namespace scorepy
7+
{
8+
9+
/// Thin wrapper around a C-String (NULL-terminated sequence of chars)
10+
class CString
11+
{
12+
const char* s_;
13+
14+
public:
15+
constexpr CString(const char* s) : s_(s)
16+
{
17+
assert(s_);
18+
}
19+
20+
constexpr const char* c_str() const
21+
{
22+
return s_;
23+
}
24+
/// Find the first occurrence of the character and return a pointer to it or NULL if not found
25+
const char* find(char c) const
26+
{
27+
return std::strchr(s_, c);
28+
}
29+
template <size_t N>
30+
bool starts_with(const char (&prefix)[N]) const
31+
{
32+
return std::strncmp(s_, prefix, N - 1u) == 0;
33+
}
34+
35+
friend bool operator==(const CString& lhs, const CString& rhs)
36+
{
37+
return std::strcmp(lhs.s_, rhs.s_) == 0;
38+
}
39+
friend bool operator!=(const CString& lhs, const CString& rhs)
40+
{
41+
return !(lhs == rhs);
42+
}
43+
};
44+
45+
} // namespace scorepy

src/scorepy/events.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ constexpr region_handle uninitialised_region_handle = region_handle();
2626
static std::unordered_map<std::string, region_handle> regions;
2727
static std::unordered_map<std::string, region_handle> rewind_regions;
2828

29-
void region_begin(const std::string& region_name, std::string module, std::string file_name,
29+
void region_begin(const std::string& region_name, const CString& module, const CString& file_name,
3030
std::uint64_t line_number)
3131
{
3232
auto& region_handle = regions[region_name];
@@ -36,8 +36,17 @@ void region_begin(const std::string& region_name, std::string module, std::strin
3636
SCOREP_User_RegionInit(&region_handle.value, NULL, &SCOREP_User_LastFileHandle,
3737
region_name.c_str(), SCOREP_USER_REGION_TYPE_FUNCTION,
3838
file_name.c_str(), line_number);
39-
SCOREP_User_RegionSetGroup(region_handle.value,
40-
std::string(module, 0, module.find('.')).c_str());
39+
// Extract main module name if module is like "mainmodule.submodule.subsubmodule"
40+
const char* dot_pos = module.find('.');
41+
if (dot_pos)
42+
{
43+
const std::string main_module(module.c_str(), dot_pos);
44+
SCOREP_User_RegionSetGroup(region_handle.value, main_module.c_str());
45+
}
46+
else
47+
{
48+
SCOREP_User_RegionSetGroup(region_handle.value, module.c_str());
49+
}
4150
}
4251
SCOREP_User_RegionEnter(region_handle.value);
4352
}

src/scorepy/events.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
#pragma once
22

3+
#include "cstring.h"
34
#include <cstdint>
45
#include <string>
56

67
namespace scorepy
78
{
89
/// Combine the arguments into a region name
910
/// Return value is a statically allocated string to avoid memory (re)allocations
10-
inline const std::string& make_region_name(const char* module_name, const char* name)
11+
inline const std::string& make_region_name(const CString& module_name, const CString& name)
1112
{
1213
static std::string region;
13-
region = module_name;
14+
region = module_name.c_str();
1415
region += ":";
15-
region += name;
16+
region += name.c_str();
1617
return region;
1718
}
1819

19-
void region_begin(const std::string& region_name, std::string module, std::string file_name,
20+
void region_begin(const std::string& region_name, const CString& module, const CString& file_name,
2021
std::uint64_t line_number);
2122
void region_end(const std::string& region_name);
2223

src/scorepy/pythonHelpers.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,32 @@
33

44
namespace scorepy
55
{
6-
const char* get_module_name(const PyFrameObject& frame)
6+
CString get_module_name(const PyFrameObject& frame)
77
{
88
PyObject* module_name = PyDict_GetItemString(frame.f_globals, "__name__");
99
if (module_name)
1010
return PyUnicode_AsUTF8(module_name);
1111

1212
// this is a NUMPY special situation, see NEP-18, and Score-P issue #63
13-
// TODO: Use string_view/C-String to avoid creating 2 std::strings
14-
const char* filename = PyUnicode_AsUTF8(frame.f_code->co_filename);
15-
if (filename && (std::string(filename) == "<__array_function__ internals>"))
13+
const CString filename = PyUnicode_AsUTF8(frame.f_code->co_filename);
14+
if (filename == "<__array_function__ internals>")
15+
{
1616
return "numpy.__array_function__";
17+
}
1718
else
19+
{
1820
return "unkown";
21+
}
1922
}
2023

21-
std::string get_file_name(const PyFrameObject& frame)
24+
CString get_file_name(const PyFrameObject& frame)
2225
{
2326
PyObject* filename = frame.f_code->co_filename;
2427
if (filename == Py_None)
2528
{
2629
return "None";
2730
}
28-
char actual_path[PATH_MAX];
31+
static char actual_path[PATH_MAX];
2932
const char* full_file_name = realpath(PyUnicode_AsUTF8(filename), actual_path);
3033
return full_file_name ? full_file_name : "ErrorPath";
3134
}

src/scorepy/pythonHelpers.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "cstring.h"
34
#include <Python.h>
45
#include <frameobject.h>
56
#include <string>
@@ -73,9 +74,10 @@ auto cast_to_PyFunc(TFunc* func) -> detail::ReplaceArgsToPyObject_t<TFunc>*
7374

7475
/// Return the module name the frame belongs to.
7576
/// The pointer is valid for the lifetime of the frame
76-
const char* get_module_name(const PyFrameObject& frame);
77+
CString get_module_name(const PyFrameObject& frame);
7778
/// Return the file name the frame belongs to
78-
std::string get_file_name(const PyFrameObject& frame);
79+
/// The returned CString is valid until the next call to this function
80+
CString get_file_name(const PyFrameObject& frame);
7981

8082
// Implementation details
8183
namespace detail

0 commit comments

Comments
 (0)