Skip to content

Optimize Allocator.allocAdvancedBla to create less duplicate code for similar types. - #16332

Merged
andrewrk merged 3 commits into
ziglang:masterfrom
IntegratedQuantum:allocator_code_size_optimization
Jul 6, 2023
Merged

Optimize Allocator.allocAdvancedBla to create less duplicate code for similar types.#16332
andrewrk merged 3 commits into
ziglang:masterfrom
IntegratedQuantum:allocator_code_size_optimization

Conversation

@IntegratedQuantum

@IntegratedQuantum IntegratedQuantum commented Jul 5, 2023

Copy link
Copy Markdown
Contributor

Motivation

Take a look at the following code:

_ = allocator.create(u32) catch return;
_ = allocator.create(i32) catch return;

This is allocating two pieces of memory with the exact same size and alignment. Yet under the hood this generates two different functions, increasing the binary size and compile time. And now in the compiler there is 102 of these plus 125 calls to alloc.
Overall 800 kB of zig's (debug) binary are wasted for allocator functions. That's ~2% of the total size (measuring only zig functions).

In my own project it's even worse. Almost 10% of the (debug) binary (excluding C functions) is filled with allocator functions.

Solution

Essentially I put all the common functionality from allocAdvancedWithRetAddr into a new function that is only generic over size and alignment:

fn allocWithSizeAndAlignment(self: Allocator, comptime size: usize, comptime alignment: u29, n: usize, return_address: usize)

This reduces the amount of code that is duplicated when two types with same size+align are allocated.

Results

The zig debug binary size was reduced by 503 kB.
In my own project the debug binary was reduced by 175 kB (~4-5% of the zig functions)
In my project the compile time decreased by ~0.4 seconds (~3%) (Note that this comes purely from the fact that there is less duplicate code to compile)

Best case improvement

// time zig build-lib test.zig
const std = @import("std");

export fn allocThemAll(allocator: *std.mem.Allocator) void {
	inline for(1..200) |i| {
		_ = allocator.alloc(std.meta.Int(.signed, i), 1) catch return;
		_ = allocator.create(std.meta.Int(.signed, i)) catch return;
		_ = allocator.alloc(std.meta.Int(.unsigned, i), 1) catch return;
		_ = allocator.create(std.meta.Int(.unsigned, i)) catch return;
	}
}

The compile-time of this best-case example was reduced from 2.3 s to 1.3 s and the size of the binary decreased from 1 MB to 550 kB.

… type into a function that only depends on comptime size and alignment.

This reduces comptime code duplication because e.g. `alloc(u32, )` and `alloc(i32, )` now use the same function `allocWithBla(4, 4, )` under the hood.
@Hejsil

Hejsil commented Jul 6, 2023

Copy link
Copy Markdown
Contributor

Wouldn't it also be possible to remove the generic size argument? Then the signature just becomes:

fn allocBytesWithAlignment(self: Allocator, comptime alignment: u29, n: usize, return_address: usize)

@IntegratedQuantum

Copy link
Copy Markdown
Contributor Author

Wouldn't it also be possible to remove the generic size argument?

Then I would need to do the math.mul(usize, size, n) catch ... outside which would either mean making an additional function or duplicating that piece of code in every instantiation of alloc.

@Hejsil

Hejsil commented Jul 6, 2023

Copy link
Copy Markdown
Contributor

Then I would need to do the math.mul(usize, size, n) catch ... outside which would either mean making an additional function or duplicating that piece of code in every instantiation of alloc.

Right, make sense. One could have allocWithSizeAndAlignment then wrap allocBytesWithAlignment, but that would only make sense if there are many calls to allocWithSizeAndAlignment, where alignment is the same but size is different.

@IntegratedQuantum

Copy link
Copy Markdown
Contributor Author

That would actually save an additional 45 kB.

…ignment.

Also optimized `create` to directly call the new function.
@IntegratedQuantum

Copy link
Copy Markdown
Contributor Author

I also decided to make create call allocBytesWithAlignment directly, saving another 40 kB.

@andrewrk
andrewrk merged commit 49ac816 into ziglang:master Jul 6, 2023
@andrewrk

andrewrk commented Jul 6, 2023

Copy link
Copy Markdown
Member

Nice work.

@IntegratedQuantum
IntegratedQuantum deleted the allocator_code_size_optimization branch July 6, 2023 19:25
@andrewrk

andrewrk commented Jul 6, 2023

Copy link
Copy Markdown
Member

Perf data point for building the self-hosted compiler:

Benchmark 1 (3 runs): before/bin/zig build-exe ...
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          59.1s  ± 1.50s     57.9s  … 60.8s           0 ( 0%)        0%
  peak_rss           3.49GB ±  550KB    3.49GB … 3.49GB          0 ( 0%)        0%
  cpu_cycles          232G  ±  705M      231G  …  232G           0 ( 0%)        0%
  instructions        332G  ± 32.8M      332G  …  332G           0 ( 0%)        0%
  cache_references   13.1G  ± 39.6M     13.1G  … 13.2G           0 ( 0%)        0%
  cache_misses       1.27G  ± 20.5M     1.25G  … 1.29G           0 ( 0%)        0%
  branch_misses      1.60G  ± 3.56M     1.59G  … 1.60G           0 ( 0%)        0%
Benchmark 2 (3 runs): after/bin/zig build-exe ...
  measurement          mean ± σ            min … max           outliers         delta
  wall_time          59.7s  ±  648ms    59.1s  … 60.4s           0 ( 0%)          +  1.0% ±  4.4%
  peak_rss           3.46GB ±  287KB    3.46GB … 3.46GB          0 ( 0%)          -  0.9% ±  0.0%
  cpu_cycles          230G  ± 1.17G      229G  …  231G           0 ( 0%)          -  0.9% ±  0.9%
  instructions        329G  ±  188M      329G  …  329G           0 ( 0%)          -  0.8% ±  0.1%
  cache_references   13.1G  ± 29.8M     13.1G  … 13.1G           0 ( 0%)          -  0.0% ±  0.6%
  cache_misses       1.19G  ±  823K     1.19G  … 1.20G           0 ( 0%)        ⚡-  5.7% ±  2.6%
  branch_misses      1.58G  ± 1.75M     1.58G  … 1.58G           0 ( 0%)          -  1.2% ±  0.4%

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.

3 participants