上一篇加上测试会很长的篇幅,单独另写一篇
原子读写锁的主要逻辑和相关的锁管理器均已实现完成,这里编写一个测试用例和标准库中的 shared_mutex
进行对比
实现
统一封装
为了便于测试,这里对 AtomicSharedMutex
和 shared_mutex
进行统一封装:
class STDRWLock { |
实现基础测试用例
实现一个模版类用于测试不同的锁:
template <class Lock> |
实现单元测试
实现基础的单元测试,同时针对标准库与原子实现对应的测试函数,这里的结果以元组的形式进行输出便于后续使用脚本语言进行分析:
/* |
测试
由于手边只有一种类型的 Arm 设备,虽然是 A58 但都是 ArmV7 的版本,这里暂时只能使用这两种进行对比
测试设备:
- Intel(R) Core(TM) i5-7300HQ 4核
- A58-ArmV7 4核
编译选项
两者均以 -O2
等级进行优化生成
读写比例
测试的线程读写线程比例为 $[2, 100] : 1$ ,随着读者线程数的上升统计读写线程进行完整操作消耗时间的平均值
图表颜色说明
- 蓝线为标准库实现
- 红线为原子实现
X86
X86 下的测试结果
--- config: xyChart: width: 900 height: 600 themeVariables: xyChart: plotColorPalette: "#28678D,#f34544" --- xychart-beta title "读者操作" x-axis "读者线程数" 2 --> 100 y-axis "操作消耗平均时间(ms)" line [1113, 2398, 2298, 1355, 1515, 1775, 1593, 1711, 585, 1179, 1684, 1778, 1779, 1777, 1635, 1880, 1562, 1563, 1386, 1648, 1639, 1762, 1759, 1682, 1810, 1572, 1535, 1020, 1804, 1469, 1710, 1595, 1857, 1713, 1778, 1840, 1602, 1712, 1678, 1623, 1899, 1409, 1624, 1631, 1822, 1763, 1648, 1734, 1535, 1474, 1418, 1676, 1759, 1853, 1679, 1554, 1853, 1446, 1394, 1630, 1697, 1401, 1646, 1633, 1907, 1437, 1859, 1642, 1339, 1685, 1644, 1759, 1513, 1491, 1597, 1732, 1745, 1785, 1612, 1558, 1662, 1909, 1825, 1556, 1769, 1688, 1707, 1633, 1880, 1763, 1644, 1805, 1760, 1641, 1616, 1599, 1872, 1664, 1863, 1843] line [148, 223, 220, 295, 340, 374, 456, 424, 508, 642, 639, 537, 514, 767, 768, 902, 946, 743, 1139, 1066, 1115, 1199, 937, 828, 761, 880, 1274, 1328, 1694, 1984, 2060, 1663, 2150, 1857, 2057, 1927, 2562, 2247, 2706, 2146, 2549, 3159, 2722, 3591, 4072, 2789, 2988, 2738, 2978, 4366, 2737, 2227, 2350, 4169, 3659, 3911, 3272, 3503, 4808, 5675, 3733, 4616, 4769, 2860, 3903, 2554, 3733, 4283, 3347, 4527, 4993, 3682, 4607, 3590, 4797, 4264, 3591, 4173, 4433, 4130, 4328, 4294, 6093, 4001, 4038, 4253, 4219, 3455, 4166, 5482, 5105, 3708, 3998, 4370, 3843, 4254, 3909, 3932, 4755]
--- config: xyChart: width: 900 height: 600 themeVariables: xyChart: plotColorPalette: "#28678D,#f34544" --- xychart-beta title "写者操作" x-axis "读者线程数" 2 --> 100 y-axis "操作消耗平均时间(ms)" line [1290, 3892, 4222, 2694, 2595, 3900, 3116, 3012, 2448, 2580, 3334, 3658, 3082, 4245, 2419, 2291, 2019, 2060, 1424, 3642, 3095, 2644, 4063, 4677, 4144, 3035, 3078, 2299, 3988, 2584, 3461, 2501, 3945, 3364, 3777, 4627, 2947, 3468, 2752, 3683, 3719, 3869, 3839, 3670, 3025, 3881, 2755, 3984, 3156, 2906, 3739, 2512, 3832, 3884, 2724, 2544, 2981, 2167, 4049, 4412, 3262, 2129, 3312, 3298, 2783, 3364, 2648, 3441, 3623, 3561, 4197, 3513, 3104, 2788, 2645, 3689, 3041, 3292, 2598, 4888, 3058, 3247, 2879, 2881, 3627, 3246, 5128, 2906, 3508, 4363, 2441, 3251, 3862, 4574, 3496, 3500, 3551, 3889, 3178, 4238] line [131, 156, 131, 166, 179, 235, 267, 233, 288, 451, 390, 368, 368, 503, 533, 579, 517, 532, 703, 589, 876, 795, 772, 529, 366, 722, 865, 1067, 900, 1310, 1642, 1025, 1650, 1352, 1012, 1829, 1920, 1772, 1848, 1536, 2147, 1660, 1557, 2892, 2928, 1843, 1845, 561, 2160, 1902, 1821, 848, 1271, 1161, 2022, 2273, 1965, 1789, 2518, 2755, 2199, 2337, 2168, 1082, 2050, 1037, 1830, 1897, 898, 2368, 1192, 1968, 1147, 1803, 1617, 1800, 1340, 1944, 1794, 1214, 2017, 1993, 2478, 1915, 1164, 1503, 1479, 1486, 1300, 2018, 2074, 1579, 1267, 1917, 1627, 1844, 2384, 1827, 2388]
A58 - ArmV7
--- config: xyChart: width: 900 height: 600 themeVariables: xyChart: plotColorPalette: "#28678D,#f34544" --- xychart-beta title "读者操作" x-axis "读者线程数" 2 --> 100 y-axis "操作消耗平均时间(ms)" line [2457, 2534, 3098, 3555, 2603, 2752, 2182, 2549, 2008, 2224, 2276, 1920, 1883, 1972, 1828, 1795, 1914, 2013, 1934, 2692, 2190, 2667, 2439, 2265, 2556, 2413, 2317, 2288, 2327, 2241, 2454, 2293, 2100, 2433, 2223, 2191, 2349, 2108, 1859, 2288, 2270, 2163, 2177, 2029, 2106, 2020, 1959, 2258, 2204, 2069, 2149, 1969, 1918, 2047, 2216, 2050, 1946, 1941, 1891, 1914, 1931, 2124, 1935, 1898, 1897, 1784, 2034, 2005, 1888, 1893, 1829, 1808, 1923, 1952, 1997, 1970, 1887, 1859, 1891, 1899, 1772, 1907, 1841, 1887, 1949, 1854, 1823, 1932, 1881, 1877, 1796, 1814, 1909, 1927, 1832, 1814, 1886, 1844, 1819] line [201, 183, 209, 260, 301, 371, 361, 382, 527, 588, 554, 514, 566, 634, 652, 629, 576, 629, 659, 715, 677, 868, 720, 779, 830, 933, 935, 959, 890, 1217, 929, 967, 1002, 1021, 1084, 1064, 1367, 1169, 1184, 1428, 1223, 1317, 1304, 1385, 1373, 1481, 1419, 1418, 1439, 1417, 1442, 1423, 1403, 1504, 1431, 1470, 1540, 1548, 1547, 1604, 1574, 1566, 1568, 1618, 1824, 2018, 1733, 1707, 1697, 1697, 1760, 1753, 1828, 1729, 1817, 1800, 1856, 1910, 1904, 1821, 1825, 1844, 1920, 1916, 1872, 1988, 1950, 1982, 1951, 2006, 1917, 1983, 2003, 1928, 1916, 2062, 2012, 2053, 2143]
--- config: xyChart: width: 900 height: 600 themeVariables: xyChart: plotColorPalette: "#28678D,#f34544" --- xychart-beta title "写者操作" x-axis "读者线程数" 2 --> 100 y-axis "操作消耗平均时间(ms)" line [3789, 3695, 5413, 11001, 9174, 10076, 5790, 8715, 3593, 5966, 6616, 3933, 3498, 5221, 3953, 5069, 7497, 5400, 5790, 9477, 5003, 10011, 11999, 10363, 12755, 12189, 11128, 10552, 13025, 11198, 10668, 8748, 7696, 14514, 13213, 9938, 8251, 8354, 5258, 9767, 10209, 9887, 10354, 10827, 8651, 9789, 8060, 10968, 13114, 8849, 10323, 7687, 7386, 10777, 12034, 9359, 8319, 7868, 6982, 7863, 9990, 11183, 10215, 8532, 8367, 5561, 8930, 11887, 8338, 8045, 4703, 6089, 10610, 6788, 7118, 8658, 7816, 5522, 8628, 8273, 7855, 10658, 6850, 6248, 8997, 9190, 9267, 7038, 11003, 9391, 8637, 6109, 7737, 9697, 5912, 8634, 8606, 8555, 7449] line [251, 182, 215, 259, 301, 354, 354, 386, 480, 539, 581, 560, 611, 738, 676, 714, 669, 703, 743, 716, 673, 900, 918, 841, 923, 1004, 1006, 1054, 923, 1318, 1032, 1095, 1001, 1151, 1191, 1182, 1512, 1160, 1245, 1279, 1345, 1339, 1279, 1362, 1116, 1647, 1440, 1445, 1323, 1564, 1175, 1463, 1533, 1489, 1585, 1443, 1648, 1823, 1730, 1697, 1722, 1821, 1672, 1745, 1981, 1666, 1670, 2159, 1765, 2021, 1662, 1841, 2027, 1890, 1936, 1945, 2045, 1962, 1924, 1978, 2029, 1983, 2056, 2403, 2088, 2114, 2196, 2343, 2279, 2235, 2022, 2231, 2113, 2029, 1814, 2382, 2193, 2182, 1997]
总结
综合来看,随着读者线程数的上升,写者操作在一定程度上会保持着一个良好的状态,这个在 Arm 上的表现尤为明显,但是读者操作在不同的架构下均会在到达一定数量时的平均耗时会超过内核调度
总结
不难看出,原子操作更加适用于快速响应的轻量线程任务管理中,对于高并发来说,采用内核进行管理是最佳的选择,毕竟内核中会有一个托管队列用于管理对应的互斥锁。同时,在不同的架构下,线程数对原子操作所产生的影响是不同的,这个需要根据具体的场景进行测试才能做出妥善的选择