]> git.nega.tv - josh/narcissus/commitdiff
narcissus-gpu: Document block splitting behavior
authorJoshua Simmons <josh@nega.tv>
Mon, 29 May 2023 18:09:24 +0000 (20:09 +0200)
committerJoshua Simmons <josh@nega.tv>
Mon, 29 May 2023 18:09:24 +0000 (20:09 +0200)
A relatively subtle part of the TLSF paper is the choice of size when
splitting blocks as part of an allocation. Add a short comment and test.

libs/narcissus-gpu/src/tlsf.rs

index 6508150e6d26f6d53004551737640e112796899e..889939e2e798db6608759e0e62ac62daae2d3d38 100644 (file)
@@ -554,6 +554,16 @@ where
 
         self.extract_block(block_index);
 
+        // It's important to use the rounded size here, not the requested size. This
+        // avoids a failure case where freeing an allocation of a given size, fails
+        // to leave the allocator in a state where that block can be re-used for
+        // another allocation of the same size as the returned block can be placed into
+        // a smaller bin.
+        //
+        // We're trading arbitrary wasted blocks in this workload, for a small bounded
+        // amount of fragmentation.
+        //
+        // Tested in `tests::split_policy_avoids_memory_waste`
         let remainder = self.blocks[block_index].size - rounded_size;
         let super_block_index = self.blocks[block_index].super_block_index;
 
@@ -712,6 +722,25 @@ mod tests {
         }
     }
 
+    #[test]
+    fn split_policy_avoids_memory_waste() {
+        let mut tlsf = Tlsf::new();
+        tlsf.insert_super_block(1024, ());
+
+        let large_size = 990;
+        let small_size = 30;
+
+        // Make a large allocation that splits the block.
+        let large = tlsf.alloc(large_size, 1).unwrap();
+        // Make a small allocation to inhibit merging upon free.
+        tlsf.alloc(small_size, 1).unwrap();
+        // Free the large block, if all goes well this will be added to a bin which is
+        // large enough to service another allocation of the same size.
+        tlsf.free(large);
+        // Allocate another large block, if this fails we've "lost" memory.
+        tlsf.alloc(large_size, 1).unwrap();
+    }
+
     #[test]
     #[should_panic]
     fn double_free() {