/src/libjxl/lib/jxl/memory_manager_internal.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) the JPEG XL Project Authors. All rights reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style |
4 | | // license that can be found in the LICENSE file. |
5 | | |
6 | | #include "lib/jxl/memory_manager_internal.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <algorithm> |
11 | | #include <atomic> |
12 | | #include <cstddef> |
13 | | #include <cstdio> |
14 | | #include <cstdlib> |
15 | | #include <cstring> // memcpy |
16 | | #include <hwy/base.h> // kMaxVectorSize |
17 | | |
18 | | #include "lib/jxl/base/common.h" |
19 | | #include "lib/jxl/base/status.h" |
20 | | #include "lib/jxl/simd_util.h" |
21 | | |
22 | | namespace jxl { |
23 | | |
24 | | namespace { |
25 | | |
26 | 0 | void* MemoryManagerDefaultAlloc(void* opaque, size_t size) { |
27 | 0 | return malloc(size); |
28 | 0 | } |
29 | | |
30 | 0 | void MemoryManagerDefaultFree(void* opaque, void* address) { free(address); } |
31 | | |
32 | | } // namespace |
33 | | |
34 | 14.5k | void* MemoryManagerAlloc(const JxlMemoryManager* memory_manager, size_t size) { |
35 | 14.5k | return memory_manager->alloc(memory_manager->opaque, size); |
36 | 14.5k | } |
37 | | |
38 | 14.5k | void MemoryManagerFree(const JxlMemoryManager* memory_manager, void* address) { |
39 | 14.5k | memory_manager->free(memory_manager->opaque, address); |
40 | 14.5k | } |
41 | | |
42 | | Status MemoryManagerInit(JxlMemoryManager* self, |
43 | 14.5k | const JxlMemoryManager* memory_manager) { |
44 | 14.5k | if (memory_manager) { |
45 | 14.5k | *self = *memory_manager; |
46 | 14.5k | } else { |
47 | 0 | memset(self, 0, sizeof(*self)); |
48 | 0 | } |
49 | 14.5k | bool is_default_alloc = (self->alloc == nullptr); |
50 | 14.5k | bool is_default_free = (self->free == nullptr); |
51 | 14.5k | if (is_default_alloc != is_default_free) { |
52 | 0 | return false; |
53 | 0 | } |
54 | 14.5k | if (is_default_alloc) self->alloc = jxl::MemoryManagerDefaultAlloc; |
55 | 14.5k | if (is_default_free) self->free = jxl::MemoryManagerDefaultFree; |
56 | | |
57 | 14.5k | return true; |
58 | 14.5k | } |
59 | | |
60 | 6.03M | size_t BytesPerRow(const size_t xsize, const size_t sizeof_t) { |
61 | | // Special case: we don't allow any ops -> don't need extra padding/ |
62 | 6.03M | if (xsize == 0) { |
63 | 95.0k | return 0; |
64 | 95.0k | } |
65 | | |
66 | 5.94M | const size_t vec_size = MaxVectorSize(); |
67 | 5.94M | size_t valid_bytes = xsize * sizeof_t; |
68 | | |
69 | | // Allow unaligned accesses starting at the last valid value. |
70 | | // Skip for the scalar case because no extra lanes will be loaded. |
71 | 5.94M | if (vec_size != 0) { |
72 | 5.94M | valid_bytes += vec_size - sizeof_t; |
73 | 5.94M | } |
74 | | |
75 | | // Round up to vector and cache line size. |
76 | 5.94M | const size_t align = std::max(vec_size, memory_manager_internal::kAlignment); |
77 | 5.94M | size_t bytes_per_row = RoundUpTo(valid_bytes, align); |
78 | | |
79 | | // During the lengthy window before writes are committed to memory, CPUs |
80 | | // guard against read after write hazards by checking the address, but |
81 | | // only the lower 11 bits. We avoid a false dependency between writes to |
82 | | // consecutive rows by ensuring their sizes are not multiples of 2 KiB. |
83 | | // Avoid2K prevents the same problem for the planes of an Image3. |
84 | 5.94M | if (bytes_per_row % memory_manager_internal::kAlias == 0) { |
85 | 70.7k | bytes_per_row += align; |
86 | 70.7k | } |
87 | | |
88 | 5.94M | JXL_DASSERT(bytes_per_row % align == 0); |
89 | 5.94M | return bytes_per_row; |
90 | 6.03M | } |
91 | | |
92 | | StatusOr<AlignedMemory> AlignedMemory::Create(JxlMemoryManager* memory_manager, |
93 | 5.94M | size_t size, size_t pre_padding) { |
94 | 5.94M | JXL_ENSURE(pre_padding <= memory_manager_internal::kAlias); |
95 | 5.94M | size_t allocation_size = size + pre_padding + memory_manager_internal::kAlias; |
96 | 5.94M | if (size > allocation_size) { |
97 | 0 | return JXL_FAILURE("Requested allocation is too large"); |
98 | 0 | } |
99 | 5.94M | JXL_ENSURE(memory_manager); |
100 | 5.94M | void* allocated = |
101 | 5.94M | memory_manager->alloc(memory_manager->opaque, allocation_size); |
102 | 5.94M | if (allocated == nullptr) { |
103 | 8 | return JXL_FAILURE("Allocation failed"); |
104 | 8 | } |
105 | 5.94M | return AlignedMemory(memory_manager, allocated, pre_padding); |
106 | 5.94M | } |
107 | | |
108 | | AlignedMemory::AlignedMemory(JxlMemoryManager* memory_manager, void* allocation, |
109 | | size_t pre_padding) |
110 | 5.94M | : allocation_(allocation), memory_manager_(memory_manager) { |
111 | | // Congruence to `offset` (mod kAlias) reduces cache conflicts and load/store |
112 | | // stalls, especially with large allocations that would otherwise have similar |
113 | | // alignments. |
114 | 5.94M | static std::atomic<uint32_t> next_group{0}; |
115 | 5.94M | size_t group = |
116 | 5.94M | static_cast<size_t>(next_group.fetch_add(1, std::memory_order_relaxed)); |
117 | 5.94M | group &= (memory_manager_internal::kNumAlignmentGroups - 1); |
118 | 5.94M | size_t offset = memory_manager_internal::kAlignment * group; |
119 | | |
120 | | // Actual allocation. |
121 | 5.94M | uintptr_t address = reinterpret_cast<uintptr_t>(allocation) + pre_padding; |
122 | | |
123 | | // Aligned address, but might land before allocation (50%/50%) or not have |
124 | | // enough pre-padding. |
125 | 5.94M | uintptr_t aligned_address = |
126 | 5.94M | (address & ~(memory_manager_internal::kAlias - 1)) + offset; |
127 | 5.94M | if (aligned_address < address) |
128 | 3.04M | aligned_address += memory_manager_internal::kAlias; |
129 | | |
130 | 5.94M | address_ = reinterpret_cast<void*>(aligned_address); // NOLINT |
131 | 5.94M | } |
132 | | |
133 | 25.6M | AlignedMemory::AlignedMemory(AlignedMemory&& other) noexcept { |
134 | 25.6M | allocation_ = other.allocation_; |
135 | 25.6M | memory_manager_ = other.memory_manager_; |
136 | 25.6M | address_ = other.address_; |
137 | 25.6M | other.memory_manager_ = nullptr; |
138 | 25.6M | } |
139 | | |
140 | 9.18M | AlignedMemory& AlignedMemory::operator=(AlignedMemory&& other) noexcept { |
141 | 9.18M | if (this == &other) return *this; |
142 | 9.18M | if (memory_manager_ && allocation_) { |
143 | 626k | memory_manager_->free(memory_manager_->opaque, allocation_); |
144 | 626k | } |
145 | 9.18M | allocation_ = other.allocation_; |
146 | 9.18M | memory_manager_ = other.memory_manager_; |
147 | 9.18M | address_ = other.address_; |
148 | 9.18M | other.memory_manager_ = nullptr; |
149 | 9.18M | return *this; |
150 | 9.18M | } |
151 | | |
152 | 41.1M | AlignedMemory::~AlignedMemory() { |
153 | 41.1M | if (memory_manager_ == nullptr) return; |
154 | 5.31M | memory_manager_->free(memory_manager_->opaque, allocation_); |
155 | 5.31M | } |
156 | | |
157 | | } // namespace jxl |