diff --git a/compiler-rt/lib/scudo/standalone/local_cache.h b/compiler-rt/lib/scudo/standalone/local_cache.h
--- a/compiler-rt/lib/scudo/standalone/local_cache.h
+++ b/compiler-rt/lib/scudo/standalone/local_cache.h
@@ -122,8 +122,16 @@
   }
 
   TransferBatch *createBatch(uptr ClassId, void *B) {
-    if (ClassId != BatchClassId)
+    if (ClassId != BatchClassId) {
       B = allocate(BatchClassId);
+      const uptr BatchSize = SizeClassAllocator::getSizeByClassId(BatchClassId);
+      // We don't want to track allocated/free'd stats of transfer batches.
+      // Specializing or branching in allocate() based on `ClassId !=
+      // BatchClassId` ends up trashing the icache for a very hot function, and
+      // so we just undo the changes to the stats back here in the slow path.
+      Stats.sub(StatAllocated, BatchSize);
+      Stats.add(StatFree, BatchSize);
+    }
     return reinterpret_cast<TransferBatch *>(B);
   }
 
@@ -159,8 +167,16 @@
   }
 
   void destroyBatch(uptr ClassId, void *B) {
-    if (ClassId != BatchClassId)
+    if (ClassId != BatchClassId) {
       deallocate(BatchClassId, B);
+      const uptr BatchSize = SizeClassAllocator::getSizeByClassId(BatchClassId);
+      // We don't want to track allocated/free'd stats of transfer batches.
+      // Specializing or branching in deallocate() based on `ClassId !=
+      // BatchClassId` ends up trashing the icache for a very hot function, and
+      // so we just undo the changes to the stats back here in the slow path.
+      Stats.add(StatAllocated, BatchSize);
+      Stats.sub(StatFree, BatchSize);
+    }
   }
 
   NOINLINE bool refill(PerClass *C, uptr ClassId) {