diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae0b7439d5fd251b7c2571b01dbc44ffe3a3a21b..b840983fd78d63a58164332f38bfbcdad0e8bf5e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,11 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## v10.1.1
+
+#### Improvements
+* [#2214](https://github.com/osmosis-labs/osmosis/pull/2214) Speedup epoch distribution, superfluid component
+
 ## v10.1.0
 
 #### Bug Fixes
@@ -90,8 +95,6 @@ This release contains minor CLI bug fixes.
 * [1698](https://github.com/osmosis-labs/osmosis/pull/1698) Register wasm snapshotter extension.
 * [1931](https://github.com/osmosis-labs/osmosis/pull/1931) Add explicit check for input denoms to `CalcJoinPoolShares`
 
-
-
 ## [v9.0.0 - Nitrogen](https://github.com/osmosis-labs/osmosis/releases/tag/v9.0.0)
 
 The Nitrogen release brings with it a number of features enabling further cosmwasm development work in Osmosis.
diff --git a/x/incentives/keeper/distribute.go b/x/incentives/keeper/distribute.go
index 57c96e9d74a4fcc390d9678cbf209965e29e7381..46b935b2adfa3b1f40aede9328ee821401c2a2b8 100644
--- a/x/incentives/keeper/distribute.go
+++ b/x/incentives/keeper/distribute.go
@@ -227,20 +227,37 @@ func (k Keeper) doDistributionSends(ctx sdk.Context, distrs *distributionInfo) e
 func (k Keeper) distributeSyntheticInternal(
 	ctx sdk.Context, gauge types.Gauge, locks []lockuptypes.PeriodLock, distrInfo *distributionInfo,
 ) (sdk.Coins, error) {
-	denom := gauge.DistributeTo.Denom
-
-	qualifiedLocks := make([]lockuptypes.PeriodLock, 0, len(locks))
+	qualifiedLocks := k.lk.GetLocksLongerThanDurationDenom(ctx, gauge.DistributeTo.Denom, gauge.DistributeTo.Duration)
+
+	// map from lockID to present index in resultant list
+	// to be state compatible with what we had before, we iterate over locks, to get qualified locks
+	// to be in the same order as what is present in locks.
+	// in a future release, we can just use qualified locks directly.
+	type lockIndexPair struct {
+		lock  lockuptypes.PeriodLock
+		index int
+	}
+	qualifiedLocksMap := make(map[uint64]lockIndexPair, len(qualifiedLocks))
+	for _, lock := range qualifiedLocks {
+		qualifiedLocksMap[lock.ID] = lockIndexPair{lock, -1}
+	}
+	curIndex := 0
 	for _, lock := range locks {
-		// See if this lock has a synthetic lockup. If so, err == nil, and we add to qualifiedLocks
-		// otherwise it does not, and we continue.
-		_, err := k.lk.GetSyntheticLockup(ctx, lock.ID, denom)
-		if err != nil {
+		if v, ok := qualifiedLocksMap[lock.ID]; ok {
+			qualifiedLocksMap[lock.ID] = lockIndexPair{v.lock, curIndex}
+			curIndex += 1
+		}
+	}
+
+	sortedAndTrimmedQualifiedLocks := make([]lockuptypes.PeriodLock, curIndex)
+	for _, v := range qualifiedLocksMap {
+		if v.index < 0 {
 			continue
 		}
-		qualifiedLocks = append(qualifiedLocks, lock)
+		sortedAndTrimmedQualifiedLocks[v.index] = v.lock
 	}
 
-	return k.distributeInternal(ctx, gauge, qualifiedLocks, distrInfo)
+	return k.distributeInternal(ctx, gauge, sortedAndTrimmedQualifiedLocks, distrInfo)
 }
 
 // distributeInternal runs the distribution logic for a gauge, and adds the sends to