mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 23:46:15 +01:00
gstrfuncs: Add inline version of g_strdup()
g_strdup() is often used to duplicate static strings, in these cases the compiler could use a faster path because it knows the length of the string at compile time, but this cannot happen because our g_strdup() implementation is hidden. To improve this case, we add a simple implementation of g_strdup() when it is used with static or NULL strings that explicitly uses strlen, g_malloc and memcpy to give hints to the compiler how to behave better. This has definitely some benefits in terms of performances, causing an iteration of 1000000 string duplication to drop from 2.7002s to 1.9428s for a static string and from ~0.6584s to ~0.4408 for a NULL one. Since compiler can optimize these cases quite a bit, the generated code [2] is not increasing a lot, given that it can now avoid generating some code or do it in few simpler steps. Update tests to cover both inlined and non inlined cases. [1] https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3209#note_1644383 [2] https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3209#note_1646662
This commit is contained in:
parent
ed42f57704
commit
cc0fb5e77c
@ -352,12 +352,12 @@ get_C_locale (void)
|
||||
* Returns: a newly-allocated copy of @str
|
||||
*/
|
||||
gchar*
|
||||
g_strdup (const gchar *str)
|
||||
(g_strdup) (const gchar *str)
|
||||
{
|
||||
gchar *new_str;
|
||||
gsize length;
|
||||
|
||||
if (str)
|
||||
if G_LIKELY (str)
|
||||
{
|
||||
length = strlen (str) + 1;
|
||||
new_str = g_new (char, length);
|
||||
|
@ -153,7 +153,7 @@ gboolean (g_str_has_prefix) (const gchar *str,
|
||||
* Without it, it thinks strlen and memcmp may be getting passed NULL
|
||||
* despite the explicit check for NULL right above the calls.
|
||||
*/
|
||||
#define _G_STR_NONNULL(x) (x + !x)
|
||||
#define _G_STR_NONNULL(x) ((x) + !(x))
|
||||
|
||||
#define g_str_has_prefix(STR, PREFIX) \
|
||||
(__builtin_constant_p (PREFIX)? \
|
||||
@ -203,6 +203,23 @@ gboolean (g_str_has_prefix) (const gchar *str,
|
||||
(g_str_has_suffix) (STR, SUFFIX) \
|
||||
)
|
||||
|
||||
#define g_strdup(STR) \
|
||||
(__builtin_constant_p ((STR)) ? \
|
||||
(G_LIKELY ((STR) != NULL) ? \
|
||||
G_GNUC_EXTENSION ({ \
|
||||
const char *const ___str = ((STR)); \
|
||||
const char *const __str = _G_STR_NONNULL (___str); \
|
||||
const size_t __str_len = strlen (__str) + 1; \
|
||||
char *__dup_str = (char *) g_malloc (__str_len); \
|
||||
(char *) memcpy (__dup_str, __str, __str_len); \
|
||||
}) \
|
||||
: \
|
||||
(char *) (NULL) \
|
||||
) \
|
||||
: \
|
||||
(g_strdup) ((STR)) \
|
||||
)
|
||||
|
||||
#endif /* !defined (__GTK_DOC_IGNORE__) && !defined (__GI_SCANNER__) */
|
||||
#endif /* G_GNUC_CHECK_VERSION (2, 0) */
|
||||
|
||||
@ -279,7 +296,7 @@ gchar* g_strup (gchar *string);
|
||||
* ought to be freed with g_free from the caller at some point.
|
||||
*/
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar* g_strdup (const gchar *str) G_GNUC_MALLOC;
|
||||
gchar* (g_strdup) (const gchar *str) G_GNUC_MALLOC;
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gchar* g_strdup_printf (const gchar *format,
|
||||
...) G_GNUC_PRINTF (1, 2) G_GNUC_MALLOC;
|
||||
|
@ -304,6 +304,7 @@ test_str_equal (void)
|
||||
{
|
||||
const char *str_a = "a";
|
||||
char *str_b = g_strdup ("b");
|
||||
char *str_null = g_strdup (NULL);
|
||||
gconstpointer str_a_ptr = str_a, str_b_ptr = str_b;
|
||||
const unsigned char *str_c = (const unsigned char *) "c";
|
||||
|
||||
@ -317,10 +318,36 @@ test_str_equal (void)
|
||||
g_assert_true (g_str_equal (str_a, str_a_ptr));
|
||||
g_assert_false (g_str_equal (str_a_ptr, str_b_ptr));
|
||||
g_assert_false (g_str_equal (str_c, str_b));
|
||||
g_assert_cmpstr (str_b, !=, str_null);
|
||||
|
||||
g_free (str_b);
|
||||
}
|
||||
|
||||
static void
|
||||
test_strdup (void)
|
||||
{
|
||||
gchar *str;
|
||||
|
||||
g_assert_null ((g_strdup) (NULL));
|
||||
|
||||
str = (g_strdup) ("C++ is cool too!");
|
||||
g_assert_nonnull (str);
|
||||
g_assert_cmpstr (str, ==, "C++ is cool too!");
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
static void
|
||||
test_strdup_macro (void)
|
||||
{
|
||||
gchar *str;
|
||||
|
||||
g_assert_null (g_strdup (NULL));
|
||||
|
||||
str = g_strdup ("C++ is cool too!");
|
||||
g_assert_nonnull (str);
|
||||
g_assert_cmpstr (str, ==, "C++ is cool too!");
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
static void
|
||||
test_string_append (void)
|
||||
@ -444,6 +471,8 @@ main (int argc, char *argv[])
|
||||
g_test_add_func ("/C++/clear-pointer", test_clear_pointer);
|
||||
g_test_add_func ("/C++/steal-pointer", test_steal_pointer);
|
||||
g_test_add_func ("/C++/str-equal", test_str_equal);
|
||||
g_test_add_func ("/C++/strdup", test_strdup);
|
||||
g_test_add_func ("/C++/strdup/macro", test_strdup_macro);
|
||||
g_test_add_func ("/C++/string-append", test_string_append);
|
||||
|
||||
return g_test_run ();
|
||||
|
@ -499,12 +499,53 @@ test_strdup (void)
|
||||
{
|
||||
gchar *str;
|
||||
|
||||
g_assert_null ((g_strdup) (NULL));
|
||||
|
||||
str = (g_strdup) (GLIB_TEST_STRING);
|
||||
g_assert_nonnull (str);
|
||||
g_assert_cmpstr (str, ==, GLIB_TEST_STRING);
|
||||
|
||||
char *other_str = (g_strdup) (str);
|
||||
g_free (str);
|
||||
|
||||
g_assert_nonnull (other_str);
|
||||
g_assert_cmpstr (other_str, ==, GLIB_TEST_STRING);
|
||||
g_clear_pointer (&other_str, g_free);
|
||||
|
||||
str = (g_strdup) ("");
|
||||
g_assert_cmpint (str[0], ==, '\0');
|
||||
g_assert_cmpstr (str, ==, "");
|
||||
g_clear_pointer (&str, g_free);
|
||||
}
|
||||
|
||||
static void
|
||||
test_strdup_inline (void)
|
||||
{
|
||||
gchar *str;
|
||||
|
||||
#if G_GNUC_CHECK_VERSION (2, 0)
|
||||
#ifndef g_strdup
|
||||
#error g_strdup() should be defined as a macro in this platform!
|
||||
#endif
|
||||
#else
|
||||
g_test_incomplete ("g_strdup() is not inlined in this platform");
|
||||
#endif
|
||||
|
||||
/* Testing inline version of g_strdup() function with various positive and
|
||||
* negative cases */
|
||||
|
||||
g_assert_null (g_strdup (NULL));
|
||||
|
||||
str = g_strdup (GLIB_TEST_STRING);
|
||||
g_assert_nonnull (str);
|
||||
g_assert_cmpstr (str, ==, GLIB_TEST_STRING);
|
||||
g_free (str);
|
||||
|
||||
char *other_str = g_strdup (str);
|
||||
g_clear_pointer (&str, g_free);
|
||||
|
||||
g_assert_nonnull (other_str);
|
||||
g_assert_cmpstr (other_str, ==, GLIB_TEST_STRING);
|
||||
g_clear_pointer (&other_str, g_free);
|
||||
|
||||
str = g_strdup ("");
|
||||
g_assert_cmpint (str[0], ==, '\0');
|
||||
@ -2698,6 +2739,7 @@ main (int argc,
|
||||
g_test_add_func ("/strfuncs/strconcat", test_strconcat);
|
||||
g_test_add_func ("/strfuncs/strdelimit", test_strdelimit);
|
||||
g_test_add_func ("/strfuncs/strdup", test_strdup);
|
||||
g_test_add_func ("/strfuncs/strdup/inline", test_strdup_inline);
|
||||
g_test_add_func ("/strfuncs/strdup-printf", test_strdup_printf);
|
||||
g_test_add_func ("/strfuncs/strdupv", test_strdupv);
|
||||
g_test_add_func ("/strfuncs/strerror", test_strerror);
|
||||
|
Loading…
Reference in New Issue
Block a user