Skip to content

Commit bccab6a

Browse files
committed
dm cache: switch the "default" cache replacement policy from mq to smq
The Stochastic multiqueue (SMQ) policy (vs MQ) offers the promise of less memory utilization, improved performance and increased adaptability in the face of changing workloads. SMQ also does not have any cumbersome tuning knobs. Users may switch from "mq" to "smq" simply by appropriately reloading a DM table that is using the cache target. Doing so will cause all of the mq policy's hints to be dropped. Also, performance of the cache may degrade slightly until smq recalculates the origin device's hotspots that should be cached. In the future the "mq" policy will just silently make use of "smq" and the mq code will be removed. Signed-off-by: Mike Snitzer <[email protected]> Acked-by: Joe Thornber <[email protected]>
1 parent 6096d91 commit bccab6a

File tree

3 files changed

+86
-32
lines changed

3 files changed

+86
-32
lines changed

Documentation/device-mapper/cache-policies.txt

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ trying to see when the io scheduler has let the ios run.
2525
Overview of supplied cache replacement policies
2626
===============================================
2727

28-
multiqueue
29-
----------
28+
multiqueue (mq)
29+
---------------
3030

31-
This policy is the default.
31+
This policy has been deprecated in favor of the smq policy (see below).
3232

3333
The multiqueue policy has three sets of 16 queues: one set for entries
3434
waiting for the cache and another two for those in the cache (a set for
@@ -73,6 +73,67 @@ If you're trying to quickly warm a new cache device you may wish to
7373
reduce these to encourage promotion. Remember to switch them back to
7474
their defaults after the cache fills though.
7575

76+
Stochastic multiqueue (smq)
77+
---------------------------
78+
79+
This policy is the default.
80+
81+
The stochastic multi-queue (smq) policy addresses some of the problems
82+
with the multiqueue (mq) policy.
83+
84+
The smq policy (vs mq) offers the promise of less memory utilization,
85+
improved performance and increased adaptability in the face of changing
86+
workloads. SMQ also does not have any cumbersome tuning knobs.
87+
88+
Users may switch from "mq" to "smq" simply by appropriately reloading a
89+
DM table that is using the cache target. Doing so will cause all of the
90+
mq policy's hints to be dropped. Also, performance of the cache may
91+
degrade slightly until smq recalculates the origin device's hotspots
92+
that should be cached.
93+
94+
Memory usage:
95+
The mq policy uses a lot of memory; 88 bytes per cache block on a 64
96+
bit machine.
97+
98+
SMQ uses 28bit indexes to implement it's data structures rather than
99+
pointers. It avoids storing an explicit hit count for each block. It
100+
has a 'hotspot' queue rather than a pre cache which uses a quarter of
101+
the entries (each hotspot block covers a larger area than a single
102+
cache block).
103+
104+
All these mean smq uses ~25bytes per cache block. Still a lot of
105+
memory, but a substantial improvement nontheless.
106+
107+
Level balancing:
108+
MQ places entries in different levels of the multiqueue structures
109+
based on their hit count (~ln(hit count)). This means the bottom
110+
levels generally have the most entries, and the top ones have very
111+
few. Having unbalanced levels like this reduces the efficacy of the
112+
multiqueue.
113+
114+
SMQ does not maintain a hit count, instead it swaps hit entries with
115+
the least recently used entry from the level above. The over all
116+
ordering being a side effect of this stochastic process. With this
117+
scheme we can decide how many entries occupy each multiqueue level,
118+
resulting in better promotion/demotion decisions.
119+
120+
Adaptability:
121+
The MQ policy maintains a hit count for each cache block. For a
122+
different block to get promoted to the cache it's hit count has to
123+
exceed the lowest currently in the cache. This means it can take a
124+
long time for the cache to adapt between varying IO patterns.
125+
Periodically degrading the hit counts could help with this, but I
126+
haven't found a nice general solution.
127+
128+
SMQ doesn't maintain hit counts, so a lot of this problem just goes
129+
away. In addition it tracks performance of the hotspot queue, which
130+
is used to decide which blocks to promote. If the hotspot queue is
131+
performing badly then it starts moving entries more quickly between
132+
levels. This lets it adapt to new IO patterns very quickly.
133+
134+
Performance:
135+
Testing SMQ shows substantially better performance than MQ.
136+
76137
cleaner
77138
-------
78139

drivers/md/dm-cache-policy-mq.c

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,21 +1431,12 @@ static struct dm_cache_policy *mq_create(dm_cblock_t cache_size,
14311431

14321432
static struct dm_cache_policy_type mq_policy_type = {
14331433
.name = "mq",
1434-
.version = {1, 3, 0},
1434+
.version = {1, 4, 0},
14351435
.hint_size = 4,
14361436
.owner = THIS_MODULE,
14371437
.create = mq_create
14381438
};
14391439

1440-
static struct dm_cache_policy_type default_policy_type = {
1441-
.name = "default",
1442-
.version = {1, 3, 0},
1443-
.hint_size = 4,
1444-
.owner = THIS_MODULE,
1445-
.create = mq_create,
1446-
.real = &mq_policy_type
1447-
};
1448-
14491440
static int __init mq_init(void)
14501441
{
14511442
int r;
@@ -1455,36 +1446,21 @@ static int __init mq_init(void)
14551446
__alignof__(struct entry),
14561447
0, NULL);
14571448
if (!mq_entry_cache)
1458-
goto bad;
1449+
return -ENOMEM;
14591450

14601451
r = dm_cache_policy_register(&mq_policy_type);
14611452
if (r) {
14621453
DMERR("register failed %d", r);
1463-
goto bad_register_mq;
1464-
}
1465-
1466-
r = dm_cache_policy_register(&default_policy_type);
1467-
if (!r) {
1468-
DMINFO("version %u.%u.%u loaded",
1469-
mq_policy_type.version[0],
1470-
mq_policy_type.version[1],
1471-
mq_policy_type.version[2]);
1472-
return 0;
1454+
kmem_cache_destroy(mq_entry_cache);
1455+
return -ENOMEM;
14731456
}
14741457

1475-
DMERR("register failed (as default) %d", r);
1476-
1477-
dm_cache_policy_unregister(&mq_policy_type);
1478-
bad_register_mq:
1479-
kmem_cache_destroy(mq_entry_cache);
1480-
bad:
1481-
return -ENOMEM;
1458+
return 0;
14821459
}
14831460

14841461
static void __exit mq_exit(void)
14851462
{
14861463
dm_cache_policy_unregister(&mq_policy_type);
1487-
dm_cache_policy_unregister(&default_policy_type);
14881464

14891465
kmem_cache_destroy(mq_entry_cache);
14901466
}

drivers/md/dm-cache-policy-smq.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,6 +1748,15 @@ static struct dm_cache_policy_type smq_policy_type = {
17481748
.create = smq_create
17491749
};
17501750

1751+
static struct dm_cache_policy_type default_policy_type = {
1752+
.name = "default",
1753+
.version = {1, 0, 0},
1754+
.hint_size = 4,
1755+
.owner = THIS_MODULE,
1756+
.create = smq_create,
1757+
.real = &smq_policy_type
1758+
};
1759+
17511760
static int __init smq_init(void)
17521761
{
17531762
int r;
@@ -1758,12 +1767,20 @@ static int __init smq_init(void)
17581767
return -ENOMEM;
17591768
}
17601769

1770+
r = dm_cache_policy_register(&default_policy_type);
1771+
if (r) {
1772+
DMERR("register failed (as default) %d", r);
1773+
dm_cache_policy_unregister(&smq_policy_type);
1774+
return -ENOMEM;
1775+
}
1776+
17611777
return 0;
17621778
}
17631779

17641780
static void __exit smq_exit(void)
17651781
{
17661782
dm_cache_policy_unregister(&smq_policy_type);
1783+
dm_cache_policy_unregister(&default_policy_type);
17671784
}
17681785

17691786
module_init(smq_init);

0 commit comments

Comments
 (0)