Whether reallocating a small block keeps it in place was decided by
old_bin_num > 0 && size <= bin_data_size[old_bin_num - 1], which needs
a test of the bin number, an index computation and a compare on every
in-place reallocation.
Store the smallest size a block of each bin keeps in a table,
bin_keep_size[], which is one more than the size of the bin below, and 0
for the first bin, so the check becomes a single load and compare. The
behavior is unchanged.
As each entry comes from the row of the bin below, the table has one
entry more than there are bins, and the last one is unused. This does
not grow the binary: the table is aligned to at least 16 bytes, so 30
entries (120 bytes) would be padded to 128 bytes anyway, and the extra
4 bytes fit into that padding.
Mark the truncation as unexpected: a block reallocated to a smaller size
usually stays in its bin. Without the hint, GCC merges the epilogue of
the in-place return with another return path, which adds a branch to
every in-place reallocation of a large run.
Whether reallocating a small block keeps it in place was decided by old_bin_num > 0 && size <= bin_data_size[old_bin_num - 1], which needs a test of the bin number, an index computation and a compare on every in-place reallocation.
Store the smallest size a block of each bin keeps in a table, bin_keep_size[], which is one more than the size of the bin below, and 0 for the first bin, so the check becomes a single load and compare. The behavior is unchanged.
As each entry comes from the row of the bin below, the table has one entry more than there are bins, and the last one is unused. This does not grow the binary: the table is aligned to at least 16 bytes, so 30 entries (120 bytes) would be padded to 128 bytes anyway, and the extra 4 bytes fit into that padding.
Mark the truncation as unexpected: a block reallocated to a smaller size usually stays in its bin. Without the hint, GCC merges the epilogue of the in-place return with another return path, which adds a branch to every in-place reallocation of a large run.
Instructions per request (callgrind) vs. master (first run).
in parentheses master vs. master as a run-to-run control.