Skip to content

py: Prevent many extra vstr allocations - #1271

Closed
dhylands wants to merge 1 commit into
micropython:masterfrom
dhylands:vstr-optimize-alloc
Closed

dhylands wants to merge 1 commit into
micropython:masterfrom
dhylands:vstr-optimize-alloc

Conversation

@dhylands

Copy link
Copy Markdown
Contributor

I checked the entire codebase, and every place that vstr_init_len
was called, there was a call to mp_obj_new_str_from_vstr after it.

mp_obj_new_str_from_vstr always tries to reallocate a new buffer
1 byte larger than the original to store the terminating null
character.

In many cases, if we allocated the initial buffer to be 1 byte
longer, we can prevent this extra allocation, and just reuse
the originally allocated buffer.

Asking to read 256 bytes and only getting 100 will still cause
the extra allocation, but if you ask to read 256 and get 256
then the extra allocation will be optimized away.

Yes - the reallocation is optimized in the heap to try and reuse
the buffer if it can, but it takes quite a few cycles to figure
this out.

I checked the entire codebase, and every place that vstr_init_len
was called, there was a call to mp_obj_new_str_from_vstr after it.

mp_obj_new_str_from_vstr always tries to reallocate a new buffer
1 byte larger than the original to store the terminating null
character.

In many cases, if we allocated the initial buffer to be 1 byte
longer, we can prevent this extra allocation, and just reuse
the originally allocated buffer.

Asking to read 256 bytes and only getting 100 will still cause
the extra allocation, but if you ask to read 256 and get 256
then the extra allocation will be optimized away.

Yes - the reallocation is optimized in the heap to try and reuse
the buffer if it can, but it takes quite a few cycles to figure
this out.
@danicampora

Copy link
Copy Markdown
Member

Nice, looks good to me and makes sense.

@coveralls

Copy link
Copy Markdown

Coverage Status

Coverage decreased (-0.0%) to 93.81% when pulling dc964de on dhylands:vstr-optimize-alloc into 97ce883 on micropython:master.

@pfalcon

pfalcon commented May 19, 2015

Copy link
Copy Markdown
Contributor

I checked the entire codebase, and every place that vstr_init_len was called, there was a call to mp_obj_new_str_from_vstr after it.

The problem with this is that vstr is a generic buffer object. It's nice that in every place you saw, vstr_init_len is followed by mp_obj_new_str_from_vstr. But it doesn't preclude some other code to request vstr_init_len(256) and expect that it will take exactly 256 bytes, and not for example 256+16, factory memory manager overhead. I proposed a solution to that: we split current vstr into vstr for strings and vbuf for generic buffers. But @dpgeorge argued of bloat and scope creep (and I can make an argument that it's paradigmatically right, not that it's needed).

All in all, I'm +0.5 on this, but @dpgeorge should look into this as he last refactored vstr code (and before that, I did guerrilla patches to it to factor in various adhoc improvements, like this one, which he eventually had to refactor/clean up).

@dhylands

Copy link
Copy Markdown
Contributor Author

If you really don't want the extra byte, you can just call vstr_init instead of vstr_init_len.

I was originally going to introduce a new API, which I called: vstr_init_alloc_plus_1, but by the time I was done, I had changed every single instance of vstr_init_len to vstr_init_alloc_plus_1, so I left out the rename of the new API.

I think that this should actually be a performance improvement, but I couldn't figure out what performance tests we run, and when I tried run-bench-tests it failed.

@dpgeorge

dpgeorge commented Jul 6, 2015

Copy link
Copy Markdown
Member

Merged in 9f76dcd with a change to the comment above vstr_init_len.

The vstr_t type now (after previous patches here and there) behaves pretty much like a vbuf_t, and it has 2 convenience functions for actual string (ie null terminated buffer) behaviour:

  • vstr_init_len -- used to allocate a buffer that will eventually hold a null-terminated string
  • vstr_null_terminated_str -- used to null terminate the buffer and return a pointer to the start

So that's how to think about and use the vstr_t type (primarily as a buffer with 2 str convenience functions). We could rename vstr to vbuf but that would be 630 lines changed. We could change vstr_init_len to something more appropriate, like vstr_init_str_len.

If you want to init a normal buffer use vstr_init, not vstr_init_len.

@dpgeorge dpgeorge closed this Jul 6, 2015
@dhylands
dhylands deleted the vstr-optimize-alloc branch November 8, 2015 02:43
tannewt added a commit to tannewt/circuitpython that referenced this pull request Oct 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants