4 deleted 2 characters in body
source | link

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {...0}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_calloc(&values_mem, n, sizeof(int));

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I often getI've gotten reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply. When you can get something down from 3 seconds to 1.8 seconds by just introducing a line of code and changing two, it's a pretty good bang for such a small buck.

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {...}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_calloc(&values_mem, n, sizeof(int));

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I often get reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply. When you can get something down from 3 seconds to 1.8 seconds by just introducing a line of code and changing two, it's a pretty good bang for such a small buck.

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {0}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_calloc(&values_mem, n, sizeof(int));

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I've gotten reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply. When you can get something down from 3 seconds to 1.8 seconds by just introducing a line of code and changing two, it's a pretty good bang for such a small buck.

3 added 16 characters in body
source | link

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {...}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_mallocmf_calloc(&values_mem, n, sizeof(int));

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I often get reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply. When you can get something down from 3 seconds to 1.8 seconds by just introducing a line of code and changing two, it's a pretty good bang for such a small buck.

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {...}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_malloc(&values_mem);

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I often get reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply. When you can get something down from 3 seconds to 1.8 seconds by just introducing a line of code and changing two, it's a pretty good bang for such a small buck.

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {...}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_calloc(&values_mem, n, sizeof(int));

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I often get reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply. When you can get something down from 3 seconds to 1.8 seconds by just introducing a line of code and changing two, it's a pretty good bang for such a small buck.

2 added 162 characters in body
source | link

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {...}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_malloc(&values_mem);

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I often get reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply. When you can get something down from 3 seconds to 1.8 seconds by just introducing a line of code and changing two, it's a pretty good bang for such a small buck.

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {...}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_malloc(&values_mem);

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I often get reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply.

Simplest one to me is using the stack when possible whenever a common case use pattern fits a range of, say, [0, 64) but has rare cases that have no small upper bound.

Simple C example (before):

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int* values = calloc(n, sizeof(int));

    // do stuff with values
    ...
    free(values);
}

And after:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    int values_mem[64] = {...}
    int* values = (n <= 64) ? values_mem: calloc(n, sizeof(int));

    // do stuff with values
    ...
    if (values != values_mem)
        free(values);
}

I've generalized this like so since these kinds of hotspots crop up a lot in profiling:

void some_hotspot_called_in_big_loops(int n, ...)
{
    // 'n' is, 99% of the time, <= 64.
    MemFast values_mem;
    int* values = mf_malloc(&values_mem);

    // do stuff with values
    ...

    mf_free(&values_mem);
}

The above uses the stack when the data being allocated is small enough in those 99.9% cases, and uses the heap otherwise.

In C++ I've generalized this with a standard-compliant small sequence (similar to SmallVector implementations out there) which revolves around the same concept.

It's not an epic optimization (I often get reductions from, say, 3 seconds for an operation to complete down to 1.8 seconds), but it requires such trivial effort to apply. When you can get something down from 3 seconds to 1.8 seconds by just introducing a line of code and changing two, it's a pretty good bang for such a small buck.

1
source | link