Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Optimize string repetition and string copying #2634

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

Merged
merged 6 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions integration_tests/test_str_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def test_str_repeat():
assert a*2*3 == "XyzXyzXyzXyzXyzXyz"
assert 3*a*3 == "XyzXyzXyzXyzXyzXyzXyzXyzXyz"
assert a*-1 == ""
assert len(a*(10**6)) == (3 * 10 ** 6)

def test_str_join():
a: str
Expand Down
34 changes: 22 additions & 12 deletions src/libasr/runtime/lfortran_intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -1976,12 +1976,14 @@ LFORTRAN_API void _lfortran_strcpy(char** x, char *y, int8_t free_target)
*x = (char*) malloc((strlen(y) + 1) * sizeof(char));
_lfortran_string_init(strlen(y) + 1, *x);
// }
for (size_t i = 0; i < strlen(*x); i++) {
if (i < strlen(y)) {
x[0][i] = y[i];
} else {
x[0][i] = ' ';
}
size_t y_len = strlen(y);
size_t x_len = strlen(*x);
size_t i = 0;
for (; i < x_len && i < y_len; i++) {
x[0][i] = y[i];
}
for (; i < x_len; i++) {
x[0][i] = ' ';
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a great improvement. Thanks!

}
}

Expand Down Expand Up @@ -2129,21 +2131,29 @@ LFORTRAN_API int32_t _lpython_bit_length8(int64_t num)
//repeat str for n time
LFORTRAN_API void _lfortran_strrepeat(char** s, int32_t n, char** dest)
{
int cntr = 0;
char trmn = '\0';
int s_len = strlen(*s);
int trmn_size = sizeof(trmn);
int f_len = s_len*n;
if (f_len < 0)
f_len = 0;
char* dest_char = (char*)malloc(f_len+trmn_size);
for (int i = 0; i < n; i++) {
for (int j = 0; j < s_len; j++) {
dest_char[cntr] = (*s)[j];
cntr++;

if (s_len == 1) {
memset(dest_char, *(*s), n);
} else {
Comment on lines +2142 to +2144
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder why we need to handle the case for s_len == 1 separately. The else portion seems general, so I think that should be enough to handle the case of s_len == 1.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Does the else portion fail when s_len == 1?

Copy link
Collaborator Author

@advikkabra advikkabra Mar 29, 2024

Choose a reason for hiding this comment

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

It does not fail when s_len == 1, but memset is very optimized for this usecase. Setting a large block of data to a single value is what memset excels in, and it is optimized for every architecture. While testing, these are the compared times:

100000000
Binary exponentiation: 0.086963
memset: 0.006341
400000000
Binary exponentiation: 0.211737
memset: 0.018612

This is after compiling with -O3

memcpy(dest_char, *s, s_len);
int chars_copied = s_len;
int copy_length;
while (chars_copied < f_len) {
copy_length = (chars_copied <= f_len-chars_copied)
? chars_copied : f_len-chars_copied;
memcpy(dest_char+chars_copied, dest_char, copy_length);
chars_copied += copy_length;
}
}
dest_char[cntr] = trmn;

dest_char[f_len] = trmn;
*dest = &(dest_char[0]);
}

Expand Down