Reduce default inline storage in absl::FixedArray from 256 to 48 bytes.

With a stateless allocator and without AddressSanitizer redzones, 48 bytes of inline element storage plus the size and data pointer keep a default absl::FixedArray within one 64-byte cache line for every element type. Element types larger than 48 bytes select zero inline elements and occupy 16 bytes. A 256-byte default inline buffer wastes memory inside heap-allocated objects when member arrays hold only a few elements or allocate out of line anyway.

PiperOrigin-RevId: 1007192305
diff --git a/absl/container/fixed_array.h b/absl/container/fixed_array.h
index 713c685..867ba37 100644
--- a/absl/container/fixed_array.h
+++ b/absl/container/fixed_array.h
@@ -82,7 +82,11 @@
   static_assert(!std::is_array_v<T> || std::extent_v<T> > 0,
                 "Arrays with unknown bounds cannot be used with FixedArray.");
 
-  static constexpr size_t kInlineBytesDefault = 256;
+  // 48 bytes of inline elements plus the size and data pointer keep a default
+  // FixedArray with a stateless allocator within one 64-byte cache line.
+  // Larger default buffers waste memory in heap-resident objects that hold few
+  // elements or allocate out of line.
+  static constexpr size_t kInlineBytesDefault = 48;
 
   using AllocatorTraits = std::allocator_traits<A>;
   template <typename Iterator>
diff --git a/absl/container/fixed_array_test.cc b/absl/container/fixed_array_test.cc
index e82919e..ba16d81 100644
--- a/absl/container/fixed_array_test.cc
+++ b/absl/container/fixed_array_test.cc
@@ -16,6 +16,7 @@
 
 #include <stdio.h>
 
+#include <array>
 #include <cassert>
 #include <cstdint>
 #include <cstring>
@@ -523,6 +524,28 @@
       "default-sized absl::FixedArray should have same alignment as Data.");
 }
 
+#ifndef ABSL_HAVE_ADDRESS_SANITIZER
+TEST(FixedArrayTest, DefaultInlinedSpaceFitsInCacheLine) {
+  struct alignas(32) Aligned32 {
+    char c[32];
+  };
+  struct alignas(64) Aligned64 {
+    char c[64];
+  };
+
+  constexpr size_t kHeaderSize = sizeof(void*) + sizeof(size_t);
+  EXPECT_EQ(sizeof(absl::FixedArray<char>), 48 + kHeaderSize);
+  EXPECT_EQ(sizeof(absl::FixedArray<int>), 48 + kHeaderSize);
+  EXPECT_EQ(sizeof(absl::FixedArray<void*>), 48 + kHeaderSize);
+  EXPECT_EQ(sizeof(absl::FixedArray<ThreeInts>), 48 + kHeaderSize);
+  EXPECT_EQ(sizeof(absl::FixedArray<std::array<char, 24>>), 48 + kHeaderSize);
+  EXPECT_EQ(sizeof(absl::FixedArray<std::array<char, 40>>), 40 + kHeaderSize);
+  EXPECT_EQ(sizeof(absl::FixedArray<std::array<char, 48>>), 48 + kHeaderSize);
+  EXPECT_EQ(sizeof(absl::FixedArray<Aligned32>), size_t{64});
+  EXPECT_EQ(sizeof(absl::FixedArray<Aligned64>), kHeaderSize);
+}
+#endif  // ABSL_HAVE_ADDRESS_SANITIZER
+
 // PickyDelete EXPECTs its class-scope deallocation funcs are unused.
 struct PickyDelete {
   PickyDelete() {}
@@ -840,19 +863,19 @@
 }
 
 TEST(FixedArrayTest, AddressSanitizerAnnotations4) {
-  absl::FixedArray<ThreeInts> a(10);
+  absl::FixedArray<ThreeInts> a(2);
   ThreeInts* raw = a.data();
   raw[0] = ThreeInts();
-  raw[9] = ThreeInts();
+  raw[1] = ThreeInts();
   // Note: raw[-1] is pointing to 12 bytes before the container range. However,
   // there is only a 8-byte red zone before the container range, so we only
   // access the last 4 bytes of the struct to make sure it stays within the red
   // zone.
   EXPECT_DEATH_IF_SUPPORTED(raw[-1].z_ = 0, "container-overflow");
-  EXPECT_DEATH_IF_SUPPORTED(raw[10] = ThreeInts(), "container-overflow");
-  // The actual size of storage is kDefaultBytes=256, 21*12 = 252,
-  // so reading raw[21] should still trigger the correct warning.
-  EXPECT_DEATH_IF_SUPPORTED(raw[21] = ThreeInts(), "container-overflow");
+  EXPECT_DEATH_IF_SUPPORTED(raw[2] = ThreeInts(), "container-overflow");
+  // The actual size of storage is kInlineBytesDefault=48, 4*12 = 48,
+  // so reading raw[4] should still trigger the correct warning.
+  EXPECT_DEATH_IF_SUPPORTED(raw[4] = ThreeInts(), "container-overflow");
 }
 #endif  // ABSL_HAVE_ADDRESS_SANITIZER