Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62c8ac3e5d | ||
|
|
1d7e93251c | ||
|
|
92fa4691d1 | ||
|
|
a20fdf4713 | ||
|
|
570dedf1c3 | ||
|
|
59e5c873ad | ||
|
|
8dbcdf9991 | ||
|
|
cd43b50068 | ||
|
|
c0413a26f9 | ||
|
|
b2d3d86e0c | ||
|
|
bc7552f4a8 | ||
|
|
28ae13092f | ||
|
|
1f561da43f | ||
|
|
ef8dcda196 | ||
|
|
cf9eb892cf | ||
|
|
ffa1e2e59c | ||
|
|
9549e82b04 | ||
|
|
50261e390e | ||
|
|
618fc99cda | ||
|
|
a9fc7e15f8 | ||
|
|
6b064870f5 | ||
|
|
d78c50be3b | ||
|
|
c96cea5a13 | ||
|
|
51d4e930f5 | ||
|
|
e650addfe4 | ||
|
|
30afcb45d8 | ||
|
|
e10c3586a3 | ||
|
|
836d892832 | ||
|
|
826d251ffe | ||
|
|
510cd062ba | ||
|
|
2695cd6f3e | ||
|
|
f6e3cbbb65 | ||
|
|
f172bbaad2 | ||
|
|
9176953a8e | ||
|
|
ae6b87d646 | ||
|
|
f719b2387a | ||
|
|
5bfa8321d0 | ||
|
|
e99fd23e69 | ||
|
|
5057366fb2 | ||
|
|
44801b8b91 | ||
|
|
9eabc86bdd | ||
|
|
fbc8f407eb | ||
|
|
09a8b93634 | ||
|
|
2caa7b77e7 | ||
|
|
c057247126 | ||
|
|
0899be21c3 | ||
|
|
c29c9f9ccd | ||
|
|
c12f698f32 | ||
|
|
d6a8a6d159 | ||
|
|
7bf325f91a | ||
|
|
fdfff0c5c5 | ||
|
|
4409f613e0 | ||
|
|
9de644b0f3 | ||
|
|
3e36363d28 | ||
|
|
4f1d1a949c | ||
|
|
e52f3b8b30 | ||
|
|
36916e7b3d | ||
|
|
ba778e9bd2 | ||
|
|
df7112489a | ||
|
|
d9c203736f | ||
|
|
2b61ffa784 | ||
|
|
ba85640172 | ||
|
|
4d21ad5050 | ||
|
|
aae14de62b | ||
|
|
70fa621745 | ||
|
|
e97f4f7f41 | ||
|
|
7a08ddce9e | ||
|
|
e19c9d31a8 | ||
|
|
51ae5f9f0c | ||
|
|
22e8133f95 | ||
|
|
c2e19fed01 | ||
|
|
274afc9978 | ||
|
|
0c337836ca | ||
|
|
1a3e694e20 | ||
|
|
a0572b63b1 | ||
|
|
1765d8ef06 | ||
|
|
1e00a102e8 | ||
|
|
61f8dcdbae | ||
|
|
39eeb3aec7 | ||
|
|
2178dcfcab | ||
|
|
f8b4f44105 | ||
|
|
9c2468c2b7 | ||
|
|
3d5cc55d0a | ||
|
|
5b9cccd2dd | ||
|
|
103b2ab79b | ||
|
|
05f1cd784c | ||
|
|
6e6754f84e | ||
|
|
33ef448a86 | ||
|
|
8ce59c2e9d | ||
|
|
85a363d325 | ||
|
|
7dc1a9dc0e | ||
|
|
983d90b731 | ||
|
|
f307762801 | ||
|
|
7924798e7a | ||
|
|
e46bbaffb9 | ||
|
|
0171c49413 | ||
|
|
a79cd9da97 | ||
|
|
408959c927 | ||
|
|
ac4d93fe29 | ||
|
|
82d224fa66 | ||
|
|
a5b6d8e391 | ||
|
|
7f79abec3f | ||
|
|
ca8d4aa97a | ||
|
|
690cef561c | ||
|
|
16ac4c556b | ||
|
|
3c175b9199 | ||
|
|
a3827cc384 | ||
|
|
353daecbd1 | ||
|
|
368392fb8f | ||
|
|
57441b2fc1 | ||
|
|
b225600238 | ||
|
|
6b361e0d0c | ||
|
|
08668aa02b | ||
|
|
3d3eb6f1d2 | ||
|
|
9bd820c652 | ||
|
|
0e960ec23f | ||
|
|
ca65d81b60 | ||
|
|
23624a1d73 | ||
|
|
3ac08b0395 | ||
|
|
dedb79dbc5 | ||
|
|
154b503bc6 | ||
|
|
5c612ce521 | ||
|
|
80582ac5ce | ||
|
|
8e6af3bfeb | ||
|
|
61671d1b18 | ||
|
|
aa3ad973d0 | ||
|
|
a19a010d1e | ||
|
|
36003b6bb3 | ||
|
|
3c83e4088c | ||
|
|
357feb9532 | ||
|
|
7e234cba65 | ||
|
|
0535ceeaeb | ||
|
|
e688f0b1df | ||
|
|
a950e15625 | ||
|
|
cf8b198dac | ||
|
|
c75d5da02d | ||
|
|
cdffb15066 | ||
|
|
cd0ad0dba9 | ||
|
|
d3be5dba96 | ||
|
|
cb94a26a9f | ||
|
|
3c8b238e75 | ||
|
|
74202d52b3 | ||
|
|
603c92cd0e | ||
|
|
e514d74f43 | ||
|
|
970161464a | ||
|
|
3e7de27c00 | ||
|
|
e28d403216 | ||
|
|
fdeb9f1322 | ||
|
|
76fad21103 | ||
|
|
5d4fa31083 | ||
|
|
c3858f4e63 | ||
|
|
2e0fc21878 | ||
|
|
4b71bbec7e | ||
|
|
052989a5a2 | ||
|
|
60cfc63c4e | ||
|
|
60ef4c7f06 | ||
|
|
58fc40d0f1 | ||
|
|
3d7e9e7c03 | ||
|
|
52e745f905 | ||
|
|
8433fc178c | ||
|
|
59807baa18 | ||
|
|
9b5b15e8cb | ||
|
|
2c54b63c51 | ||
|
|
e4d1167274 | ||
|
|
2f10aba390 | ||
|
|
a044fadec8 | ||
|
|
0edb6165b5 | ||
|
|
7d262f2450 | ||
|
|
fa30328572 | ||
|
|
b0cff7c6ac | ||
|
|
50da9e803c | ||
|
|
26252fedf5 | ||
|
|
2dcda90c7b | ||
|
|
b7d79c0a83 | ||
|
|
ae285c0a01 | ||
|
|
07db846f3b | ||
|
|
3b6907b743 | ||
|
|
82e49c6214 | ||
|
|
8e9aa9cac4 | ||
|
|
5cd6c1dd36 | ||
|
|
a98ce5ea91 | ||
|
|
a4bb0fab8f | ||
|
|
7b2e242c94 | ||
|
|
5fd6352a4e | ||
|
|
a82f2ba065 | ||
|
|
3377acd9e6 | ||
|
|
709168652a | ||
|
|
e25e9ba29b | ||
|
|
ed5eac6e9d | ||
|
|
651768f012 | ||
|
|
88c0a881da | ||
|
|
5b9dd504e5 | ||
|
|
165ec09061 | ||
|
|
6cc972bab2 | ||
|
|
333047fc4c | ||
|
|
3e4a5932c2 | ||
|
|
e2f6b81a03 | ||
|
|
0231aad947 | ||
|
|
6636800c5b | ||
|
|
d1f41de0cb | ||
|
|
c4d339cb62 | ||
|
|
56b2adc57d | ||
|
|
a73bee2650 | ||
|
|
517da1b999 | ||
|
|
25497bd96f | ||
|
|
d12b1edc39 | ||
|
|
0515cfd068 | ||
|
|
e84bc9fc0d | ||
|
|
d85df43329 | ||
|
|
16e2e07d41 | ||
|
|
57fcb4012b | ||
|
|
5be981def3 | ||
|
|
493e38abfc | ||
|
|
6e006d216b | ||
|
|
da613ad188 | ||
|
|
3dad326bea | ||
|
|
480a791f4c | ||
|
|
753c46ef14 | ||
|
|
f2d5de252b | ||
|
|
0cb1a8054f | ||
|
|
07eb5697ab | ||
|
|
e4dc06a0e2 | ||
|
|
cd2012f33d | ||
|
|
9eba30648d | ||
|
|
7e70371197 | ||
|
|
527f6d8d9a | ||
|
|
9ebc12a98f | ||
|
|
bb21d9a6ad | ||
|
|
ad39cdd393 | ||
|
|
0de771036b | ||
|
|
6d6afa4cfd | ||
|
|
7546cbedd2 | ||
|
|
8d5962eefd | ||
|
|
8597b0b8c0 | ||
|
|
c625c39883 | ||
|
|
bce86460fe | ||
|
|
f002eebc83 | ||
|
|
139da2e49f | ||
|
|
91d40a0f43 | ||
|
|
25fe26deaa | ||
|
|
cd357c5eb7 | ||
|
|
561f67b3b9 | ||
|
|
15a02e3306 | ||
|
|
944d29e8bc | ||
|
|
aefba70c1e | ||
|
|
c9ed4ba820 | ||
|
|
df97ea848c | ||
|
|
93d810421d | ||
|
|
f560839516 | ||
|
|
8a9d5f6a00 | ||
|
|
5f05c1e169 | ||
|
|
3df2408ffb | ||
|
|
ffc2ec8c4b | ||
|
|
b0ebbb306e | ||
|
|
c3574aa290 | ||
|
|
eabc867c2a | ||
|
|
a03b2e80c6 | ||
|
|
c4f23640d5 | ||
|
|
a42b854f14 | ||
|
|
9aa08bfc59 | ||
|
|
06b79a6caa | ||
|
|
6d533bd52a | ||
|
|
1b4c608087 | ||
|
|
6ccc81fd92 | ||
|
|
6f7d3fcd79 | ||
|
|
7484dee707 | ||
|
|
a580aaac83 | ||
|
|
52bef353e0 | ||
|
|
826ecf76c0 | ||
|
|
2cf5bff32d | ||
|
|
1377d7a1c2 | ||
|
|
ab9284ca20 | ||
|
|
5dab01c1c7 | ||
|
|
f9c434401b | ||
|
|
76b9f9a715 | ||
|
|
2ee82c478b | ||
|
|
211121435d | ||
|
|
fffd1702a5 | ||
|
|
9aa99bf230 | ||
|
|
1671ca8d67 | ||
|
|
ad16b310e1 | ||
|
|
4dbcfd25b3 | ||
|
|
01167b09c4 | ||
|
|
15d0b86c62 | ||
|
|
edbcecb272 | ||
|
|
1689db7b9c | ||
|
|
f5bc8796f7 | ||
|
|
e893560a48 | ||
|
|
a4ff7c71f2 | ||
|
|
9f780805f0 | ||
|
|
749273d1bb | ||
|
|
d8fd2e3e69 | ||
|
|
d443a84254 | ||
|
|
ef614b19ee | ||
|
|
1c5c4aa50f | ||
|
|
5533ffffaa | ||
|
|
34c990e259 | ||
|
|
5f0b6b9f26 | ||
|
|
26eb882a30 | ||
|
|
4d0e799c7a | ||
|
|
6801f688a4 | ||
|
|
bdf29af9e9 | ||
|
|
79dabfc52f | ||
|
|
de2268c406 | ||
|
|
85ea1f40dc | ||
|
|
f89ba94689 | ||
|
|
0279dcc3cc | ||
|
|
62c8e005bb | ||
|
|
2a28f8d407 | ||
|
|
9802fc5098 | ||
|
|
f372a72fd8 | ||
|
|
9d661852b2 | ||
|
|
b343d356c3 | ||
|
|
2e4404ca7e | ||
|
|
29eebaf38d | ||
|
|
d0d69aebfa | ||
|
|
477c5853f1 | ||
|
|
55e7b3083a | ||
|
|
0b9e27fb7f | ||
|
|
36ac6d5104 | ||
|
|
d898d6d4cc | ||
|
|
e7388929c4 | ||
|
|
45f8b31efc | ||
|
|
ebf440731d | ||
|
|
501a4a65ec | ||
|
|
800d03f728 | ||
|
|
19ff6bfeac | ||
|
|
24f5739a3b | ||
|
|
b658598e02 | ||
|
|
47343a2893 | ||
|
|
c1f99359be | ||
|
|
bd89e162fc | ||
|
|
983a63fbbc | ||
|
|
978e0ea5d7 | ||
|
|
85c4b50b6f | ||
|
|
e77aec4619 | ||
|
|
d67e084010 | ||
|
|
b0a74f162e | ||
|
|
d92126b97e | ||
|
|
c055358fa2 | ||
|
|
9f89148b69 | ||
|
|
ef1145603a | ||
|
|
d169a06f79 | ||
|
|
8e15a8cfe9 | ||
|
|
6466cefbf4 | ||
|
|
bec9a897c9 | ||
|
|
37d47cb0f1 | ||
|
|
0854136005 | ||
|
|
9427931e45 | ||
|
|
6aa28b4cfe | ||
|
|
a29682ca29 | ||
|
|
f5afd6e275 | ||
|
|
7b5fd14bcf | ||
|
|
22ce001f03 | ||
|
|
6f761091cc | ||
|
|
bb9e0e4f6e | ||
|
|
5e658a81fe | ||
|
|
ddc722bf78 | ||
|
|
0818d0ac7c | ||
|
|
2f315793cd | ||
|
|
067a034bc9 | ||
|
|
201cc45fd8 | ||
|
|
7a270ffacb | ||
|
|
27bb62533a | ||
|
|
032388b790 | ||
|
|
f1e2f81e79 | ||
|
|
54d4b80368 | ||
|
|
8a0a556604 | ||
|
|
895f362628 | ||
|
|
d24b52586d | ||
|
|
d76585cb0e | ||
|
|
fb44be6efe | ||
|
|
0b6a5aaa97 | ||
|
|
c00fdc65a2 | ||
|
|
ab7490eb8a | ||
|
|
1a46c0cd3f | ||
|
|
22f595c015 | ||
|
|
68c76ad146 | ||
|
|
0b18792b96 | ||
|
|
464e8e5f8f | ||
|
|
45410f3f0c | ||
|
|
556e706343 | ||
|
|
1e534bd307 | ||
|
|
7a517ee3ae | ||
|
|
9bb1957afa | ||
|
|
86f942ee5f | ||
|
|
a23df528e4 | ||
|
|
0e9af6dfac | ||
|
|
99a8db1111 | ||
|
|
24cdf1ca5c | ||
|
|
f1b794b239 | ||
|
|
b4fdcbc547 | ||
|
|
ce9986f6d8 | ||
|
|
ad4dcae1db | ||
|
|
b3fa63c971 | ||
|
|
364101592b | ||
|
|
5b89196cd7 | ||
|
|
feb8b30048 | ||
|
|
8340cf70fb | ||
|
|
affb3778a0 | ||
|
|
c039f07f2d | ||
|
|
c180981768 | ||
|
|
2cfac5c332 | ||
|
|
8ba489eef3 | ||
|
|
a4cb6e3681 | ||
|
|
379e0be211 | ||
|
|
acb886c391 | ||
|
|
7f26ef0f1c | ||
|
|
1d8e541205 | ||
|
|
a4fa70a478 | ||
|
|
3d3a473716 | ||
|
|
e04b120d41 | ||
|
|
63eee6bba3 | ||
|
|
b396b42808 | ||
|
|
eb8b106f48 | ||
|
|
5d144991cb | ||
|
|
35718ae551 | ||
|
|
1dd9aff402 | ||
|
|
6d47074457 | ||
|
|
1943650ca3 | ||
|
|
5722f19939 | ||
|
|
7cf14b5d8a | ||
|
|
b3a1a75f6f | ||
|
|
eb7556f88f | ||
|
|
805c747c60 | ||
|
|
5f9feb833c | ||
|
|
efef35c1e1 | ||
|
|
b1b32f565d | ||
|
|
f4cd8eaff9 | ||
|
|
2cd5258f89 | ||
|
|
1b206931df | ||
|
|
f68840ef8c | ||
|
|
779c15ec08 | ||
|
|
29bcc00202 | ||
|
|
1ff0d9fac0 | ||
|
|
4488853642 | ||
|
|
d5dc5d083d | ||
|
|
784c42d850 | ||
|
|
8c1eb1fcb5 | ||
|
|
49180bfbe5 | ||
|
|
a3116dd813 | ||
|
|
23df465401 | ||
|
|
966170452f | ||
|
|
eaf7ae7dee | ||
|
|
8a6652646a | ||
|
|
2063bf879d | ||
|
|
2529d4ae49 | ||
|
|
439bc06423 | ||
|
|
ab6c90f42f | ||
|
|
ac9e9a83b9 | ||
|
|
e495b92ae5 | ||
|
|
50b1bedc54 | ||
|
|
4a80ccd5f4 | ||
|
|
6ca65dd60c | ||
|
|
b2be4d5397 | ||
|
|
de34cefa83 | ||
|
|
40c8dbae1e | ||
|
|
18af53cf38 | ||
|
|
8510318ba5 | ||
|
|
6ad02ffe56 | ||
|
|
535e45d564 | ||
|
|
e2c60aa45c | ||
|
|
56d2a74818 | ||
|
|
8a36b5ff15 | ||
|
|
7607645636 | ||
|
|
cbb84accfe | ||
|
|
3e70cf7b5f | ||
|
|
1a7deef735 | ||
|
|
748771ab72 | ||
|
|
6ef279dd2f | ||
|
|
abcf37dc72 | ||
|
|
690b7bf9c1 | ||
|
|
71cec5155a | ||
|
|
d2b0873a95 | ||
|
|
4cecdbb93e | ||
|
|
2abca7d244 | ||
|
|
c26b467e86 | ||
|
|
19514b09b0 | ||
|
|
ab98577deb | ||
|
|
aae5f00dbd | ||
|
|
04706d48b9 | ||
|
|
c7df60c78d | ||
|
|
f03a4503eb | ||
|
|
a6951fe5e3 | ||
|
|
6def48a0d1 | ||
|
|
8b5d1e07ce | ||
|
|
3182c39280 | ||
|
|
e0b5dc2f02 | ||
|
|
6e5181e405 | ||
|
|
75f4f23144 | ||
|
|
bc1402f66a | ||
|
|
fc5ffcb404 | ||
|
|
bd20a021ce | ||
|
|
dfd12107de | ||
|
|
6743cb9456 | ||
|
|
4aa93325e9 | ||
|
|
f2a06ede62 | ||
|
|
a36752b8ce | ||
|
|
3b347f2ba5 | ||
|
|
47296e5c11 | ||
|
|
a24c530ec6 | ||
|
|
7ebe0608ae | ||
|
|
2a3021c021 | ||
|
|
4d313aa72b | ||
|
|
7906a39647 | ||
|
|
86254958f0 | ||
|
|
0089c1af61 | ||
|
|
6b5a3ddef7 | ||
|
|
42c2fd69a2 | ||
|
|
bcb9b831db | ||
|
|
f6483cbb30 | ||
|
|
f742707699 | ||
|
|
aee02d86ab | ||
|
|
dfbf34daf6 | ||
|
|
494cbbecf0 | ||
|
|
a1513777f1 | ||
|
|
9784a16d47 | ||
|
|
a8648e0cc1 | ||
|
|
3f8e93ad7a | ||
|
|
b040bc411f | ||
|
|
ae8c3e5300 | ||
|
|
01d390c3e9 | ||
|
|
56e4cda76c | ||
|
|
c59d585c22 | ||
|
|
81c97682a7 | ||
|
|
a432628df6 | ||
|
|
d16effb4c5 | ||
|
|
6279f98bb5 | ||
|
|
3cbc474e76 | ||
|
|
c80fb7bebe | ||
|
|
68788a6fb8 | ||
|
|
fd3cb50249 | ||
|
|
7e0bd8414d | ||
|
|
70de571dc8 | ||
|
|
cc6da8941c | ||
|
|
d27c448a94 | ||
|
|
06a1590645 | ||
|
|
652653af0b | ||
|
|
c520b7d0be | ||
|
|
4f4605befb | ||
|
|
3f84f54d65 | ||
|
|
bf49b8e933 | ||
|
|
1edb204ec6 | ||
|
|
dd5c9ac8ad | ||
|
|
32b6f82249 | ||
|
|
44b3895698 | ||
|
|
5ce6ccde3d | ||
|
|
ee64e068f9 | ||
|
|
8d89e7c958 | ||
|
|
ef74f8eb55 | ||
|
|
a7749e944f | ||
|
|
18bf06ff5f | ||
|
|
b27f490acb | ||
|
|
e84fa859c6 | ||
|
|
4c57ca69bc | ||
|
|
b0d2557ea1 | ||
|
|
82906e03e9 | ||
|
|
1859868838 | ||
|
|
4ab3a3605b | ||
|
|
279b46b4e2 | ||
|
|
17950c5fe5 | ||
|
|
45b94c8abc | ||
|
|
a98ebd3b20 | ||
|
|
cb7cd1ac67 | ||
|
|
9a41faf40a | ||
|
|
8c44362f74 | ||
|
|
548c0c0c61 | ||
|
|
607221f480 | ||
|
|
160c76361b | ||
|
|
2fadad0c8d | ||
|
|
4d4973abcc | ||
|
|
f8ae9eb677 | ||
|
|
de825203cf | ||
|
|
4f865176d3 | ||
|
|
cb4b43e2e8 | ||
|
|
89b6e1aac0 | ||
|
|
daeaa242b8 | ||
|
|
100af17328 | ||
|
|
7527bfec85 | ||
|
|
f73d25c92d | ||
|
|
9080f5d52f | ||
|
|
6beb142bbe | ||
|
|
9b8b93288a | ||
|
|
6a2a549ec5 | ||
|
|
468b7e7a0c | ||
|
|
fd640e9232 | ||
|
|
d13d7feae3 | ||
|
|
17f69326e2 | ||
|
|
273fcd51b6 | ||
|
|
f4b9185ceb | ||
|
|
b722227b39 | ||
|
|
3d7e2f5c6b | ||
|
|
9369530068 | ||
|
|
4e968c3777 | ||
|
|
9a5302ac19 | ||
|
|
8216ef0f7b | ||
|
|
b897027021 | ||
|
|
6095cf8610 | ||
|
|
e917ac5663 | ||
|
|
2b44892aad | ||
|
|
294e45c7e1 | ||
|
|
467acfdd10 | ||
|
|
d3823951b5 | ||
|
|
cc33079d43 | ||
|
|
78d0bfb286 | ||
|
|
b2f5ae6917 | ||
|
|
9c723da768 | ||
|
|
70c3932f6e | ||
|
|
92bddcaab4 | ||
|
|
79a8401d75 | ||
|
|
570473e465 | ||
|
|
990b7b02d7 | ||
|
|
0b301d1cea | ||
|
|
f35db79792 | ||
|
|
dc1f163716 | ||
|
|
4d6bb407ee | ||
|
|
37cef298c6 | ||
|
|
0396d1f648 | ||
|
|
e694d83cc4 | ||
|
|
19fc150763 | ||
|
|
3784f653d9 | ||
|
|
eb40cff6a1 | ||
|
|
b7f84236d3 | ||
|
|
65e3f1049b | ||
|
|
cc45b9c1fe | ||
|
|
9340c2e55c | ||
|
|
9710e014f2 | ||
|
|
7761fb6bf1 | ||
|
|
2f4df5f67c | ||
|
|
b841f81416 | ||
|
|
72fd53ab44 | ||
|
|
8335bc525b | ||
|
|
9e5d14ce36 | ||
|
|
3bafede8b7 | ||
|
|
3f2904ab0d | ||
|
|
933b495ed1 | ||
|
|
77813adc00 | ||
|
|
ce2adea6c0 | ||
|
|
3ecdfff8f0 | ||
|
|
a103ceb850 | ||
|
|
9f24b89487 | ||
|
|
8be06cadb3 | ||
|
|
c488d979cf | ||
|
|
f4c24f07af | ||
|
|
3d2ce63d63 | ||
|
|
0ac2c62ffe | ||
|
|
6001781e68 | ||
|
|
d90cb7caf3 | ||
|
|
88a4835025 | ||
|
|
75ae7a06e5 | ||
|
|
3885cb29a2 | ||
|
|
bf3ffd1766 | ||
|
|
d884d257bb | ||
|
|
a87cac84dd | ||
|
|
d1ae26f0fd | ||
|
|
9c58e73c37 | ||
|
|
024b62a7a7 | ||
|
|
c59731775e | ||
|
|
4668476c6a | ||
|
|
743959ebde | ||
|
|
dd27fa7e72 | ||
|
|
f2025f83d3 | ||
|
|
8c06b43b2b | ||
|
|
3cde7a1731 | ||
|
|
e9db1d28b8 | ||
|
|
adffaf0e87 | ||
|
|
49a2e600c4 | ||
|
|
0bedcba32b | ||
|
|
c531f805b0 | ||
|
|
da0ab5ea98 | ||
|
|
6774afd751 | ||
|
|
6d489136e7 | ||
|
|
4ea6dbc7fa | ||
|
|
defc58f6a3 | ||
|
|
006d0d16a5 | ||
|
|
b8af8bb344 | ||
|
|
e249be1142 | ||
|
|
05fde6c3cf | ||
|
|
aaa1d7dc4b | ||
|
|
8c479b2e5a | ||
|
|
45d2a529b5 | ||
|
|
7d17223332 | ||
|
|
12bc65b6a8 | ||
|
|
a849d5c031 | ||
|
|
f195b09657 | ||
|
|
439177ec71 | ||
|
|
aadd4f11b1 | ||
|
|
113fdbf388 | ||
|
|
648406a99a | ||
|
|
055e74d350 | ||
|
|
ac3d7568f2 | ||
|
|
6c222b9ad7 | ||
|
|
4426a60904 | ||
|
|
34f70a031f | ||
|
|
79f2f396b9 | ||
|
|
88143f7d52 | ||
|
|
c1261dd0f2 | ||
|
|
8f7a60ea73 | ||
|
|
4758bbd6cc | ||
|
|
3f66325c19 | ||
|
|
6f10d7deb9 | ||
|
|
919900c90a | ||
|
|
e6daaf429c | ||
|
|
1200f30695 | ||
|
|
fca1120990 | ||
|
|
3735704c95 | ||
|
|
8c0c1b3102 | ||
|
|
db192993f1 | ||
|
|
4f44b3f60b | ||
|
|
b196fcc3c0 | ||
|
|
395f1af19e | ||
|
|
d8f761e889 | ||
|
|
563ffb4275 | ||
|
|
9ba2de4b70 | ||
|
|
0ac6a53fe9 | ||
|
|
4431e9f9ae | ||
|
|
48526ff5c7 | ||
|
|
4ee453025c | ||
|
|
1e7b543c0f | ||
|
|
8c2b4f8cf9 | ||
|
|
13cadfa470 | ||
|
|
deb595372b | ||
|
|
01fd5d929e | ||
|
|
2d60bd2c1b | ||
|
|
9655b13203 | ||
|
|
6b97a7436d | ||
|
|
1c792dd0a4 | ||
|
|
dcbd9f496f | ||
|
|
9804120cde | ||
|
|
a9b06718f0 | ||
|
|
963d819e22 | ||
|
|
28847af43f | ||
|
|
130fa36970 | ||
|
|
ab99035291 | ||
|
|
6de43d8a45 | ||
|
|
7b46891622 | ||
|
|
a0a32d8c6c | ||
|
|
1ffd07dc6e | ||
|
|
cb49cd7100 | ||
|
|
923b340a80 | ||
|
|
5b8750ee2a | ||
|
|
d438d089fb | ||
|
|
843ddac3dd | ||
|
|
c5238e0a4c | ||
|
|
f99ee8aa40 | ||
|
|
18c7d0e11a | ||
|
|
4e67823593 | ||
|
|
e7fcefb391 | ||
|
|
be06426783 | ||
|
|
e558e83be3 | ||
|
|
d77a235b2a | ||
|
|
3add6bb5bf | ||
|
|
4367daaa00 | ||
|
|
4d46b3d126 | ||
|
|
548cd84a7f | ||
|
|
d87ffaa572 | ||
|
|
370cdb4e45 | ||
|
|
068d023917 | ||
|
|
86b6ad54d5 | ||
|
|
bdf999a9fc | ||
|
|
39f8b6828b | ||
|
|
11f84ab34b | ||
|
|
c3a5306266 | ||
|
|
1fe9e4c14c | ||
|
|
e69203dc50 | ||
|
|
342954f272 | ||
|
|
741d6b8828 | ||
|
|
f1f41ef320 | ||
|
|
785792d1f4 | ||
|
|
9affe3106c | ||
|
|
eb9fd14e2c | ||
|
|
c281a2c339 | ||
|
|
cb109eb479 | ||
|
|
3c6d194974 | ||
|
|
653a0d9734 | ||
|
|
a5fbd8405c | ||
|
|
70994d4f60 | ||
|
|
f15203afb2 | ||
|
|
0a0d707afd | ||
|
|
e206c3fe48 | ||
|
|
7f5704d321 | ||
|
|
4f7b0b9455 | ||
|
|
f5f16015df | ||
|
|
935325e0cb | ||
|
|
7b9530ac58 | ||
|
|
a9fc259420 | ||
|
|
6f603b52df | ||
|
|
aeb2a47952 | ||
|
|
ca7cea3fe3 | ||
|
|
f14aca8808 | ||
|
|
f1e5e9d9db | ||
|
|
cecba9777a | ||
|
|
4352267034 | ||
|
|
0c3730d9a3 | ||
|
|
5111b0c726 | ||
|
|
8ecd3f1592 | ||
|
|
0a6f8851c9 | ||
|
|
f0d3e93d9d | ||
|
|
e77ff2451b | ||
|
|
142c224f84 | ||
|
|
29e8d9199d | ||
|
|
7128f5add2 | ||
|
|
1b7bcda4e1 | ||
|
|
03b6146ef0 | ||
|
|
e99735d2b6 | ||
|
|
7263629c55 | ||
|
|
605ce9b938 | ||
|
|
049d8b6ca6 | ||
|
|
90d3dda830 | ||
|
|
2022a66db3 | ||
|
|
78c786070e | ||
|
|
c5c72007af | ||
|
|
c60d59cb7a | ||
|
|
00111e3e3c | ||
|
|
5cc2ac17d9 | ||
|
|
d37ea84ab5 | ||
|
|
5e335bc8bd | ||
|
|
65765763ae | ||
|
|
90f248fbe8 | ||
|
|
a39cf23697 | ||
|
|
261776762c | ||
|
|
f207f9441c | ||
|
|
ffed880e0a | ||
|
|
a79b67cea2 | ||
|
|
bc3c8b5bcd | ||
|
|
3489bb42bc | ||
|
|
7fe36f7bd7 | ||
|
|
82fd4c94db | ||
|
|
4fc48b490e | ||
|
|
750e5e6d95 | ||
|
|
cb564128e6 | ||
|
|
047fc986f2 | ||
|
|
aca800ef8a | ||
|
|
dde090d4b0 | ||
|
|
87183573ce | ||
|
|
baddcc3855 | ||
|
|
ba531774a0 | ||
|
|
708d17a4d2 | ||
|
|
7d802c03e5 | ||
|
|
787e1e9965 | ||
|
|
625691d279 | ||
|
|
4f0148dc77 | ||
|
|
c78f2b992f | ||
|
|
9857db0d15 | ||
|
|
8b71064643 | ||
|
|
bc39034f6d | ||
|
|
e6a9b8fc8e | ||
|
|
3db43dc2db | ||
|
|
d384d5ad6c | ||
|
|
7b2004caad | ||
|
|
6754f88861 | ||
|
|
9393809dfd | ||
|
|
79ecacb6c0 | ||
|
|
853acbad4b | ||
|
|
e158eac10a | ||
|
|
48ca2a2384 | ||
|
|
d9d3d3cbe3 | ||
|
|
6b4f10bf45 | ||
|
|
3b647b982f | ||
|
|
00b875ea53 | ||
|
|
545fc4d93c | ||
|
|
c41342739a | ||
|
|
ed99c5aaa3 | ||
|
|
08842811e8 | ||
|
|
3ea063d1e0 | ||
|
|
a905f12915 | ||
|
|
5d8548b92b | ||
|
|
99b63e9c77 | ||
|
|
4002ef5aff | ||
|
|
d31ccfe8a4 | ||
|
|
c4ab76a330 | ||
|
|
84d4277be3 | ||
|
|
0a99814ce2 | ||
|
|
11d695cb57 | ||
|
|
13e2d6b473 | ||
|
|
e8137e4218 | ||
|
|
fe5df08d6e | ||
|
|
f77017f35f | ||
|
|
ce93c00a7b | ||
|
|
b8f44cc219 | ||
|
|
2c921fdefe | ||
|
|
20eb95ba45 | ||
|
|
7a6239912e | ||
|
|
9067311788 | ||
|
|
23b2e07132 | ||
|
|
c8f2c0e06e | ||
|
|
72c4eb80a2 | ||
|
|
1f18e788e6 | ||
|
|
f855e24b56 | ||
|
|
84d1b7ad50 | ||
|
|
9f82649aa8 | ||
|
|
791f586c7e | ||
|
|
8d3b0cc94b | ||
|
|
70d1dfeb8b | ||
|
|
9a4c6ba0d5 | ||
|
|
ef2dbf89ee | ||
|
|
d30b776fb0 | ||
|
|
6408b28513 | ||
|
|
32b3e21564 | ||
|
|
1ef28bedcb | ||
|
|
1356bb746e | ||
|
|
23240a44d5 | ||
|
|
589c023622 | ||
|
|
047b6c5a58 | ||
|
|
951425e963 | ||
|
|
e69cbfcab3 | ||
|
|
37f3cd6a72 | ||
|
|
ec7b5dd120 | ||
|
|
d50a78171f | ||
|
|
b422aec2cd | ||
|
|
7ba0e03b4f | ||
|
|
c535766c22 | ||
|
|
289a3fdc71 | ||
|
|
7f0b6208a4 | ||
|
|
60a10eb799 | ||
|
|
5321085856 | ||
|
|
03be814a4d | ||
|
|
6c3f7683a5 | ||
|
|
06d6a90a21 | ||
|
|
747da27489 | ||
|
|
2869345920 | ||
|
|
6b8938cf76 | ||
|
|
8e7eae5751 | ||
|
|
1882148a7c | ||
|
|
e46746ce01 | ||
|
|
32917e2cfa | ||
|
|
65a243fc13 | ||
|
|
1e011cb851 | ||
|
|
48f585a4ee | ||
|
|
bc10b616ae | ||
|
|
851f08131d | ||
|
|
e294f4557e | ||
|
|
9e5775fc16 | ||
|
|
8eb95d0cdc | ||
|
|
42f807d64b | ||
|
|
d1bb97ac52 | ||
|
|
f502b16b1d | ||
|
|
51e5764258 | ||
|
|
ec2281c29a | ||
|
|
acfe1cb7e8 | ||
|
|
594aebd903 | ||
|
|
8bca63ac08 | ||
|
|
c02ec4f94c | ||
|
|
797ac1f0da | ||
|
|
058d3202ff | ||
|
|
62fcdac9cb | ||
|
|
b9131353b6 | ||
|
|
a34deb2f3b | ||
|
|
9ef4b5c93f | ||
|
|
417b0295be | ||
|
|
d11409bcc6 | ||
|
|
928b81f454 | ||
|
|
05a68157bc | ||
|
|
b6d5b5462b | ||
|
|
1c77082a29 | ||
|
|
363c55668d | ||
|
|
7ed85fb97a | ||
|
|
585ea1293d | ||
|
|
448072555f | ||
|
|
4701d5919b | ||
|
|
0997f59665 | ||
|
|
dcf7a94728 | ||
|
|
12ba563416 | ||
|
|
f2fffb9219 | ||
|
|
3f03615ed2 | ||
|
|
bc6bda057c | ||
|
|
aeebe42bfb | ||
|
|
5c4c93b62f | ||
|
|
c3ddd48d38 | ||
|
|
98f3c40ac5 | ||
|
|
ada1b9861f | ||
|
|
50cc8689bb | ||
|
|
83da861a9c | ||
|
|
1cb1d16e77 | ||
|
|
b525723084 | ||
|
|
4accdc994d | ||
|
|
ad9bee0eec | ||
|
|
6c7ff1c723 | ||
|
|
3b2aad1b2e | ||
|
|
5222fabf02 | ||
|
|
44abead174 | ||
|
|
492d5f0f9a | ||
|
|
9bc5ac3df8 | ||
|
|
5c726d63f4 | ||
|
|
2cd0a8db74 | ||
|
|
0fa0445fa9 | ||
|
|
1e171f9cc0 | ||
|
|
8d16ea4bf4 | ||
|
|
a0c2ead91d | ||
|
|
b2875f6ba0 | ||
|
|
edb4ccf63e | ||
|
|
481009d18b | ||
|
|
6153383dab | ||
|
|
74fed41818 | ||
|
|
2d5de1b9ea | ||
|
|
e23691c743 | ||
|
|
aaac236db6 | ||
|
|
ccccedc2d5 | ||
|
|
446afb04c7 | ||
|
|
87cd964329 | ||
|
|
80a344e46d | ||
|
|
f2eb5014ac | ||
|
|
d6c72061ce | ||
|
|
7c26fd25c9 | ||
|
|
10e12d2b5c | ||
|
|
b9070f89d9 | ||
|
|
46d41a8882 | ||
|
|
b5dc3481d4 | ||
|
|
c18cd81dde | ||
|
|
eaf5a26157 | ||
|
|
547c67b676 | ||
|
|
c89d6ef0ce | ||
|
|
a54041c340 | ||
|
|
352e0972cd | ||
|
|
91ebd7cc9f | ||
|
|
d8854c276b | ||
|
|
cc61840ae8 | ||
|
|
67a7a4762a | ||
|
|
c28597014f | ||
|
|
d00f29bf26 | ||
|
|
1432fbabca | ||
|
|
f3b6ee0190 | ||
|
|
229d21ef72 | ||
|
|
00f1b1fc4f | ||
|
|
277d8b37af | ||
|
|
7b83761ef4 | ||
|
|
a96e9c7e11 | ||
|
|
866721d067 | ||
|
|
b356c29c89 | ||
|
|
a2f7cb7670 | ||
|
|
08887c1df0 | ||
|
|
8b0a9f2da9 | ||
|
|
e6c958c0df | ||
|
|
01ad221972 | ||
|
|
c269a36e8b | ||
|
|
95fcd054a3 | ||
|
|
7760f16260 | ||
|
|
6d016d732b | ||
|
|
b9aebf875e | ||
|
|
1eb0e8730d | ||
|
|
7eb26a29f1 | ||
|
|
0ef051e6d2 | ||
|
|
0d641f0d17 | ||
|
|
ce6a7f6cb3 | ||
|
|
07b4c1a9f4 | ||
|
|
4d3a82583d | ||
|
|
2835d53ef1 | ||
|
|
620ecadec1 | ||
|
|
dc803f23bb | ||
|
|
a6d11fe897 | ||
|
|
78b05690e7 | ||
|
|
958a79ad73 | ||
|
|
ead25c905a | ||
|
|
97f80450ca | ||
|
|
8e62537604 | ||
|
|
7813182e2b | ||
|
|
b199fef674 | ||
|
|
e27c06c227 | ||
|
|
41b04f34e2 | ||
|
|
b091f84f90 | ||
|
|
05ae9f382b | ||
|
|
a36efcd888 | ||
|
|
af2fe9ccf9 | ||
|
|
c4a5727c2a | ||
|
|
31ca762c62 | ||
|
|
30bda70319 | ||
|
|
7fa9d375b8 | ||
|
|
91ae3fd4f2 | ||
|
|
b00d6a9655 | ||
|
|
013aa58524 | ||
|
|
8ce7e0483f | ||
|
|
582c413cd2 | ||
|
|
22e16e983b | ||
|
|
7c0c16260a | ||
|
|
e496fbd152 | ||
|
|
e025de91ab | ||
|
|
69e2fc432f | ||
|
|
78ba83ce42 | ||
|
|
6d9b61c119 | ||
|
|
61cfa5cedd | ||
|
|
0507d94306 | ||
|
|
7e8919080b | ||
|
|
8406efcaab | ||
|
|
2d87eb1e6a | ||
|
|
4685aac454 | ||
|
|
9863a8325c | ||
|
|
b9514cbe72 | ||
|
|
7b085d18fc | ||
|
|
91e73520f3 | ||
|
|
aebf5c83ba | ||
|
|
42ff47f511 | ||
|
|
746bdf144b | ||
|
|
4e8f5f7b51 | ||
|
|
50d89f71cb | ||
|
|
d70ef53283 | ||
|
|
1b6182c1cd | ||
|
|
73b50c852a | ||
|
|
063011d8e9 | ||
|
|
0f3bf6ab82 | ||
|
|
475836e851 | ||
|
|
325ca30c61 | ||
|
|
72e872c768 | ||
|
|
c2a837b30b | ||
|
|
08822ac7d5 | ||
|
|
7f9007ca1a | ||
|
|
f067cb334c | ||
|
|
60ee253fa7 | ||
|
|
4642e0b506 | ||
|
|
bd29680b62 | ||
|
|
c337462af6 | ||
|
|
c990277855 | ||
|
|
8870a1c2f9 | ||
|
|
60f1454550 | ||
|
|
38eb1c26c6 | ||
|
|
af050d0100 | ||
|
|
14f3ae5cea | ||
|
|
f4ed750d39 | ||
|
|
4b3adc8d35 | ||
|
|
8b3a4a802c | ||
|
|
8b9f605ffa | ||
|
|
3c56f24db2 | ||
|
|
82dbf60a4c | ||
|
|
b596e81e88 | ||
|
|
b7bbe15547 | ||
|
|
cb5f8c20c8 | ||
|
|
61a1e15103 | ||
|
|
2416c2e466 | ||
|
|
b898ce3c56 | ||
|
|
061e83b9e1 | ||
|
|
0a8f727908 | ||
|
|
ba584ab096 | ||
|
|
1fb1fa2e0a | ||
|
|
8cf780a60f | ||
|
|
ae171115bc | ||
|
|
a8620c3aff | ||
|
|
c154cdf46c | ||
|
|
77c59e2b85 | ||
|
|
f07656757f | ||
|
|
a1c22fb679 | ||
|
|
3640041c47 | ||
|
|
d3934dccb9 | ||
|
|
10cf2f2270 | ||
|
|
c180b2b96d | ||
|
|
5d5c2380ed | ||
|
|
a8b5c3ff4e | ||
|
|
16587aeb93 | ||
|
|
629a3a3dc1 | ||
|
|
ea46379d24 | ||
|
|
ee0c77edc8 | ||
|
|
c0610b2d60 | ||
|
|
13ab545246 | ||
|
|
d85d6bcb13 | ||
|
|
138662d6a0 | ||
|
|
e45437e19f | ||
|
|
725846e155 | ||
|
|
74aa514903 | ||
|
|
7f7230be90 | ||
|
|
d4a6e8c47a | ||
|
|
9c19ed4471 | ||
|
|
357c37c69d | ||
|
|
7c2d9c8643 | ||
|
|
69d699b80e | ||
|
|
40832dd727 | ||
|
|
31c3baa42b | ||
|
|
0ed03224c2 | ||
|
|
f8d82ae74a | ||
|
|
16234bde82 | ||
|
|
31a5d7ee3b | ||
|
|
f908c7e04d | ||
|
|
eb20f66bd9 | ||
|
|
3b3e853cf8 | ||
|
|
998ae05a5b | ||
|
|
155d01b8c3 | ||
|
|
1a4cce7efd | ||
|
|
3a0dd2ffe7 | ||
|
|
68b705b16a | ||
|
|
fe245e76cf | ||
|
|
618e37b07c | ||
|
|
3dde8d45c1 | ||
|
|
4691f95b73 | ||
|
|
02d4bf0e09 | ||
|
|
5ecf4f37c0 | ||
|
|
15f6484dd2 | ||
|
|
2ead3905e5 | ||
|
|
99d883253c | ||
|
|
ed4d4f9f23 | ||
|
|
d76f9565b2 | ||
|
|
1c637f1182 | ||
|
|
4dbe5de79c | ||
|
|
afcb88d04c | ||
|
|
1ea1deef94 | ||
|
|
08d2af22db | ||
|
|
70179bb1d7 | ||
|
|
83226c5ce8 | ||
|
|
3d6b737c46 | ||
|
|
fff511101c | ||
|
|
6672becdd5 | ||
|
|
bd315c7e43 | ||
|
|
fbfe28c087 | ||
|
|
adbafc0b02 | ||
|
|
4e6dad939a | ||
|
|
12bf9c0f59 | ||
|
|
f7f7d7bc38 | ||
|
|
c169c05bba | ||
|
|
19d48740cd | ||
|
|
7a067a4b90 | ||
|
|
13f3dab23a | ||
|
|
fd9d308b28 | ||
|
|
1047d842d0 | ||
|
|
77385e53f7 | ||
|
|
f7b3de2750 | ||
|
|
a65e6de66b | ||
|
|
41cf523dfc | ||
|
|
3774f8fd11 | ||
|
|
b9e26b1c2c | ||
|
|
609175713b | ||
|
|
d56b794177 | ||
|
|
608a929fcb | ||
|
|
4bf045e3d6 | ||
|
|
6b5434d64f | ||
|
|
abb5480890 | ||
|
|
95069b0d4c | ||
|
|
8f15b50b5f | ||
|
|
c7a1505f9b | ||
|
|
ff098c91f4 | ||
|
|
fb40a4f904 | ||
|
|
c87696f5c1 | ||
|
|
4f3dad4bea | ||
|
|
46c2e33096 | ||
|
|
66ee266b90 | ||
|
|
14883b1e26 | ||
|
|
33f74988b1 | ||
|
|
78b157ff7f | ||
|
|
546b756932 | ||
|
|
5b74d54c64 | ||
|
|
fc608b21ee | ||
|
|
2b6a266b93 | ||
|
|
7cb5092010 | ||
|
|
a1c3681002 | ||
|
|
91f35aa3bb | ||
|
|
49a6715dfe | ||
|
|
b799725bef | ||
|
|
1ada22462f | ||
|
|
b0ec9680a1 | ||
|
|
7a8a6b1bb1 | ||
|
|
1380a91bf1 | ||
|
|
83208f74ec | ||
|
|
5f571418d0 | ||
|
|
a7d8ff208f | ||
|
|
1046f3e220 | ||
|
|
6406f97f56 | ||
|
|
e0c456490a | ||
|
|
6a1b2a3964 | ||
|
|
3452b72add | ||
|
|
2e3b312fab | ||
|
|
8f67ca339b | ||
|
|
1679b7b5cf | ||
|
|
757049543d | ||
|
|
38b098b547 | ||
|
|
60478e3f61 | ||
|
|
6325f01122 | ||
|
|
74c12e6ea2 | ||
|
|
fd3453bb2a | ||
|
|
2ff7631c9c | ||
|
|
7a6b0ab641 | ||
|
|
8fd0003548 | ||
|
|
bb8de993b6 | ||
|
|
7c83d733cd | ||
|
|
6f49a1905c | ||
|
|
d0378aadeb | ||
|
|
4834318742 | ||
|
|
1838a490ba | ||
|
|
de3cbe45b4 | ||
|
|
a34cb6c89d | ||
|
|
9c0724ddb9 | ||
|
|
ce948bd9ce | ||
|
|
5eb5d2420e | ||
|
|
7510e36f6b | ||
|
|
8bfdf2bd5f | ||
|
|
2174c28aa2 | ||
|
|
7a55667a5d | ||
|
|
79d0452427 | ||
|
|
ad5c141a7c | ||
|
|
d4cd327ea3 | ||
|
|
27b29d5ffe | ||
|
|
b52222cad9 | ||
|
|
822c27399c | ||
|
|
0e717389c1 | ||
|
|
2197c4ce81 | ||
|
|
b894d64063 | ||
|
|
d95a11365a | ||
|
|
dbcade3087 | ||
|
|
7ac54c0eb8 | ||
|
|
91c74b863d | ||
|
|
8c0b6f1e9d | ||
|
|
539f388e66 | ||
|
|
b03c312b4e | ||
|
|
71db5f9a79 | ||
|
|
e33e77595f | ||
|
|
d4016b310d | ||
|
|
a41177a110 | ||
|
|
f8600296f0 | ||
|
|
e1147fb33d | ||
|
|
ec0d020669 | ||
|
|
e8eab86546 | ||
|
|
c9ebedb01f | ||
|
|
af5742ce2d | ||
|
|
8f85c71654 | ||
|
|
70753c1082 | ||
|
|
2ab2b9efd6 | ||
|
|
dbb34b75fd | ||
|
|
7447492c76 | ||
|
|
492e4c33db | ||
|
|
cbb08bdcfc | ||
|
|
0de829741a | ||
|
|
0fa7aed1c4 | ||
|
|
3b0e940284 | ||
|
|
48a970ceb9 | ||
|
|
c7b533f21e | ||
|
|
839d4da191 | ||
|
|
b76ddfe93c | ||
|
|
4ba0156ad8 | ||
|
|
6ef9936e24 | ||
|
|
e835dbc7c6 | ||
|
|
5c8429a7bb | ||
|
|
824011657a | ||
|
|
8e2031bf5a | ||
|
|
8bb5f5475e | ||
|
|
eb4f249a54 | ||
|
|
3aa3b87709 | ||
|
|
0046157862 | ||
|
|
acbf1efad6 | ||
|
|
5d1eb45d5c | ||
|
|
c52c41c885 | ||
|
|
822f9aacf6 | ||
|
|
7d5608ca0e | ||
|
|
937bbef818 | ||
|
|
c506443ed0 | ||
|
|
5f42f26587 | ||
|
|
f84e96d790 | ||
|
|
ede57a20be | ||
|
|
df8306c721 | ||
|
|
1bb144e256 | ||
|
|
7d83603646 | ||
|
|
7e4d51bab0 | ||
|
|
4ef1b079cb | ||
|
|
e95d13adb1 | ||
|
|
92d7009804 | ||
|
|
354327e40b | ||
|
|
34d56c54c8 | ||
|
|
143a5fdf4f | ||
|
|
030510613c | ||
|
|
984b8e7da0 | ||
|
|
aba1ac1646 | ||
|
|
6b93d7644a | ||
|
|
68c737dcef | ||
|
|
b8f8ffe0a2 | ||
|
|
02e8681f76 | ||
|
|
603fb9db62 | ||
|
|
b01459d1ef | ||
|
|
667f1f5951 | ||
|
|
660c5a2db4 | ||
|
|
8a4d013354 | ||
|
|
faedca81d1 | ||
|
|
166d408f5e | ||
|
|
017f25d9d5 | ||
|
|
6a70eba83f | ||
|
|
ef13ab6938 | ||
|
|
75cb61fd36 | ||
|
|
9c31f8aa92 | ||
|
|
27a08da430 | ||
|
|
fdada9661f | ||
|
|
f1a52439bb | ||
|
|
d13849c173 | ||
|
|
d8a0e73899 | ||
|
|
b6a3387f8a | ||
|
|
04dd708941 | ||
|
|
39ffe1c95a | ||
|
|
7b5ae3c15b | ||
|
|
7678625071 | ||
|
|
dbe8817d95 | ||
|
|
09e117cd57 | ||
|
|
4b8779d8d3 | ||
|
|
79158dabbc | ||
|
|
f962bc8da5 | ||
|
|
28e8dde06d | ||
|
|
6c3d41a124 | ||
|
|
9c2967f8df | ||
|
|
99311364e7 | ||
|
|
16e86b8291 | ||
|
|
5741af8fd3 | ||
|
|
6bdd47a557 | ||
|
|
750ba93c14 | ||
|
|
18be930507 | ||
|
|
be26176828 | ||
|
|
540e01f1fe | ||
|
|
7cfb6b16e4 | ||
|
|
d5a8b9648f | ||
|
|
1657d7af7d | ||
|
|
318f1a2818 | ||
|
|
8b0b115709 | ||
|
|
7d2494b7f9 | ||
|
|
3b642ffeb3 | ||
|
|
815bf5f0e0 | ||
|
|
4af48724ce | ||
|
|
001272820c | ||
|
|
4aca0664ef | ||
|
|
6721a86768 | ||
|
|
5025b3690d | ||
|
|
42b37a718f | ||
|
|
fcdfd836ec | ||
|
|
b2f005239b | ||
|
|
bc3fa890bd | ||
|
|
95dbbf6e75 | ||
|
|
22f2ae7341 | ||
|
|
96202aa78f | ||
|
|
cef8a13cc2 | ||
|
|
54271231a0 | ||
|
|
d1bb651596 | ||
|
|
10b7f65437 | ||
|
|
3a70324d43 | ||
|
|
21b72cd174 | ||
|
|
ce8656f1b2 | ||
|
|
9277dc967b | ||
|
|
67b1e65d97 | ||
|
|
79fe9da2da | ||
|
|
3164e2698d | ||
|
|
ffae33e6f7 | ||
|
|
8dafe3b7a6 | ||
|
|
e23616169b | ||
|
|
9d7fc942f7 | ||
|
|
982d8cf19d | ||
|
|
aae460623d | ||
|
|
df14e5bcb6 | ||
|
|
15d5a83c16 | ||
|
|
4b0f7543ee | ||
|
|
362c64aa9c | ||
|
|
56c497cf2c | ||
|
|
e0dc2da80a | ||
|
|
41362f12ea | ||
|
|
3765c0b0fb | ||
|
|
d1f2b62f66 | ||
|
|
26b8c1d0a2 | ||
|
|
aa195f9f6f | ||
|
|
cd1d59264e | ||
|
|
923e009fd3 | ||
|
|
70a4fd99f4 | ||
|
|
feb4011f57 | ||
|
|
bf2a04ff66 | ||
|
|
e16d352e71 | ||
|
|
817724ce39 | ||
|
|
0fda117ffe | ||
|
|
e5be1e1f05 | ||
|
|
7d7af8cf59 | ||
|
|
cbc09f83d4 | ||
|
|
31913ae80e | ||
|
|
522632bea6 | ||
|
|
397f75a4db | ||
|
|
0bf84a321b | ||
|
|
5915208dee | ||
|
|
2167931e3f | ||
|
|
2d18171914 | ||
|
|
28f6a7d399 | ||
|
|
8e042435e5 | ||
|
|
cddf8712e4 | ||
|
|
88fd96e022 | ||
|
|
17e715f408 | ||
|
|
c821d9ea65 | ||
|
|
5cb8a9ddd0 | ||
|
|
f0131b2c39 | ||
|
|
bc3c68a3cb | ||
|
|
b81d387778 | ||
|
|
0afbe72672 | ||
|
|
5fc63485f3 | ||
|
|
1bc84c98f8 | ||
|
|
737af45c85 | ||
|
|
e08fbc7d86 | ||
|
|
91090d20de | ||
|
|
05a22e20b8 | ||
|
|
7c43f343a3 | ||
|
|
029e6aa251 | ||
|
|
2f26d4ffd3 | ||
|
|
d306bae035 | ||
|
|
34dc8b32cb | ||
|
|
fe38244d7c | ||
|
|
25e8c9f637 | ||
|
|
337b568b5e | ||
|
|
e696192ef7 | ||
|
|
928fc077b8 | ||
|
|
2a448b932c | ||
|
|
d40d23a50e | ||
|
|
c4548c4355 | ||
|
|
50fdea813b | ||
|
|
0ff163e541 | ||
|
|
38cd8847a2 | ||
|
|
e62ae9fb94 | ||
|
|
187b6cf072 | ||
|
|
a3fad5966a | ||
|
|
74350f3638 | ||
|
|
ee2c7caac2 | ||
|
|
c915a6912e | ||
|
|
d5cd232441 | ||
|
|
c4c14b0fc6 | ||
|
|
e5f5563249 | ||
|
|
2a6893efb4 | ||
|
|
9fd920945a | ||
|
|
a116daa7a1 | ||
|
|
0b8759380b | ||
|
|
00c5e3544f | ||
|
|
53a79e3170 | ||
|
|
5810bf9acd | ||
|
|
7815feddba | ||
|
|
f529a5eea8 | ||
|
|
40e9550b29 | ||
|
|
64d0202f95 | ||
|
|
da9483aa3a | ||
|
|
0221e555fe | ||
|
|
494ee2c099 | ||
|
|
dc92ad9c02 | ||
|
|
8a4a019973 | ||
|
|
a1a49ebf4b | ||
|
|
45263a6261 | ||
|
|
7ccede36b1 | ||
|
|
482e1631fa | ||
|
|
d0fe7f3e25 | ||
|
|
86083e3a92 | ||
|
|
281f567d97 | ||
|
|
7f887720cc | ||
|
|
717268add9 | ||
|
|
2f7e3b1596 | ||
|
|
1a3cd2f626 | ||
|
|
2fbea43a5c | ||
|
|
70788e932e | ||
|
|
949d12052e | ||
|
|
2af35673bb | ||
|
|
4a64ca58ac | ||
|
|
815c594fa6 | ||
|
|
fc4dba4569 | ||
|
|
1d8df98339 | ||
|
|
745aabcfd5 | ||
|
|
be100790f1 | ||
|
|
afc5e82906 | ||
|
|
2951d4b7fa | ||
|
|
dcd300211f | ||
|
|
f796d27bb4 | ||
|
|
47a2553356 | ||
|
|
67bf199254 | ||
|
|
4b1ed25ef3 | ||
|
|
a5ac43e5f3 | ||
|
|
0557527bfe | ||
|
|
dc1445e0d6 | ||
|
|
431401bc96 | ||
|
|
fded6c404a | ||
|
|
680a850618 | ||
|
|
7d08a7ba1a | ||
|
|
e9f603fa30 | ||
|
|
1a064e033b | ||
|
|
ebcfcf9bfa | ||
|
|
cf063b75ff | ||
|
|
951a9d7989 | ||
|
|
37191a53c3 | ||
|
|
42d420db6a | ||
|
|
122cf63a3f | ||
|
|
fafce60f9e | ||
|
|
e855f27c8c | ||
|
|
f01a593ff7 | ||
|
|
6af34b1c16 | ||
|
|
188b329eae | ||
|
|
7b0f71cc99 | ||
|
|
73f8baf464 | ||
|
|
3a09efde91 | ||
|
|
4efa8c1ef9 | ||
|
|
9f178208f2 | ||
|
|
0f2113ee12 | ||
|
|
139e003b0b | ||
|
|
1bf80cf315 | ||
|
|
bcf182f515 | ||
|
|
c34fb94e77 | ||
|
|
64080fadd3 | ||
|
|
1009cd6ed4 | ||
|
|
839c1af124 | ||
|
|
6b24842704 | ||
|
|
2ad8ae3451 | ||
|
|
b1597f6f61 | ||
|
|
47a49263ae | ||
|
|
9fed336c9e | ||
|
|
ac5b689950 | ||
|
|
3d43bb260a | ||
|
|
2eaeb26d4c | ||
|
|
34d0f8ec81 | ||
|
|
ddc1c7e98f | ||
|
|
e41475f672 | ||
|
|
e954622925 | ||
|
|
fae899e52e | ||
|
|
7b11f5afbc | ||
|
|
40c0bf79b1 | ||
|
|
df2df593b6 | ||
|
|
1dc59d610d | ||
|
|
8fc8decd09 | ||
|
|
08f44845ce | ||
|
|
4fbffc4788 | ||
|
|
8cf4050d3d | ||
|
|
31c18fc56d | ||
|
|
005207cfd6 | ||
|
|
c8d668ad96 | ||
|
|
a39c4ba9a4 | ||
|
|
f4deea05bf | ||
|
|
1c18133603 | ||
|
|
15566b4b65 | ||
|
|
737185752c | ||
|
|
a12e3534c6 | ||
|
|
51575b341b | ||
|
|
06b330ec51 | ||
|
|
e0a6bce5b0 | ||
|
|
da07a5b613 | ||
|
|
fbbca37b55 | ||
|
|
7fa1c2264e | ||
|
|
dcfbe53713 | ||
|
|
8b2993c979 | ||
|
|
131da660c8 | ||
|
|
f3de34a62b | ||
|
|
928d6bafcb | ||
|
|
704fb0cbd9 | ||
|
|
3be27aefa3 | ||
|
|
8be81ac09e | ||
|
|
e2fc9c3699 | ||
|
|
472eaff6f0 | ||
|
|
805b016aee | ||
|
|
624a13ba1a | ||
|
|
9692bf3991 | ||
|
|
3f5406f1d4 | ||
|
|
b684a62f8b | ||
|
|
6bb94db22f | ||
|
|
d70a971d2e | ||
|
|
b05b515f3d | ||
|
|
12b15e0037 | ||
|
|
2c9169308b | ||
|
|
74a3d50c88 | ||
|
|
6b8bb33221 | ||
|
|
175fb46508 | ||
|
|
2f65400641 | ||
|
|
b5f4f2fc4b | ||
|
|
8c34f44dae | ||
|
|
d97f46133b | ||
|
|
a94a5feb84 | ||
|
|
ead5d7511a | ||
|
|
1cd7446013 | ||
|
|
40747a463b | ||
|
|
8e20118ab6 | ||
|
|
19f330e670 | ||
|
|
20d549b5e1 | ||
|
|
e09fa89781 | ||
|
|
70b1421114 | ||
|
|
b80a30785a | ||
|
|
c3245ac301 | ||
|
|
085d5b411c | ||
|
|
8c26d6d0b5 | ||
|
|
76f5085e84 | ||
|
|
80d78e3270 | ||
|
|
654521b74c | ||
|
|
353d2d4cc9 | ||
|
|
b38d84419a | ||
|
|
0d085771e4 | ||
|
|
f3b2b6b41e | ||
|
|
fb35ad7ab3 | ||
|
|
cf6e721994 | ||
|
|
9faea5ff15 | ||
|
|
fe18009bc0 | ||
|
|
10f900ea3e | ||
|
|
63874694f8 | ||
|
|
9b9e246e66 | ||
|
|
e6a6eb9be5 | ||
|
|
050bb5dbfa | ||
|
|
b005ec1845 | ||
|
|
f158a94e0a | ||
|
|
a166620a6d | ||
|
|
47fb467878 | ||
|
|
67af80c772 | ||
|
|
1f44155127 | ||
|
|
87a3ac4a76 | ||
|
|
73354a04d5 | ||
|
|
cb3038e73c | ||
|
|
b6ec31fe11 | ||
|
|
c7059a60ee | ||
|
|
af49aaf8b1 | ||
|
|
4149b8388b | ||
|
|
df0d723714 | ||
|
|
44209ed93e | ||
|
|
930a3c8637 | ||
|
|
d2f5698397 | ||
|
|
42d9fc3284 | ||
|
|
91db494898 | ||
|
|
75217d4914 | ||
|
|
ceaf004719 | ||
|
|
27b7380433 | ||
|
|
7ca254ade9 | ||
|
|
c91864c2a7 | ||
|
|
4925297a35 | ||
|
|
d7ece50ed0 | ||
|
|
3ef4a9f413 | ||
|
|
9768b5b37a | ||
|
|
e4cd999bd0 | ||
|
|
8e335535cc | ||
|
|
2f53d98806 | ||
|
|
5d99331358 | ||
|
|
316022cde4 | ||
|
|
8d1abca8de | ||
|
|
1b2e73eaa2 | ||
|
|
6127371920 | ||
|
|
7936dbbf8c | ||
|
|
8aa45309a2 |
No files matched your search
@@ -0,0 +1,106 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make check C99
|
||||
run: make clean && make check -j 2 CC="cc -std=c99 -Wno-unknown-warning-option -Werror"
|
||||
- name: make check C11
|
||||
working-directory: ./tests
|
||||
run: make clean && make check -j 2 CC="cc -std=c11 -Wno-unknown-warning-option -Werror"
|
||||
- name: make check C++
|
||||
working-directory: ./tests
|
||||
run: make clean && make check -j 2 CC="c++ -std=c++11"
|
||||
|
||||
build_linux32:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: update package list
|
||||
run: sudo apt-get update
|
||||
- name: install multilib
|
||||
run: sudo apt-get install gcc-multilib g++-multilib
|
||||
- name: make check C99 (32bits)
|
||||
working-directory: ./tests
|
||||
run: make clean && make check -j 2 CC="gcc -m32 -std=c99 -Wno-unknown-warning-option -Werror"
|
||||
- name: make check C11 (32bits)
|
||||
working-directory: ./tests
|
||||
run: make clean && make check -j 2 CC="gcc -m32 -std=c11 -Wno-unknown-warning-option -Werror"
|
||||
- name: make check C++ (32 bits)
|
||||
working-directory: ./tests
|
||||
run: make clean && make check -j 2 CC="g++ -m32 -std=c++11 -Wno-unknown-warning-option "
|
||||
- name: make check against C++ STL
|
||||
working-directory: ./tests
|
||||
run: make clean && make check-stl -j 2
|
||||
|
||||
build_linux_tcc:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Clone TCC
|
||||
shell: bash
|
||||
run: git clone https://repo.or.cz/tinycc.git
|
||||
- name: Configure, build & install TCC
|
||||
shell: bash
|
||||
run: cd tinycc && ./configure --prefix=/tmp/tcc && make && make install
|
||||
- name: make depend
|
||||
shell: bash
|
||||
run: make depend
|
||||
- name: make check
|
||||
shell: bash
|
||||
run: make CC="/tmp/tcc/bin/tcc -std=c99" check
|
||||
|
||||
build_windows_mingw:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make check
|
||||
shell: bash
|
||||
run: mingw32-make -j 2 CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1" check
|
||||
|
||||
build_windows_msys2:
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
update: true
|
||||
install: git mingw-w64-x86_64-toolchain make
|
||||
- name: make check
|
||||
run: make -j 2 CC="gcc -std=c99 -D__USE_MINGW_ANSI_STDIO=1" check
|
||||
|
||||
build_windows_visual:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make check
|
||||
shell: cmd
|
||||
working-directory: ./tests
|
||||
run: Make-check-cl.bat cl
|
||||
|
||||
build_windows_clang_cl:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make check
|
||||
shell: cmd
|
||||
working-directory: ./tests
|
||||
run: Make-check-cl.bat clang-cl
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Override automatic language detection by changing the below list
|
||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||
language: ['cpp']
|
||||
# Learn more...
|
||||
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
- name: Autobuild
|
||||
working-directory: ./tests
|
||||
run: make exe
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -36,3 +36,10 @@ depend
|
||||
|
||||
# Others
|
||||
other/
|
||||
a*.dat
|
||||
config
|
||||
*.log
|
||||
.vscode
|
||||
.vs
|
||||
tmp-serial.*
|
||||
*~
|
||||
@@ -1,6 +1,6 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2017-2018, Patrick Pelissier
|
||||
Copyright (c) 2017-2021, Patrick Pelissier
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -1,8 +1,49 @@
|
||||
PREFIX=/usr/local
|
||||
# Copyright (c) 2017-2021, Patrick Pelissier
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# + Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# + Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Environnement variable. To customize if needed.
|
||||
RM=rm -f
|
||||
MKDIR=mkdir -p
|
||||
RMDIR=rmdir
|
||||
INSTALL=install
|
||||
INSTALL_PROGRAM=${INSTALL}
|
||||
INSTALL_DATA=${INSTALL} -m 644
|
||||
|
||||
.PHONY: all test check doc clean distclean depend depend.png install
|
||||
# Installation directory
|
||||
PREFIX=/usr/local
|
||||
DESTDIR=
|
||||
|
||||
# Package name & version.
|
||||
PACKAGE=m_lib-$(VERSION)
|
||||
VERSION=0.6.0
|
||||
|
||||
# Define the contain of the distribution tarball
|
||||
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-c-mempool.h m-concurrent.h m-core.h m-deque.h m-dict.h m-funcobj.h m-genint.h m-i-list.h m-i-shared.h m-list.h m-mempool.h m-mutex.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-shared.h m-snapshot.h m-string.h m-tuple.h m-variant.h m-worker.h
|
||||
DOC1=LICENSE README.md
|
||||
DOC2=doc/API.txt doc/Container.html doc/Container.ods doc/depend.png doc/DEV.md doc/ISSUES.org doc/oplist.odp doc/oplist.png
|
||||
EXAMPLE=example/ex11-algo01.c example/ex11-algo02.c example/ex11-json01.c example/ex11-section.c example/ex-algo02.c example/ex-algo03.c example/ex-algo04.c example/ex-array00.c example/ex-array01.c example/ex-array02.c example/ex-array03.c example/ex-array04.c example/ex-array05.c example/ex-bptree01.c example/ex-buffer01.c example/ex-dict01.c example/ex-dict02.c example/ex-dict03.c example/ex-dict04.c example/ex-grep01.c example/ex-list01.c example/ex-mph.c example/ex-multi01.c example/ex-multi02.c example/ex-multi03.c example/ex-multi04.c example/ex-multi05.c example/ex-rbtree01.c example/ex11-algo02.json example/ex11-json01.json example/Makefile example/ex-defer01.c example/ex-string01.c example/ex-string02.c example/ex-astar.c example/ex-string03.c example/ex11-tstc.c
|
||||
TEST=tests/test-malgo.c tests/test-marray.c tests/test-mbitset.c tests/test-mbptree.c tests/test-mbuffer.c tests/test-mcmempool.c tests/test-mconcurrent.c tests/test-mcore.c tests/test-mdeque.c tests/test-mdict.c tests/test-mfuncobj.c tests/test-mgenint.c tests/test-milist.c tests/test-mlist.c tests/test-mmempool.c tests/test-mmutex.c tests/test-mprioqueue.c tests/test-mrbtree.c tests/test-mserial-bin.c tests/test-mserial-json.c tests/test-mshared.c tests/test-msnapshot.c tests/test-mstring.c tests/test-mtuple.c tests/test-mvariant.c tests/test-mworker.c tests/tgen-bitset.c tests/tgen-marray.c tests/tgen-mdict.c tests/tgen-mlist.c tests/tgen-mstring.c tests/tgen-openmp.c tests/tgen-queue.c tests/tgen-shared.c tests/tgen-mserial.c tests/Makefile tests/coverage.h tests/test-obj.h tests/dict.txt tests/fail-chain-oplist.c tests/fail-incompatible.c tests/fail-no-oplist.c tests/test-mishared.c tests/check-array.cpp tests/check-deque.cpp tests/check-dplist.cpp tests/check-list.cpp tests/check-rbtree.cpp tests/check-uset.cpp tests/check-generic.hpp
|
||||
|
||||
.PHONY: all test check doc clean distclean depend install uninstall dist
|
||||
|
||||
all:
|
||||
@echo "Nothing to be done."
|
||||
@@ -13,25 +54,65 @@ check:
|
||||
cd tests && $(MAKE) check
|
||||
cd example && $(MAKE) all
|
||||
|
||||
doc: README.md
|
||||
checkall:
|
||||
cd tests && $(MAKE) checkall
|
||||
cd example && $(MAKE) all
|
||||
|
||||
html: README.md
|
||||
markdown < README.md > README.html
|
||||
|
||||
doc: html doc/depend.png
|
||||
|
||||
clean:
|
||||
cd tests && $(MAKE) clean
|
||||
cd example && $(MAKE) clean
|
||||
cd bench && $(MAKE) clean
|
||||
$(RM) -f README.html depend.dot
|
||||
$(RM) README.html depend.dot
|
||||
$(RM) -r 'm*lib-$(VERSION)'
|
||||
$(RM) 'm*lib-$(VERSION).tar.bz2'
|
||||
|
||||
distclean:
|
||||
distclean: clean
|
||||
cd tests && $(MAKE) distclean
|
||||
|
||||
depend:
|
||||
cd tests && $(MAKE) depend
|
||||
|
||||
depend.png:
|
||||
doc/depend.png: $(HEADER)
|
||||
(echo "digraph g { " ; for i in *.h ; do list=$$(grep "include \"" $$i |cut -f2 -d\") ; for j in $$list ; do echo "\"$$i\" -> \"$$j\" ;" ; done ; done ; echo "}" )> depend.dot
|
||||
dot -Tpng depend.dot -o depend.png
|
||||
dot -Tpng depend.dot -o doc/depend.png
|
||||
optipng -o7 doc/depend.png
|
||||
|
||||
dist: $(HEADER) $(DOC1) $(DOC2) $(EXAMPLE) $(TEST) Makefile clean versioncheck
|
||||
$(MKDIR) '$(PACKAGE)'
|
||||
$(MKDIR) '$(PACKAGE)/doc'
|
||||
$(MKDIR) '$(PACKAGE)/tests'
|
||||
$(MKDIR) '$(PACKAGE)/example'
|
||||
cp $(HEADER) $(DOC1) Makefile '$(PACKAGE)'
|
||||
cp $(EXAMPLE) '$(PACKAGE)/example'
|
||||
cp $(TEST) '$(PACKAGE)/tests'
|
||||
cp $(DOC2) '$(PACKAGE)/doc'
|
||||
cd '$(PACKAGE)/tests' && $(MAKE) depend
|
||||
tar jcf '$(PACKAGE).tar.bz2' '$(PACKAGE)'
|
||||
|
||||
distcheck: dist
|
||||
cd '$(PACKAGE)' && make check
|
||||
|
||||
versioncheck:
|
||||
@if test "$(VERSION)." != `grep M_CORE_VERSION m-core.h |awk '{printf "%d.",$$3 } END {printf "\n"}'` ; then echo "ERROR: Version mismatch between Makefile & HEADERS" ; exit 2 ; fi
|
||||
@for i in *.h ; do echo $(HEADER) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing header $$i in Makefile" ; exit 3 ; fi ; done
|
||||
@for i in doc/* ; do echo $(DOC2) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing document $$i in Makefile" ; exit 4 ; fi ; done
|
||||
@for i in tests/*.c tests/*.txt tests/*.h ; do echo $(TEST) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing test $$i in Makefile" ; exit 5 ; fi ; done
|
||||
@for i in example/*.c example/*.json ; do echo $(EXAMPLE) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing example $$i in Makefile" ; exit 6 ; fi ; done
|
||||
@for i in tests/*.cpp tests/*.hpp ; do echo $(TEST) | grep -q $$i ; if test $$? -ne 0 ; then echo "ERROR: Missing test $$i in Makefile" ; exit 7 ; fi ; done
|
||||
|
||||
install:
|
||||
$(MKDIR) $(DESTDIR)$(PREFIX)/include
|
||||
cp m-*.h $(DESTDIR)$(PREFIX)/include
|
||||
$(MKDIR) $(DESTDIR)$(PREFIX)/include/m-lib
|
||||
$(INSTALL_DATA) $(HEADER) $(DESTDIR)$(PREFIX)/include/m-lib
|
||||
|
||||
uninstall:
|
||||
for i in $(HEADER) ; do $(RM) $(DESTDIR)$(PREFIX)/include/m-lib/$$i ; done
|
||||
$(RMDIR) $(DESTDIR)$(PREFIX)/include/m-lib/
|
||||
|
||||
format:
|
||||
cd example && $(MAKE) ex-string01.exe
|
||||
for i in $(HEADER) ; do ./example/ex-string01.exe $$i > tmp.h && mv tmp.h $$i ; done
|
||||
+172
-48
@@ -1,20 +1,48 @@
|
||||
# Copyright (c) 2017-2021, Patrick Pelissier
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# + Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# + Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
CC=cc -std=c11
|
||||
CXX=c++ -std=c++11
|
||||
CFLAGS=-O2 -march=native -Wall
|
||||
XCFLAGS=
|
||||
CPPFLAGS=-I..
|
||||
N=4
|
||||
BENCH_DEF=
|
||||
RM=rm -rf
|
||||
|
||||
.PHONY: all container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain
|
||||
.PHONY: all pgo container queue mempool string plain bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-emilib bench-hopscotchmap bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-mempool bench-string bench-plain bench-rigtorp-mpmc-queue bench-cmc
|
||||
|
||||
all: container queue mempool string plain
|
||||
|
||||
pgo:
|
||||
$(MAKE) all XCFLAGS="-fprofile-generate=pgo"
|
||||
$(MAKE) all XCFLAGS="-fprofile-use=pgo"
|
||||
|
||||
#########################################################################
|
||||
|
||||
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-sparsepp bench-collectionc bench-tommyds bench-flathashmap bench-hopscotchmap
|
||||
container: bench-mlib bench-mlib-mempool bench-stl bench-qt bench-glib bench-klib bench-libdynamic bench-cmc bench-sparsepp bench-densehashmap bench-collectionc bench-tommyds bench-flathashmap bench-emilib bench-hopscotchmap bench-uthash bench-qlibc bench-libsrt bench-nedtries bench-rigtorp-hashmap bench-ctl
|
||||
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
|
||||
queue: bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost
|
||||
queue: bench-mlib-thread bench-liblfds bench-concurrentqueue bench-boost bench-rigtorp-mpmc-queue
|
||||
@echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
|
||||
|
||||
mempool: bench-mempool
|
||||
@@ -32,42 +60,56 @@ plain: bench-plain
|
||||
|
||||
|
||||
bench-mlib:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -pthread bench-mlib.c -o bench-mlib.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -pthread bench-mlib.c common.c -o bench-mlib.exe
|
||||
@./bench-mlib.exe 10
|
||||
@./bench-mlib.exe 11
|
||||
@./bench-mlib.exe 20
|
||||
@./bench-mlib.exe 30
|
||||
@./bench-mlib.exe 31
|
||||
@./bench-mlib.exe 40
|
||||
@./bench-mlib.exe 41
|
||||
@./bench-mlib.exe 42
|
||||
@./bench-mlib.exe 46
|
||||
@./bench-mlib.exe 47
|
||||
@./bench-mlib.exe 43
|
||||
@./bench-mlib.exe 50
|
||||
@./bench-mlib.exe 51
|
||||
|
||||
bench-mlib-mempool:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c -DUSE_MEMPOOL -pthread -o bench-mlib-mempool.exe
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c common.c -DUSE_MEMPOOL -pthread -o bench-mlib-mempool.exe
|
||||
@./bench-mlib-mempool.exe 10
|
||||
@./bench-mlib-mempool.exe 11
|
||||
@./bench-mlib-mempool.exe 20
|
||||
@./bench-mlib-mempool.exe 30
|
||||
@./bench-mlib-mempool.exe 31
|
||||
@./bench-mlib-mempool.exe 40
|
||||
@./bench-mlib-mempool.exe 41
|
||||
@./bench-mlib-mempool.exe 42
|
||||
@./bench-mlib-mempool.exe 46
|
||||
@./bench-mlib-mempool.exe 47
|
||||
@./bench-mlib-mempool.exe 43
|
||||
@./bench-mlib-mempool.exe 50
|
||||
@./bench-mlib-mempool.exe 51
|
||||
|
||||
bench-mlib-thread:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c -DMULTI_THREAD_MEASURE -pthread -o bench-mlib-thread.exe
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-mlib.c common.c -DMULTI_THREAD_MEASURE -pthread -o bench-mlib-thread.exe
|
||||
@./bench-mlib-thread.exe 60
|
||||
@./bench-mlib-thread.exe 61
|
||||
@./bench-mlib-thread.exe 62
|
||||
@./bench-mlib-thread.exe 63
|
||||
@./bench-mlib-thread.exe 64
|
||||
@./bench-mlib-thread.exe 65
|
||||
@./bench-mlib-thread.exe 66
|
||||
|
||||
bench-stl:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) bench-stl.cpp -o bench-stl.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-stl.cpp common.c -o bench-stl.exe
|
||||
@./bench-stl.exe 10
|
||||
@./bench-stl.exe 20
|
||||
@./bench-stl.exe 30
|
||||
@./bench-stl.exe 40
|
||||
@./bench-stl.exe 41
|
||||
@./bench-stl.exe 42
|
||||
@./bench-stl.exe 46
|
||||
@./bench-stl.exe 43
|
||||
@./bench-stl.exe 50
|
||||
@./bench-stl.exe 51
|
||||
@@ -76,7 +118,7 @@ bench-stl:
|
||||
bench-qt:
|
||||
@if test -n "$${QT}" ; then $(MAKE) bench-qt0 ; else echo "Nothing to be done for QT." ; fi
|
||||
bench-qt0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp -o bench-qt.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -fPIC `PKG_CONFIG_PATH="$${QT}" pkg-config --libs --cflags Qt5Core` bench-qt.cpp common.c -o bench-qt.exe
|
||||
@./bench-qt.exe 10
|
||||
@./bench-qt.exe 20
|
||||
@./bench-qt.exe 30
|
||||
@@ -89,7 +131,7 @@ bench-qt0:
|
||||
bench-glib:
|
||||
@if test -n "$${GLIB}" ; then $(MAKE) bench-glib0 ; else echo "Nothing to be done for GLIB." ; fi
|
||||
bench-glib0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-glib.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-glib.c common.c -o bench-glib.exe `PKG_CONFIG_PATH="$${GLIB}" pkg-config --libs --cflags glib-2.0`
|
||||
@./bench-glib.exe 10
|
||||
@./bench-glib.exe 20
|
||||
@./bench-glib.exe 30
|
||||
@@ -102,7 +144,7 @@ bench-klib:
|
||||
@if test -n "$${KLIB}" ; then $(MAKE) bench-klib0 ; else echo "Nothing to be done for KLIB." ; fi
|
||||
|
||||
bench-klib0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c -o bench-klib.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${KLIB} bench-klib.c common.c -o bench-klib.exe
|
||||
@./bench-klib.exe 10
|
||||
@./bench-klib.exe 20
|
||||
@./bench-klib.exe 30
|
||||
@@ -110,25 +152,32 @@ bench-klib0:
|
||||
@./bench-klib.exe 41
|
||||
@./bench-klib.exe 50
|
||||
|
||||
# CMC shall point to the directory where the CMC main header is
|
||||
bench-cmc:
|
||||
@if test -n "$${CMC}" ; then $(MAKE) bench-cmc0 ; else echo "Nothing to be done for CMC." ; fi
|
||||
|
||||
bench-cmc0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CMC} bench-cmc.c common.c -o bench-cmc.exe
|
||||
@./bench-cmc.exe 20
|
||||
@./bench-cmc.exe 30
|
||||
@./bench-cmc.exe 40
|
||||
|
||||
# LIBDYNAMIC shall point to the the directory containing the file libdynamic.pc
|
||||
bench-libdynamic:
|
||||
@if test -n "$${LIBDYNAMIC}" ; then $(MAKE) bench-libdynamic0 ; else echo "Nothing to be done for LIBDYNAMIC." ; fi
|
||||
|
||||
bench-libdynamic0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-libdynamic.c -o bench-libdynamic.exe `PKG_CONFIG_PATH="$${LIBDYNAMIC}" pkg-config --libs --cflags libdynamic`
|
||||
@./bench-libdynamic.exe 10
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-libdynamic.c common.c -o bench-libdynamic.exe `PKG_CONFIG_PATH="$${LIBDYNAMIC}" pkg-config --libs --cflags libdynamic`
|
||||
@./bench-libdynamic.exe 20
|
||||
@./bench-libdynamic.exe 30
|
||||
@./bench-libdynamic.exe 40
|
||||
@./bench-libdynamic.exe 41
|
||||
@./bench-libdynamic.exe 50
|
||||
|
||||
# SPARSEPP shall point to the directory where the headers are
|
||||
bench-sparsepp:
|
||||
@if test -n "$${SPARSEPP}" ; then $(MAKE) bench-sparsepp0 ; else echo "Nothing to be done for SPARSEPP." ; fi
|
||||
|
||||
bench-sparsepp0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp -o bench-sparsepp.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${SPARSEPP}" -I "$${SPARSEPP}/.." bench-sparsepp.cpp common.c -o bench-sparsepp.exe
|
||||
@./bench-sparsepp.exe 41
|
||||
@./bench-sparsepp.exe 42
|
||||
@./bench-sparsepp.exe 43
|
||||
@@ -138,17 +187,17 @@ bench-densehashmap:
|
||||
@if test -n "$${DENSEHASHMAP}" ; then $(MAKE) bench-densehashmap0 ; else echo "Nothing to be done Google DENSEHASHMAP." ; fi
|
||||
|
||||
bench-densehashmap0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp -o bench-densehashmap.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${DENSEHASHMAP}" -I "$${DENSEHASHMAP}/.." bench-densehashmap.cpp common.c -o bench-densehashmap.exe
|
||||
@./bench-densehashmap.exe 41
|
||||
@./bench-densehashmap.exe 42
|
||||
@./bench-densehashmap.exe 43
|
||||
|
||||
# COLLECTIONC shall point to the directory where the 'src' directory is.
|
||||
# COLLECTIONC shall point to the directory where it is installed
|
||||
bench-collectionc:
|
||||
@if test -n "$${COLLECTIONC}" ; then $(MAKE) bench-collectionc0 ; else echo "Nothing to be done for COLLECTIONC." ; fi
|
||||
|
||||
bench-collectionc0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include" -L "$${COLLECTIONC}" -static bench-collectionc.c -lcollectc -o bench-collectionc.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${COLLECTIONC}/include/collectc" bench-collectionc.c common.c "$${COLLECTIONC}/lib/libcollectc.a" -o bench-collectionc.exe
|
||||
@./bench-collectionc.exe 10
|
||||
@./bench-collectionc.exe 20
|
||||
@./bench-collectionc.exe 30
|
||||
@@ -161,7 +210,7 @@ bench-tommyds:
|
||||
@if test -n "$${TOMMYDS}" ; then $(MAKE) bench-tommyds0 ; else echo "Nothing to be done for TOMMYDS." ; fi
|
||||
|
||||
bench-tommyds0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c -o bench-tommyds.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${TOMMYDS}" bench-tommyds.c $${TOMMYDS}/tommyds/tommyarray.c $${TOMMYDS}/tommyds/tommylist.c $${TOMMYDS}/tommyds/tommytree.c $${TOMMYDS}/tommyds/tommyhashlin.c common.c -o bench-tommyds.exe
|
||||
@./bench-tommyds.exe 10
|
||||
@./bench-tommyds.exe 20
|
||||
@./bench-tommyds.exe 30
|
||||
@@ -173,17 +222,29 @@ bench-flathashmap:
|
||||
@if test -n "$${FLATHASHMAP}" ; then $(MAKE) bench-flathashmap0 ; else echo "Nothing to be done for FLATHASHMAP." ; fi
|
||||
|
||||
bench-flathashmap0:
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp -o bench-flathashmap.exe
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${FLATHASHMAP}" bench-flathashmap.cpp common.c -o bench-flathashmap.exe
|
||||
@./bench-flathashmap.exe 41
|
||||
@./bench-flathashmap.exe 42
|
||||
@./bench-flathashmap.exe 46
|
||||
@./bench-flathashmap.exe 43
|
||||
|
||||
# EMILIB shall point to the directory where the headers are
|
||||
bench-emilib:
|
||||
@if test -n "$${EMILIB}" ; then $(MAKE) bench-emilib0 ; else echo "Nothing to be done for EMILIB." ; fi
|
||||
|
||||
bench-emilib0:
|
||||
$(CXX) -std=c++14 $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${EMILIB}" bench-emilib.cpp common.c -o bench-emilib.exe
|
||||
@./bench-emilib.exe 41
|
||||
@./bench-emilib.exe 42
|
||||
@./bench-emilib.exe 46
|
||||
@./bench-emilib.exe 43
|
||||
|
||||
# HOPSCOTCHMAP shall point to the directory where the source are
|
||||
bench-hopscotchmap:
|
||||
@if test -n "$${HOPSCOTCHMAP}" ; then $(MAKE) bench-hopscotchmap0 ; else echo "Nothing to be done for HOPSCOTCHMAP." ; fi
|
||||
|
||||
bench-hopscotchmap0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}" bench-hopscotchmap.cpp -o bench-hopscotchmap.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${HOPSCOTCHMAP}/include/tsl" bench-hopscotchmap.cpp common.c -o bench-hopscotchmap.exe
|
||||
@./bench-hopscotchmap.exe 41
|
||||
@./bench-hopscotchmap.exe 42
|
||||
@./bench-hopscotchmap.exe 43
|
||||
@@ -193,12 +254,11 @@ bench-uthash:
|
||||
@if test -n "$${UTHASH}" ; then $(MAKE) bench-uthash0 ; else echo "Nothing to be done for UTHASH." ; fi
|
||||
|
||||
bench-uthash0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c -o bench-uthash.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${UTHASH}" bench-uthash.c common.c -o bench-uthash.exe
|
||||
@./bench-uthash.exe 10
|
||||
@./bench-uthash.exe 20
|
||||
@./bench-uthash.exe 40
|
||||
@./bench-uthash.exe 41
|
||||
@./bench-uthash.exe 42
|
||||
@./bench-uthash.exe 43
|
||||
@./bench-uthash.exe 50
|
||||
|
||||
# QLIBC shall point to the directory where the library is installed.
|
||||
@@ -206,24 +266,18 @@ bench-qlibc:
|
||||
@if test -n "$${QLIBC}" ; then $(MAKE) bench-qlibc0 ; else echo "Nothing to be done for QLIBC." ; fi
|
||||
|
||||
bench-qlibc0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c -o bench-qlibc.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${QLIBC}/include" -L "$${QLIBC}/lib" -lqlibc -pthread bench-qlibc.c common.c -o bench-qlibc.exe
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 10
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 20
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 30
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 42
|
||||
@LD_LIBRARY_PATH="$${QLIBC}/lib:$${LD_LIBRARY_PATH}" ./bench-qlibc.exe 41
|
||||
|
||||
# LIBSRT shall point to the directory where the source are
|
||||
# LIBSRT shall point to the directory where it is installed
|
||||
bench-libsrt:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-libsrt0 ; else echo "Nothing to be done for LIBSRT." ; fi
|
||||
|
||||
bench-libsrt0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/src" bench-libsrt.c -o bench-libsrt.exe $${LIBSRT}/libsrt.a
|
||||
@./bench-libsrt.exe 10
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBSRT}/include/libsrt" bench-libsrt.c common.c -o bench-libsrt.exe $${LIBSRT}/lib/libsrt.a
|
||||
@./bench-libsrt.exe 20
|
||||
@./bench-libsrt.exe 41
|
||||
@./bench-libsrt.exe 42
|
||||
@./bench-libsrt.exe 43
|
||||
@./bench-libsrt.exe 50
|
||||
|
||||
|
||||
@@ -232,7 +286,7 @@ bench-libsrt0:
|
||||
bench-liblfds:
|
||||
@if test -n "$${LIBLFDS}" ; then $(MAKE) bench-liblfds0 ; else echo "Nothing to be done for LIBLFDS." ; fi
|
||||
bench-liblfds0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.so -lpthread
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${LIBLFDS}/inc" -Wno-unknown-pragmas bench-liblfds.c common.c -DMULTI_THREAD_MEASURE -o bench-liblfds.exe $${LIBLFDS}/bin/liblfds711.a -lpthread
|
||||
@LD_LIBRARY_PATH="$${LIBLFDS}/bin:$${LD_LIBRARY_PATH}" ./bench-liblfds.exe 60
|
||||
|
||||
|
||||
@@ -241,36 +295,81 @@ bench-liblfds0:
|
||||
bench-concurrentqueue:
|
||||
@if test -n "$${CONCURRENTQUEUE}" ; then $(MAKE) bench-concurrentqueue0 ; else echo "Nothing to be done for CONCURRENTQUEUE." ; fi
|
||||
bench-concurrentqueue0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp -o bench-concurrentqueue.exe -lpthread
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${CONCURRENTQUEUE}/" bench-concurrentqueue.cpp common.c -DMULTI_THREAD_MEASURE -o bench-concurrentqueue.exe -lpthread
|
||||
@./bench-concurrentqueue.exe 60
|
||||
|
||||
|
||||
# XXHASH shall point to the source directory of xxhash
|
||||
# The library shall be built.
|
||||
bench-xxhash:
|
||||
@if test -n "$${XXHASH}" ; then $(MAKE) bench-xxhash0 ; else echo "Nothing to be done for XXHASH." ; fi
|
||||
bench-xxhash0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${XXHASH}/" bench-xxhash.c common.c "$${XXHASH}/libxxhash.a" -o bench-xxhash.exe
|
||||
@./bench-xxhash.exe 70
|
||||
|
||||
# BOOST shall point to the header location.
|
||||
bench-boost:
|
||||
@if test -n "$${BOOST}" ; then $(MAKE) bench-boost0 ; else echo "Nothing to be done for BOOST." ; fi
|
||||
bench-boost0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp -o bench-boost.exe -lpthread
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${BOOST}" bench-boost.cpp common.c -DMULTI_THREAD_MEASURE -o bench-boost.exe -lpthread
|
||||
@./bench-boost.exe 60
|
||||
|
||||
# NEDTRIES shall point to the header location.
|
||||
bench-nedtries:
|
||||
@if test -n "$${NEDTRIES}" ; then $(MAKE) bench-nedtries0 ; else echo "Nothing to be done for NEDTRIES." ; fi
|
||||
bench-nedtries0:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c -o bench-nedtries.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${NEDTRIES}" bench-nedtries.c common.c -o bench-nedtries.exe
|
||||
@./bench-nedtries.exe 40
|
||||
@./bench-nedtries.exe 45
|
||||
|
||||
# RIGTORP_HASHMAP shall point to the directory where the headers are
|
||||
bench-rigtorp-hashmap:
|
||||
@if test -n "$${RIGTORP_HASHMAP}" ; then $(MAKE) bench-rigtorp-hashmap0 ; else echo "Nothing to be done for RIGTORP_HASHMAP." ; fi
|
||||
|
||||
bench-rigtorp-hashmap0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RIGTORP_HASHMAP}/include" bench-rigtorp-hashmap.cpp common.c -o bench-rigtorp-hashmap.exe
|
||||
@./bench-rigtorp-hashmap.exe 42
|
||||
@./bench-rigtorp-hashmap.exe 41
|
||||
@./bench-rigtorp-hashmap.exe 43
|
||||
|
||||
# RIGTORP_MPMC_QUEUE shall point to the directory where the headers are
|
||||
bench-rigtorp-mpmc-queue:
|
||||
@if test -n "$${RIGTORP_MPMC_QUEUE}" ; then $(MAKE) bench-rigtorp-mpmc-queue0 ; else echo "Nothing to be done for RIGTORP_MPMC_QUEUE." ; fi
|
||||
bench-rigtorp-mpmc-queue0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RIGTORP_MPMC_QUEUE}" bench-rigtorp-mpmcqueue.cpp common.c -DMULTI_THREAD_MEASURE -o bench-mpmcqueue.exe -lpthread
|
||||
@./bench-mpmcqueue.exe 60
|
||||
|
||||
# CTL shall point to the source directory (not where the headers are)
|
||||
bench-ctl:
|
||||
@if test -n "$${CTL}" ; then $(MAKE) bench-ctl0 ; else echo "Nothing to be done for CTL." ; fi
|
||||
|
||||
bench-ctl0:
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I $${CTL} bench-ctl.c common.c -o bench-ctl.exe
|
||||
@./bench-ctl.exe 10
|
||||
@./bench-ctl.exe 20
|
||||
@./bench-ctl.exe 30
|
||||
@./bench-ctl.exe 40
|
||||
@./bench-ctl.exe 50
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-mlib-json: bench-mlib
|
||||
@./bench-mlib.exe 110
|
||||
@./bench-mlib.exe 111
|
||||
@./bench-mlib.exe 112
|
||||
|
||||
bench-rapidjson:
|
||||
@if test -n "$${RAPIDJSON}" ; then $(MAKE) bench-rapidjson0 ; else echo "Nothing to be done for RAPIDJSON." ; fi
|
||||
bench-rapidjson0:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -I "$${RAPIDJSON}/" bench-rapidjson.cpp common.c -o bench-rapidjson.exe
|
||||
@./bench-rapidjson.exe 110
|
||||
@./bench-rapidjson.exe 111
|
||||
@./bench-rapidjson.exe 112
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-plain:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) bench-plain-c.c -o bench-plain-c.exe
|
||||
@./bench-plain-c.exe
|
||||
|
||||
############################################################################
|
||||
@@ -278,34 +377,59 @@ bench-plain:
|
||||
bench-mempool:
|
||||
@if test -n "$${BOOST}" ; then if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_BOOST -DUSE_GLIB `pkg-config --libs --cflags glib-2.0` -lboost_system" ; else make bench-mempool0 BENCH_DEF="-DUSE_BOOST -lboost_system" ; fi ; else if test -n "$${GLIB}" ; then make bench-mempool0 BENCH_DEF="-DUSE_GLIB `pkg-config --libs --cflags glib-2.0`" ; else make bench-mempool0 ; fi ; fi
|
||||
bench-mempool0:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) pool.cc $(BENCH_DEF) -o bench-mempool.exe
|
||||
@./bench-mempool.exe
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-maxdict:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
|
||||
$(CC) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) max-dict.c -o bench-max-dict.exe
|
||||
@./bench-max-dict.exe
|
||||
|
||||
############################################################################
|
||||
|
||||
# BSTRLIB shall point to the BSTRLIB sources
|
||||
# LIBSRT shall point to the LIBSRT sources
|
||||
# LIBSRT shall point to the LIBSRT install directory
|
||||
bench-string:
|
||||
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-with BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-with ; fi
|
||||
@if test -n "$${BSTRLIB}" ; then $(MAKE) bench-string-step1 BENCH_DEF="-DBENCH_CAN_USE_BSTRLIB -I ${BSTRLIB} $${BSTRLIB}/obj/bstrlib.o $${BSTRLIB}/obj/bstraux.o $${BSTRLIB}/obj/bstrwrap.o" ; else $(MAKE) bench-string-step1 ; fi
|
||||
|
||||
bench-string-with:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/src $${LIBSRT}/libsrt.a" ; else $(MAKE) bench-string-exe BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
bench-string-step1:
|
||||
@if test -n "$${LIBSRT}" ; then $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_LIBSRT -I$${LIBSRT}/include/libsrt $${LIBSRT}/lib/libsrt.a" ; else $(MAKE) bench-string-step2 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-exe:
|
||||
$(CXX) $(CFLAGS) $(CPPFLAGS) -I "$${BSTRLIB}" -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp -o bench-string.exe $(BENCH_DEF)
|
||||
#NOTE: Need to build by hand sds.o since it is not build by default and sds.c is not compatible with C++
|
||||
bench-string-step2:
|
||||
@if test -n "$${SDS}" ; then $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF) -fpermissive -DBENCH_CAN_USE_SDS -I$${SDS}/ $${SDS}/sds.o" ; else $(MAKE) bench-string-step4 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-step4:
|
||||
@if test -n "$${GLIB}" ; then $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF) -DBENCH_CAN_USE_GLIB $$(pkg-config --cflags --libs glib-2.0)" ; else $(MAKE) bench-string-step5 BENCH_DEF="$(BENCH_DEF)"; fi
|
||||
|
||||
bench-string-step5:
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string.cpp common.c $(BENCH_DEF) -o bench-string.exe
|
||||
$(CXX) $(CFLAGS) $(XCFLAGS) $(CPPFLAGS) -DBENCH_CAN_USE_STL -DBENCH_CAN_USE_MSTARLIB bench-string-2.cpp common.c $(BENCH_DEF) -o bench-string-2.exe
|
||||
@./bench-string.exe
|
||||
@./bench-string-2.exe 2000000 0
|
||||
@./bench-string-2.exe 2000000 1
|
||||
@./bench-string-2.exe 2000000 2
|
||||
@./bench-string-2.exe 2000000 4
|
||||
|
||||
bench-string-pgo:
|
||||
$(MAKE) bench-string XCFLAGS="-fprofile-generate=pgo"
|
||||
$(MAKE) bench-string XCFLAGS="-fprofile-use=pgo"
|
||||
|
||||
############################################################################
|
||||
|
||||
bench-find-sort:
|
||||
g++ bench-find-sort.cpp -O2 -march=native -std=c++11 -I.. -g common.c -o bench-find-sort.exe
|
||||
./bench-find-sort.exe 10000000
|
||||
|
||||
############################################################################
|
||||
|
||||
clean:
|
||||
rm -f *.o bench-*.exe bench-*.s *~ massif.out.*
|
||||
$(RM) *.o bench-*.exe
|
||||
$(RM) bench-*.s *~ massif.out.*
|
||||
$(RM) plot-*.dat
|
||||
$(RM) pgo
|
||||
$(RM) tmp-serial.*
|
||||
cd nucleotide && $(MAKE) clean
|
||||
|
||||
############################################################################
|
||||
|
||||
@@ -6,11 +6,8 @@
|
||||
|
||||
#include <boost/lockfree/queue.hpp>
|
||||
|
||||
#include "m-mutex.h"
|
||||
#define MULTI_THREAD_MEASURE
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
boost::lockfree::queue<unsigned int, boost::lockfree::capacity<4*64>> g_buff;
|
||||
boost::lockfree::queue<unsigned long long, boost::lockfree::capacity<4*64>> g_final;
|
||||
@@ -51,7 +48,7 @@ static void prod(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void test_buffer(size_t n)
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
@@ -88,11 +85,12 @@ static void test_buffer(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
test("BOOST-LOCKFREE", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "macro_collections.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// CMC list are dynamic array
|
||||
#define MY_LIST_PARAMS (list_uint, list_uint_s, , , unsigned int)
|
||||
C_MACRO_COLLECTIONS_EXTENDED(CMC, LIST, MY_LIST_PARAMS, (STR))
|
||||
|
||||
struct list_uint_s_fval list_fval = { .cmp = NULL,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
struct list_uint_s *a1 = list_uint_new(1, &list_fval);
|
||||
struct list_uint_s *a2 = list_uint_new(1, &list_fval);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
list_uint_push_back(a1, rand_get() );
|
||||
list_uint_push_back(a2, rand_get() );
|
||||
}
|
||||
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += list_uint_get(a1, i ) * list_uint_get(a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
list_uint_free(a1);
|
||||
list_uint_free(a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define my_cmp(a,b) ( (a) < (b) ? -1 : (a) > (b) )
|
||||
|
||||
#define MY_TMAP_PARAMS (tree_ulong, tree_ulong_s, , unsigned long, bool)
|
||||
C_MACRO_COLLECTIONS_EXTENDED(CMC, TREEMAP, MY_TMAP_PARAMS, (STR))
|
||||
|
||||
int cmp_ulong(unsigned long a, unsigned long b) { return my_cmp(a,b); }
|
||||
|
||||
struct tree_ulong_s_fkey tree_fkey = { .cmp = cmp_ulong,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
struct tree_ulong_s_fval tree_fval = { .cmp = NULL,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
struct tree_ulong_s *tree;
|
||||
tree = tree_ulong_new(&tree_fkey, &tree_fval);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
tree_ulong_insert(tree, j, true);
|
||||
}
|
||||
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
if (tree_ulong_get_ref(tree, j) != 0)
|
||||
s+= j;
|
||||
}
|
||||
g_result = s;
|
||||
tree_ulong_free(tree);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
#define hash_func(a) kh_int64_hash_func(a)
|
||||
size_t hash_ulong(unsigned long a)
|
||||
{
|
||||
return ( (a) >> 33 ) ^ (a) ^ ((a) << 11);
|
||||
}
|
||||
|
||||
#define MY_DICT_PARAMS (hmap, hmap_s, , unsigned long, unsigned long)
|
||||
C_MACRO_COLLECTIONS_EXTENDED(CMC, HASHMAP, MY_DICT_PARAMS, (STR))
|
||||
|
||||
struct hmap_s_fkey hmap_fkey = { .cmp = cmp_ulong,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = hash_ulong,
|
||||
.pri = NULL };
|
||||
|
||||
struct hmap_s_fval hmap_fval = { .cmp = NULL,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
struct hmap_s *dict = hmap_new(16, 0.7, &hmap_fkey, &hmap_fval);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
hmap_insert(dict, rand_get(), rand_get());
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long *val = hmap_get_ref(dict, rand_get());
|
||||
if (val != 0)
|
||||
s += *val;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
hmap_free(dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#if 0 // Not working issue in using not integer types
|
||||
|
||||
typedef struct {
|
||||
char a[256];
|
||||
} char_array_t;
|
||||
static inline int char_cmp (const char_array_t a, const char_array_t b) { return strcmp(a.a,b.a); }
|
||||
static inline size_t char_hash(const char_array_t a) {
|
||||
size_t hash = 0;
|
||||
const char *s = a.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
}
|
||||
|
||||
#define MY_DICTB_PARAMS (bhmap, bhmap_s, , char_array_t, char_array_t)
|
||||
C_MACRO_COLLECTIONS_EXTENDED(CMC, HASHMAP, MY_DICT_PARAMS, (STR))
|
||||
|
||||
struct bhmap_s_fkey bhmap_fkey = { .cmp = char_cmp,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = char_hash,
|
||||
.pri = NULL };
|
||||
|
||||
struct bhmap_s_fval bhmap_fval = { .cmp = NULL,
|
||||
.cpy = NULL,
|
||||
.str = NULL,
|
||||
.free = NULL,
|
||||
.hash = NULL,
|
||||
.pri = NULL };
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
struct bhmap_s *dict = bhmap_new(16, 0.7, &bhmap_fkey, &bhmap_fval);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
bhmap_insert(dict, s1, s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1.a, "%u", rand_get() );
|
||||
char_array_t *val = bhmap_get_ref(dict, s1);
|
||||
if (val != 0)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
bhmap_free(dict);
|
||||
}
|
||||
#endif
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
//{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("CMC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+24
-19
@@ -93,7 +93,7 @@ static void test_rbtree(size_t n)
|
||||
stat = treetable_new(compare, &tree);
|
||||
if (stat != CC_OK) abort();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
void *key = (void*)(uintptr_t) i;
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
stat = treetable_add(tree, key, key);
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
@@ -122,11 +122,11 @@ static int equal_func(const void *key1, const void *key2)
|
||||
{
|
||||
const unsigned long pa = (uintptr_t) key1;
|
||||
const unsigned long pb = (uintptr_t) key2;
|
||||
return pa == pb;
|
||||
return (pa < pb) ? -1 : pa > pb;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
HashTable *dict;
|
||||
enum cc_stat stat;
|
||||
@@ -138,8 +138,8 @@ test_dict(unsigned long n)
|
||||
if (stat != CC_OK) abort();
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
void *value = (void*)(uintptr_t) rand_get();
|
||||
void *key = (void*)(uintptr_t) rand_get();
|
||||
stat = hashtable_add(dict, key, value );
|
||||
if (stat != CC_OK) abort();
|
||||
}
|
||||
@@ -164,7 +164,7 @@ static int char_equal (const void* a, const void * b)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
return strcmp(*pa,*pb)==0;
|
||||
return strcmp(*pa,*pb);
|
||||
}
|
||||
|
||||
static size_t char_hash(const void *a, int l, uint32_t seed)
|
||||
@@ -178,7 +178,7 @@ static size_t char_hash(const void *a, int l, uint32_t seed)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
HashTable *dict;
|
||||
enum cc_stat stat;
|
||||
@@ -189,9 +189,14 @@ test_dict_big(unsigned long n)
|
||||
stat = hashtable_new_conf(&htc, &dict);
|
||||
if (stat != CC_OK) abort();
|
||||
|
||||
char_array_t **tab = malloc (sizeof(char_array_t *)*2*n);
|
||||
if (!tab) abort();
|
||||
size_t cpt = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t *key = malloc (sizeof (char_array_t));
|
||||
char_array_t *value = malloc (sizeof (char_array_t));
|
||||
tab[cpt++] = key;
|
||||
tab[cpt++] = value;
|
||||
sprintf(*key, "%u", rand_get());
|
||||
sprintf(*value, "%u", rand_get());
|
||||
stat = hashtable_add(dict, key, value );
|
||||
@@ -209,6 +214,9 @@ test_dict_big(unsigned long n)
|
||||
}
|
||||
g_result = s;
|
||||
hashtable_destroy(dict);
|
||||
if (cpt != 2*n) abort();
|
||||
for(size_t i = 0; i < 2*n;i++)
|
||||
free(tab[i]);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
@@ -242,20 +250,17 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("CollectionC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -6,11 +6,8 @@
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
#include "m-mutex.h"
|
||||
#define MULTI_THREAD_MEASURE
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
moodycamel::ConcurrentQueue<unsigned int> g_buff;
|
||||
moodycamel::ConcurrentQueue<unsigned long long> g_final;
|
||||
@@ -51,7 +48,7 @@ static void prod(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
static void test_buffer(size_t n)
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
@@ -88,11 +85,12 @@ static void test_buffer(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
test("CONCURRENT-QUEUE", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "m-core.h"
|
||||
#include "common.h"
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
|
||||
/********************************************************************************************/
|
||||
#define POD
|
||||
#define T uint
|
||||
#include "ctl/vector.h"
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
vec_uint a1 = vec_uint_init();
|
||||
vec_uint a2 = vec_uint_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
vec_uint_push_back(&a1, rand_get() );
|
||||
vec_uint_push_back(&a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(unsigned long i = 0; i < n; i++) {
|
||||
s += *vec_uint_at(&a1, i ) * *vec_uint_at(&a2, i );
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
vec_uint_free(&a1);
|
||||
vec_uint_free(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define POD
|
||||
#define T uint
|
||||
#include "ctl/forward_list.h"
|
||||
|
||||
static void test_list (size_t n)
|
||||
{
|
||||
slist_uint a1 = slist_uint_init();
|
||||
slist_uint a2 = slist_uint_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
slist_uint_push_front(&a1, rand_get());
|
||||
slist_uint_push_front(&a2, rand_get());
|
||||
}
|
||||
unsigned int s = 0;
|
||||
slist_uint_it it1 = slist_uint_begin(&a1);
|
||||
slist_uint_it it2 = slist_uint_begin(&a2);
|
||||
for( ; !slist_uint_it_done(&it1) ;
|
||||
slist_uint_it_next(&it1), slist_uint_it_next(&it2)) {
|
||||
s += *slist_uint_it_ref(&it1) * *slist_uint_it_ref(&it2);
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
slist_uint_free(&a1);
|
||||
slist_uint_free(&a2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define POD
|
||||
#define T ulong
|
||||
#include "ctl/set.h"
|
||||
|
||||
static inline int
|
||||
int_cmp(ulong *a, ulong *b) {
|
||||
return *a < *b;
|
||||
}
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
set_ulong tree = set_ulong_init (int_cmp);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
set_ulong_insert (&tree, rand_get() );
|
||||
}
|
||||
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = rand_get();
|
||||
if (set_ulong_find_node(&tree, j) != NULL)
|
||||
s+= j;
|
||||
}
|
||||
g_result = s;
|
||||
set_ulong_free(&tree);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
unsigned long key;
|
||||
unsigned long value;
|
||||
} pair_ulong;
|
||||
|
||||
// Use of M*LIB hash function
|
||||
static inline size_t
|
||||
pair_ulong_hash(pair_ulong *a)
|
||||
{
|
||||
return M_HASH_DEFAULT(a->key);
|
||||
}
|
||||
|
||||
static inline int
|
||||
pair_ulong_equal(pair_ulong *a, pair_ulong *b)
|
||||
{
|
||||
return a->key == b->key;
|
||||
}
|
||||
|
||||
static inline void
|
||||
pair_ulong_free(pair_ulong *a) {
|
||||
(void) a;
|
||||
}
|
||||
|
||||
static inline pair_ulong
|
||||
pair_ulong_copy(pair_ulong *self) {
|
||||
return (pair_ulong) { .key = self->key, .value = self->value };
|
||||
}
|
||||
|
||||
#define T pair_ulong
|
||||
#include <ctl/unordered_map.h>
|
||||
|
||||
// It seems broken: it doesn't output the expected result.
|
||||
static void
|
||||
test_dict(size_t n)
|
||||
{
|
||||
umap_pair_ulong dict = umap_pair_ulong_init(pair_ulong_hash, pair_ulong_equal);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
umap_pair_ulong_insert(&dict, (pair_ulong) { rand_get(), rand_get() });
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
umap_pair_ulong_it it = umap_pair_ulong_find(&dict, (pair_ulong) { rand_get() , 0 } );
|
||||
if (!umap_pair_ulong_it_done(&it) )
|
||||
s += it.ref->value;
|
||||
}
|
||||
g_result = s;
|
||||
|
||||
umap_pair_ulong_free(&dict);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define POD
|
||||
#define T float
|
||||
#include "ctl/vector.h"
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
vec_float a1 = vec_float_init();
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
vec_float_push_back(&a1, rand_get());
|
||||
}
|
||||
vec_float_sort(&a1);
|
||||
g_result = *vec_float_at(&a1, 0);
|
||||
vec_float_free(&a1);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 50, "Sort", 10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("CTL", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace std;
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
dense_hash_map<unsigned long, unsigned long> dict;
|
||||
dict.set_empty_key(-1);
|
||||
@@ -75,7 +75,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
dense_hash_map<char_array_s, char_array_s> dict;
|
||||
struct char_array_s empty, del;
|
||||
@@ -104,7 +104,7 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
dense_hash_map<string, string> dict;
|
||||
string empty, del;
|
||||
@@ -131,14 +131,14 @@ test_dict_str(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
test("DENSE-HASPMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "emilib/hash_map.hpp"
|
||||
|
||||
using namespace emilib;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
HashMap<char_array_s, char_array_s> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
HashMap<char_array_s, char_array_s>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
HashMap<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
HashMap<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0},
|
||||
{ 46, "DictLinear", 1000000, 0, test_dict2_linear, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("EMILIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// program to find longest contiguous subsequence
|
||||
// based on https://www.geeksforgeeks.org/longest-consecutive-subsequence/
|
||||
#include <iostream>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// Returns length of the longest contiguous subsequence
|
||||
int cpp_find_longest(int arr[], int n)
|
||||
{
|
||||
unordered_set<int> S;
|
||||
int ans = 0;
|
||||
|
||||
// Hash all the array elements
|
||||
for (int i = 0; i < n; i++)
|
||||
S.insert(arr[i]);
|
||||
|
||||
// check each possible sequence from
|
||||
// the start then update optimal length
|
||||
for (int i = 0; i < n; i++) {
|
||||
// if current element is the starting
|
||||
// element of a sequence
|
||||
if (S.find(arr[i] - 1) == S.end()) {
|
||||
// Then check for next elements
|
||||
// in the sequence
|
||||
int j = arr[i] + 1;
|
||||
while (S.find(j) != S.end())
|
||||
j++;
|
||||
|
||||
// update optimal length if
|
||||
// this length is more
|
||||
ans = max(ans, j - arr[i]);
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
#include "m-dict.h"
|
||||
|
||||
static inline bool oor_equal_p(int a, unsigned char c) { return a == ((c == 0) ? INT_MIN : INT_MAX); }
|
||||
static inline void oor_set(int *a, unsigned char c) { *a = ((c == 0) ? INT_MIN : INT_MAX); }
|
||||
|
||||
DICT_OASET_DEF(dict_int, int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)))
|
||||
|
||||
// Returns length of the longest contiguous subsequence
|
||||
int mlib_find_longest(int arr[], int n)
|
||||
{
|
||||
dict_int_t S;
|
||||
int ans = 0;
|
||||
|
||||
dict_int_init(S);
|
||||
|
||||
// Hash all the array elements
|
||||
for (int i = 0; i < n; i++)
|
||||
dict_int_push(S, arr[i]);
|
||||
|
||||
// check each possible sequence from
|
||||
// the start then update optimal length
|
||||
for (int i = 0; i < n; i++) {
|
||||
// if current element is the starting
|
||||
// element of a sequence
|
||||
int *p = dict_int_get(S, arr[i]-1);
|
||||
if (p == NULL) {
|
||||
// Then check for next elements
|
||||
// in the sequence
|
||||
int j = arr[i] + 1;
|
||||
while (dict_int_get(S, j) != NULL)
|
||||
j++;
|
||||
|
||||
// update optimal length if
|
||||
// this length is more
|
||||
ans = M_MAX(ans, j - arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
dict_int_clear(S);
|
||||
return ans;
|
||||
}
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = argc == 1 ? 10000 : atoi(argv[1]);
|
||||
int *arr = (int*) malloc(n * sizeof(int));
|
||||
for(int i = 0; i < n; i++)
|
||||
arr[i] = rand_get();
|
||||
|
||||
unsigned long long t0 = cputime();
|
||||
cout << "Length of the Longest contiguous subsequence is "
|
||||
<< cpp_find_longest(arr, n) << endl;
|
||||
|
||||
unsigned long long t1 = cputime();
|
||||
|
||||
cout << "Length of the Longest contiguous subsequence is "
|
||||
<< mlib_find_longest(arr, n) << endl;
|
||||
unsigned long long t2 = cputime();
|
||||
|
||||
cout << "Time C++: " << (t1-t0) << " Time M*LIB: " << (t2-t1) << endl;
|
||||
return 0;
|
||||
}
|
||||
+28
-10
@@ -26,7 +26,7 @@ using namespace std;
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
flat_hash_map<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -42,6 +42,23 @@ test_dict2(unsigned long n)
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
flat_hash_map<unsigned long, unsigned long> dict;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
flat_hash_map<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
@@ -62,7 +79,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
flat_hash_map<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -86,7 +103,7 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
flat_hash_map<string, string> dict;
|
||||
|
||||
@@ -108,14 +125,15 @@ test_dict_str(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0},
|
||||
{ 46, "DictLinear", 1000000, 0, test_dict2_linear, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
test("FLAT-HASPMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+12
-15
@@ -97,7 +97,7 @@ static gboolean equal_func(gconstpointer a, gconstpointer b)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
GHashTable *dict = g_hash_table_new_full(hash_func, equal_func, free, free);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -140,7 +140,7 @@ static guint char_hash(gconstpointer a)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
GHashTable *dict = g_hash_table_new_full(char_hash, char_equal, free, free);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -186,20 +186,17 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("GLIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "src/hopscotch_map.h"
|
||||
#include "hopscotch_map.h"
|
||||
|
||||
using namespace tsl;
|
||||
using namespace std;
|
||||
@@ -25,7 +25,7 @@ using namespace std;
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
tsl::hopscotch_map<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
hopscotch_map<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -87,7 +87,7 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
hopscotch_map<string, string> dict;
|
||||
|
||||
@@ -109,14 +109,15 @@ test_dict_str(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
test("HOPSCOTCHMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
|
||||
}
|
||||
+13
-16
@@ -82,7 +82,7 @@ static void test_rbtree(size_t n)
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long j = 0;
|
||||
unsigned long j = rand_get();
|
||||
if (kb_getp(kTree, tree, &j) != 0)
|
||||
s+= j;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ static void test_rbtree(size_t n)
|
||||
KHASH_INIT(iun, unsigned long, unsigned long, 1, hash_func, hash_eq)
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
khash_t(iun) *dict = kh_init(iun);
|
||||
|
||||
@@ -137,7 +137,7 @@ static inline size_t char_hash(const char_array_t a) {
|
||||
KHASH_INIT(iub, char_array_t, char_array_t, 1, char_hash, char_equal_p)
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
khash_t(iub) *dict = kh_init(iub);
|
||||
|
||||
@@ -185,21 +185,18 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]): 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("B-tree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("KLIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+33
-20
@@ -7,6 +7,9 @@
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Needed due to compilation error in pool.h
|
||||
#include <pthread.h>
|
||||
|
||||
#include "dynamic.h"
|
||||
|
||||
#include "common.h"
|
||||
@@ -45,6 +48,9 @@ static size_t hash_func (void *a)
|
||||
static int equal_func(void *a, void *b)
|
||||
{
|
||||
const unsigned long *pa = a;
|
||||
if (b == NULL) {
|
||||
return *pa == -1U;
|
||||
}
|
||||
const unsigned long *pb = b;
|
||||
return *pa == *pb;
|
||||
}
|
||||
@@ -52,8 +58,12 @@ static int equal_func(void *a, void *b)
|
||||
static void set_func(void *a, void *b)
|
||||
{
|
||||
unsigned long *pa = a;
|
||||
const unsigned long *pb = b;
|
||||
*pa = *pb;
|
||||
if (b == NULL) {
|
||||
*pa = -1U;
|
||||
} else {
|
||||
const unsigned long *pb = b;
|
||||
*pa = *pb;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct map_element map_element;
|
||||
@@ -64,13 +74,13 @@ struct map_element
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
map dict;
|
||||
map_construct(&dict, sizeof(map_element), (map_element[]) {{.key = -1}}, set_func);
|
||||
map_construct(&dict, sizeof(map_element), set_func);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
map_insert(&dict, (map_element[]) {{.key = rand_get(), .value = rand_get()}}, hash_func, equal_func, set_func, NULL);
|
||||
map_insert(&dict, (map_element[]) {{.key = rand_get(), .value = rand_get()}}, hash_func, set_func, equal_func, NULL);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
@@ -90,6 +100,8 @@ typedef char char_array_t[256];
|
||||
static int char_equal (void *a, void *b)
|
||||
{
|
||||
const char_array_t *pa = (const char_array_t *)a;
|
||||
if (b == NULL)
|
||||
return strcmp(*pa, "") == 0;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
return strcmp(*pa,*pb)==0;
|
||||
}
|
||||
@@ -103,8 +115,12 @@ static size_t char_hash(void *a)
|
||||
static void char_set(void *a, void *b)
|
||||
{
|
||||
char_array_t *pa = (char_array_t *)a;
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
strcpy(*pa,*pb);
|
||||
if (b == NULL) {
|
||||
strcpy(*pa, "");
|
||||
} else {
|
||||
const char_array_t *pb = (const char_array_t *)b;
|
||||
strcpy(*pa,*pb);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct big_map_element big_map_element;
|
||||
@@ -115,19 +131,16 @@ struct big_map_element
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
map dict;
|
||||
big_map_element empty;
|
||||
strcpy(empty.key, "");
|
||||
strcpy(empty.value, "");
|
||||
map_construct(&dict, sizeof(big_map_element), &empty, char_set);
|
||||
map_construct(&dict, sizeof(big_map_element), char_set);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
big_map_element el;
|
||||
sprintf(el.key, "%u", rand_get());
|
||||
sprintf(el.value, "%u", rand_get());
|
||||
map_insert(&dict, &el, char_hash, char_equal, char_set, NULL);
|
||||
map_insert(&dict, &el, char_hash, char_set, char_equal, NULL);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
@@ -144,14 +157,14 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("Dict B time", 1000000, test_dict_big);
|
||||
test("LIBDYNAMIC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+14
-28
@@ -6,11 +6,8 @@
|
||||
|
||||
#include "liblfds711.h"
|
||||
|
||||
#include "m-mutex.h"
|
||||
#define MULTI_THREAD_MEASURE
|
||||
#include "common.h"
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct lfds711_queue_bmm_state g_buff;
|
||||
@@ -23,12 +20,9 @@ static void final(void *arg)
|
||||
unsigned long long j, s = 0;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(int i = 0; i < n;i++) {
|
||||
int rv;
|
||||
do {
|
||||
void *p;
|
||||
rv = lfds711_queue_bmm_dequeue(&g_final, &p, NULL);
|
||||
j = (uintptr_t)p;
|
||||
} while (rv == 0);
|
||||
void *p;
|
||||
while (0 == lfds711_queue_bmm_dequeue(&g_final, &p, NULL)) { m_thread_yield(); }
|
||||
j = (uintptr_t)p;
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
@@ -42,18 +36,12 @@ static void conso(void *arg)
|
||||
unsigned long long s = 0;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(int i = 0; i < n;i++) {
|
||||
int rv;
|
||||
do {
|
||||
void *p;
|
||||
rv = lfds711_queue_bmm_dequeue(&g_buff, &p, NULL);
|
||||
j = (uintptr_t) p;
|
||||
} while (rv == 0);
|
||||
void *p;
|
||||
while (0 == lfds711_queue_bmm_dequeue(&g_buff, &p, NULL)) { m_thread_yield(); }
|
||||
j = (uintptr_t) p;
|
||||
s += j;
|
||||
}
|
||||
int rv;
|
||||
do {
|
||||
rv = lfds711_queue_bmm_enqueue(&g_final, (void*)(uintptr_t)s, NULL);
|
||||
} while (rv == 0);
|
||||
while (0 == lfds711_queue_bmm_enqueue(&g_final, (void*)(uintptr_t)s, NULL)) { m_thread_yield(); }
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
@@ -63,15 +51,12 @@ static void prod(void *arg)
|
||||
size_t r = n;
|
||||
LFDS711_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
int rv;
|
||||
do {
|
||||
rv = lfds711_queue_bmm_enqueue(&g_buff, (void*)(uintptr_t)r, NULL);
|
||||
} while (rv == 0);
|
||||
while (0 == lfds711_queue_bmm_enqueue(&g_buff, (void*)(uintptr_t)r, NULL)) { m_thread_yield(); }
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_buffer(size_t n)
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
@@ -120,11 +105,12 @@ static void test_buffer(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
test("LIBLFDS", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+10
-8
@@ -14,8 +14,8 @@
|
||||
|
||||
static void test_array(size_t n)
|
||||
{
|
||||
sv_t *a1 = sv_alloc ( sizeof (unsigned int), 16, NULL);
|
||||
sv_t *a2 = sv_alloc ( sizeof (unsigned int), 16, NULL);
|
||||
srt_vector *a1 = sv_alloc ( sizeof (unsigned int), 16, NULL);
|
||||
srt_vector *a2 = sv_alloc ( sizeof (unsigned int), 16, NULL);
|
||||
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int value = rand_get();
|
||||
@@ -48,7 +48,7 @@ static int cmp_float (const void *a, const void *b)
|
||||
|
||||
static void test_sort(size_t n)
|
||||
{
|
||||
sv_t *a1 = sv_alloc(sizeof(float), 16, cmp_float);
|
||||
srt_vector *a1 = sv_alloc(sizeof(float), 16, cmp_float);
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
unsigned int value = rand_get();
|
||||
sv_push(&a1, &value );
|
||||
@@ -60,13 +60,15 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 50, "sort", 10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("LIBSRT", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+654
-84
@@ -7,12 +7,19 @@
|
||||
#include "m-array.h"
|
||||
#include "m-list.h"
|
||||
#include "m-rbtree.h"
|
||||
#include "m-bptree.h"
|
||||
#include "m-deque.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-algo.h"
|
||||
#include "m-mempool.h"
|
||||
#include "m-string.h"
|
||||
#include "m-buffer.h"
|
||||
#include "m-mutex.h"
|
||||
#include "m-deque.h"
|
||||
#include "m-concurrent.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-serial-bin.h"
|
||||
#include "m-serial-json.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
@@ -37,6 +44,30 @@ static void test_array(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
DEQUE_DEF(deque_uint, unsigned int)
|
||||
|
||||
static void test_deque(size_t n)
|
||||
{
|
||||
M_LET(a1, a2, DEQUE_OPLIST(deque_uint)) {
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
deque_uint_push_back(a1, rand_get() );
|
||||
deque_uint_push_back(a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
deque_uint_it_t it1, it2;
|
||||
deque_uint_it(it1, a1);
|
||||
deque_uint_it(it2, a2);
|
||||
while (!deque_uint_end_p(it1)) {
|
||||
s += *deque_uint_cref(it1 ) * *deque_uint_cref(it2 );
|
||||
deque_uint_next(it1);
|
||||
deque_uint_next(it2);
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// Two benched mode: with or without mempool
|
||||
#ifdef USE_MEMPOOL
|
||||
LIST_DEF(list_uint, unsigned int, (MEMPOOL( list_mpool), MEMPOOL_LINKAGE(static)))
|
||||
@@ -68,6 +99,37 @@ static void test_list (size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// Two benched mode: with or without mempool
|
||||
#ifdef USE_MEMPOOL
|
||||
LIST_DUAL_PUSH_DEF(dlist_uint, unsigned int, (MEMPOOL( dlist_mpool), MEMPOOL_LINKAGE(static)))
|
||||
#else
|
||||
LIST_DUAL_PUSH_DEF(dlist_uint, unsigned int)
|
||||
#endif
|
||||
|
||||
static void test_dlist (size_t n)
|
||||
{
|
||||
#ifdef USE_MEMPOOL
|
||||
dlist_uint_mempool_init(dlist_mpool);
|
||||
#endif
|
||||
M_LET(a1, a2, LIST_OPLIST(dlist_uint)) {
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
dlist_uint_push_back(a1, rand_get() );
|
||||
dlist_uint_push_back(a2, rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
dlist_uint_it_t it1, it2;
|
||||
for(dlist_uint_it(it1, a1), dlist_uint_it(it2,a2); !dlist_uint_end_p(it1); dlist_uint_next(it1), dlist_uint_next(it2)) {
|
||||
s += *dlist_uint_cref(it1) * *dlist_uint_cref(it2);
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
#ifdef USE_MEMPOOL
|
||||
dlist_uint_mempool_clear(dlist_mpool);
|
||||
#endif
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
RBTREE_DEF(rbtree_ulong, unsigned long, (MEMPOOL( rbtree_mpool), MEMPOOL_LINKAGE(static)))
|
||||
#else
|
||||
@@ -99,6 +161,27 @@ static void test_rbtree(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
BPTREE_DEF(bptree_ulong, 21, unsigned long, M_DEFAULT_OPLIST)
|
||||
|
||||
static void test_bptree(size_t n)
|
||||
{
|
||||
M_LET(tree, BPTREE_OPLIST(bptree_ulong)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
bptree_ulong_push(tree, rand_get());
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long *p = bptree_ulong_get(tree, rand_get());
|
||||
if (p)
|
||||
s += *p;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
DICT_DEF2(dict_ulong, unsigned long, M_OPEXTEND(M_DEFAULT_OPLIST, MEMPOOL(dict_mpool), MEMPOOL_LINKAGE(static)), unsigned long, M_DEFAULT_OPLIST)
|
||||
#else
|
||||
@@ -106,15 +189,16 @@ DICT_DEF2(dict_ulong, unsigned long, M_DEFAULT_OPLIST, unsigned long, M_DEFAULT_
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
#ifdef USE_MEMPOOL
|
||||
dict_ulong_list_pair_mempool_init(dict_mpool);
|
||||
#endif
|
||||
M_LET(dict, DICT_OPLIST(dict_ulong)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long value = rand_get();
|
||||
unsigned long key = rand_get();
|
||||
dict_ulong_set_at(dict, key, rand_get() );
|
||||
dict_ulong_set_at(dict, key, value);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
@@ -147,7 +231,7 @@ DICT_OA_DEF2(dict_oa_ulong,
|
||||
unsigned long, M_DEFAULT_OPLIST)
|
||||
|
||||
static void
|
||||
test_dict_oa(unsigned long n)
|
||||
test_dict_oa(size_t n)
|
||||
{
|
||||
M_LET(dict, DICT_OPLIST(dict_oa_ulong)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
@@ -164,26 +248,46 @@ test_dict_oa(unsigned long n)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_oa_linear(size_t n)
|
||||
{
|
||||
M_LET(dict, DICT_OPLIST(dict_oa_ulong)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict_oa_ulong_set_at(dict, i, rand_get() );
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unsigned long *p = dict_oa_ulong_get(dict, i);
|
||||
if (p)
|
||||
s += *p;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
typedef char char_array_t[256];
|
||||
|
||||
#ifndef CHAR_ARRAY_SIZE
|
||||
#define CHAR_ARRAY_SIZE 256
|
||||
#endif
|
||||
typedef char char_array_t[CHAR_ARRAY_SIZE];
|
||||
static void char_init (char_array_t a) { a[0] = 0; }
|
||||
static void char_set (char_array_t a, const char_array_t b) { strcpy(a, b); }
|
||||
static bool char_equal_p (const char_array_t a, const char_array_t b) { return strcmp(a,b)==0; }
|
||||
static size_t char_hash(const char_array_t a) { return m_core_hash (a, strlen(a)); }
|
||||
|
||||
// NOTE: Can't use the name OPLIST as a macro!
|
||||
#define CHAR_OPLIST (INIT(char_init), INIT_SET(char_set), SET(char_set), CLEAR(char_init), HASH(char_hash), EQUAL(char_equal_p))
|
||||
static bool char_oor_equal_p(const char_array_t a, unsigned char n) { return a[0] == 1+n; }
|
||||
static void char_oor_set(char_array_t a, unsigned char n) { a[0] = 1+n; }
|
||||
#define M_OPL_char_array_t() (INIT(char_init), INIT_SET(char_set), SET(char_set), CLEAR(char_init), HASH(char_hash), EQUAL(char_equal_p), OOR_EQUAL(char_oor_equal_p), OOR_SET(char_oor_set))
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPEXTEND(CHAR_OPLIST,MEMPOOL(dict_mpool2), MEMPOOL_LINKAGE(static)), char_array_t, CHAR_OPLIST)
|
||||
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPEXTEND(M_OPL_char_array_t(),MEMPOOL(dict_mpool2), MEMPOOL_LINKAGE(static)), char_array_t, M_OPL_char_array_t())
|
||||
#else
|
||||
DICT_STOREHASH_DEF2(dict_char, char_array_t, CHAR_OPLIST, char_array_t, CHAR_OPLIST)
|
||||
DICT_STOREHASH_DEF2(dict_char, char_array_t, M_OPL_char_array_t(), char_array_t, M_OPL_char_array_t())
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
#ifdef USE_MEMPOOL
|
||||
dict_char_list_pair_mempool_init(dict_mpool2);
|
||||
@@ -211,6 +315,31 @@ test_dict_big(unsigned long n)
|
||||
#endif
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_oa_char, char_array_t, char_array_t)
|
||||
|
||||
static void
|
||||
test_dict_oa_big(size_t n)
|
||||
{
|
||||
M_LET(dict, DICT_OPLIST(dict_oa_char)) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1, s2;
|
||||
sprintf(s1, "%u", rand_get());
|
||||
sprintf(s2, "%u", rand_get());
|
||||
dict_oa_char_set_at(dict, s1, s2);
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_t s1;
|
||||
sprintf(s1, "%u", rand_get());
|
||||
char_array_t *p = dict_oa_char_get(dict, s1);
|
||||
if (p)
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#ifdef USE_MEMPOOL
|
||||
@@ -220,7 +349,7 @@ DICT_DEF2(dict_str, string_t, STRING_OPLIST, string_t, STRING_OPLIST)
|
||||
#endif
|
||||
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
#ifdef USE_MEMPOOL
|
||||
dict_str_list_pair_mempool_init(dict_mpool3);
|
||||
@@ -276,73 +405,76 @@ static void test_stable_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define SIZE_LIMIT (UINT_MAX/2)
|
||||
|
||||
BUFFER_DEF(buffer_uint, unsigned int, 0, BUFFER_QUEUE|BUFFER_BLOCKING)
|
||||
buffer_uint_t g_buff;
|
||||
buffer_uint_t g_buff_lock;
|
||||
|
||||
BUFFER_DEF(buffer_ull, unsigned long long, 0, BUFFER_QUEUE|BUFFER_BLOCKING)
|
||||
buffer_ull_t g_final;
|
||||
buffer_ull_t g_final_lock;
|
||||
|
||||
static void final(void *arg)
|
||||
static void final_lock(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
buffer_ull_pop(&j, g_final);
|
||||
buffer_ull_pop(&j, g_final_lock);
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso(void *arg)
|
||||
static void conso_lock(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
buffer_uint_pop(&j, g_buff);
|
||||
buffer_uint_pop(&j, g_buff_lock);
|
||||
s += j;
|
||||
}
|
||||
buffer_ull_push(g_final, s);
|
||||
buffer_ull_push(g_final_lock, s);
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
static void prod_lock(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
buffer_uint_push(g_buff, r );
|
||||
buffer_uint_push(g_buff_lock, r );
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_buffer(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Buffer performance.\n");
|
||||
return;
|
||||
}
|
||||
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
|
||||
// Init
|
||||
buffer_uint_init(g_buff, 64*cpu_count);
|
||||
buffer_ull_init (g_final, 64*cpu_count);
|
||||
buffer_uint_init(g_buff_lock, 64*cpu_count);
|
||||
buffer_ull_init (g_final_lock, 64*cpu_count);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod, &n);
|
||||
m_thread_create (idx_p[i], prod_lock, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso, &n);
|
||||
m_thread_create (idx_c[i], conso_lock, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final, &n2);
|
||||
m_thread_create(idx_final, final_lock, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
@@ -354,79 +486,80 @@ static void test_buffer(size_t n)
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
buffer_ull_clear(g_final);
|
||||
buffer_uint_clear(g_buff);
|
||||
buffer_ull_clear(g_final_lock);
|
||||
buffer_uint_clear(g_buff_lock);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
QUEUE_MPMC_DEF(queue_uint, unsigned int, BUFFER_QUEUE)
|
||||
queue_uint_t g_buff2;
|
||||
queue_uint_t g_buff_mpmc;
|
||||
|
||||
QUEUE_MPMC_DEF(queue_ull, unsigned long long, BUFFER_QUEUE)
|
||||
queue_ull_t g_final2;
|
||||
queue_ull_t g_final_mpmc;
|
||||
|
||||
static void final2(void *arg)
|
||||
static void final_mpmc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_ull_pop(&j, g_final2));
|
||||
while (!queue_ull_pop(&j, g_final_mpmc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso2(void *arg)
|
||||
static void conso_mpmc(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_uint_pop(&j, g_buff2));
|
||||
while (!queue_uint_pop(&j, g_buff_mpmc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
while (!queue_ull_push(g_final2, s));
|
||||
while (!queue_ull_push(g_final_mpmc, s));
|
||||
}
|
||||
|
||||
static void prod2(void *arg)
|
||||
static void prod_mpmc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!queue_uint_push(g_buff2, r ));
|
||||
while (!queue_uint_push(g_buff_mpmc, r )) m_thread_yield();
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Queue performance.\n");
|
||||
return;
|
||||
}
|
||||
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
|
||||
// Init
|
||||
queue_uint_init(g_buff2, 64*cpu_count);
|
||||
queue_ull_init (g_final2, 64*cpu_count);
|
||||
queue_uint_init(g_buff_mpmc, 64*cpu_count);
|
||||
queue_ull_init (g_final_mpmc, 64*cpu_count);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod2, &n);
|
||||
m_thread_create (idx_p[i], prod_mpmc, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso2, &n);
|
||||
m_thread_create (idx_c[i], conso_mpmc, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final2, &n2);
|
||||
m_thread_create(idx_final, final_mpmc, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
@@ -438,8 +571,265 @@ static void test_queue(size_t n)
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
queue_ull_clear(g_final2);
|
||||
queue_uint_clear(g_buff2);
|
||||
queue_ull_clear(g_final_mpmc);
|
||||
queue_uint_clear(g_buff_mpmc);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
QUEUE_SPSC_DEF(queue_single_uint, unsigned int, BUFFER_QUEUE)
|
||||
queue_single_uint_t g_buff_spsc;
|
||||
|
||||
QUEUE_SPSC_DEF(queue_single_ull, unsigned long long, BUFFER_QUEUE)
|
||||
queue_single_ull_t g_final_spsc;
|
||||
|
||||
static void final_spsc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_single_ull_pop(&j, g_final_spsc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso_spsc(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_single_uint_pop(&j, g_buff_spsc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
while (!queue_single_ull_push(g_final_spsc, s));
|
||||
}
|
||||
|
||||
static void prod_spsc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
while (!queue_single_uint_push(g_buff_spsc, r )) m_thread_yield();
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue_single(size_t n)
|
||||
{
|
||||
const int cpu_count = 2;
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
// Init
|
||||
queue_single_uint_init(g_buff_spsc, 64*cpu_count);
|
||||
queue_single_ull_init (g_final_spsc, 64*cpu_count);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod_spsc, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso_spsc, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final_spsc, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
queue_single_ull_clear(g_final_spsc);
|
||||
queue_single_uint_clear(g_buff_spsc);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
#define BULK_SIZE 20
|
||||
|
||||
static void final_spsc_bulk(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
while (!queue_single_ull_pop(&j, g_final_spsc)) m_thread_yield();
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso_spsc_bulk(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
unsigned tab[BULK_SIZE];
|
||||
for(int i = 0; i < n;i+= BULK_SIZE) {
|
||||
unsigned k = queue_single_uint_pop_bulk(BULK_SIZE, tab, g_buff_spsc);
|
||||
while (k != BULK_SIZE) {
|
||||
m_thread_yield();
|
||||
k += queue_single_uint_pop_bulk(BULK_SIZE-k, tab+k, g_buff_spsc);
|
||||
}
|
||||
for(k = 0; k < BULK_SIZE;k++)
|
||||
s += tab[k];
|
||||
}
|
||||
while (!queue_single_ull_push(g_final_spsc, s));
|
||||
}
|
||||
|
||||
static void prod_spsc_bulk(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
if ((n % BULK_SIZE) != 0) abort();
|
||||
size_t r = n;
|
||||
unsigned tab[BULK_SIZE];
|
||||
for(unsigned int i = 0; i < n;i+= BULK_SIZE) {
|
||||
for(unsigned k = 0; k < BULK_SIZE; k++) {
|
||||
tab[k] = r;
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
unsigned k = queue_single_uint_push_bulk(g_buff_spsc, BULK_SIZE, tab);
|
||||
while (k != BULK_SIZE) {
|
||||
m_thread_yield();
|
||||
k += queue_single_uint_push_bulk(g_buff_spsc, BULK_SIZE-k, tab+k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue_single_bulk(size_t n)
|
||||
{
|
||||
const int cpu_count = 2;
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
// Init
|
||||
queue_single_uint_init(g_buff_spsc, 64*cpu_count);
|
||||
queue_single_ull_init (g_final_spsc, 64*cpu_count);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod_spsc_bulk, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso_spsc_bulk, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final_spsc_bulk, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
queue_single_ull_clear(g_final_spsc);
|
||||
queue_single_uint_clear(g_buff_spsc);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
CONCURRENT_DEF(cdeque_uint, deque_uint_t, M_OPEXTEND(DEQUE_OPLIST(deque_uint, M_DEFAULT_OPLIST), PUSH(deque_uint_push_front)))
|
||||
|
||||
DEQUE_DEF(deque_ull, unsigned long long)
|
||||
CONCURRENT_DEF(cdeque_ull, deque_ull_t, M_OPEXTEND(DEQUE_OPLIST(deque_ull, M_DEFAULT_OPLIST), PUSH(deque_ull_push_front)))
|
||||
|
||||
cdeque_uint_t g_buff_conc;
|
||||
cdeque_ull_t g_final_conc;
|
||||
|
||||
static void final_conc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
cdeque_ull_pop_blocking(&j, g_final_conc, true);
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso_conc(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(int i = 0; i < n;i++) {
|
||||
cdeque_uint_pop_blocking(&j, g_buff_conc, true);
|
||||
s += j;
|
||||
}
|
||||
cdeque_ull_push(g_final_conc, s);
|
||||
}
|
||||
|
||||
static void prod_conc(void *arg)
|
||||
{
|
||||
size_t *p_n = arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
cdeque_uint_push(g_buff_conc, r );
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue_concurrent(size_t n)
|
||||
{
|
||||
const int cpu_count = n > SIZE_LIMIT ? 2 : get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure CONCURRENT performance.\n");
|
||||
return;
|
||||
}
|
||||
n = n > SIZE_LIMIT ? n - SIZE_LIMIT : n;
|
||||
// Init
|
||||
cdeque_uint_init(g_buff_conc);
|
||||
cdeque_ull_init (g_final_conc);
|
||||
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod_conc, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso_conc, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final_conc, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
// Clear & quit
|
||||
cdeque_ull_clear(g_final_conc);
|
||||
cdeque_uint_clear(g_buff_conc);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
@@ -476,45 +866,225 @@ static void test_core_hash(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
ARRAY_DEF(vector_string, string_t)
|
||||
|
||||
vector_string_t vstring;
|
||||
vector_string_t vstring2;
|
||||
|
||||
static void
|
||||
bench_vector_string_init(size_t n)
|
||||
{
|
||||
vector_string_init(vstring);
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
M_LET( (s, "%zu", i, i, i, i), string_t) {
|
||||
vector_string_push_back(vstring, s);
|
||||
}
|
||||
}
|
||||
vector_string_init(vstring2);
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_init_big(size_t n)
|
||||
{
|
||||
vector_string_init(vstring);
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
M_LET( (s, "%zu%zu%zu%zu%zu%zu", i, i, i, i, i, i), string_t) {
|
||||
vector_string_push_back(vstring, s);
|
||||
}
|
||||
}
|
||||
vector_string_init(vstring2);
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_bin_run(size_t n)
|
||||
{
|
||||
if (vector_string_size(vstring) != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
FILE *f = fopen("tmp-serial.dat", "wb");
|
||||
if (!f) abort();
|
||||
M_LET( (serial, f), m_serial_bin_write_t) {
|
||||
vector_string_out_serial(serial, vstring);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
f = fopen("tmp-serial.dat", "rb");
|
||||
if (!f) abort();
|
||||
M_LET( (serial, f), m_serial_bin_read_t) {
|
||||
vector_string_in_serial(vstring2, serial);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (vector_string_size(vstring2) != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_json_run(size_t n)
|
||||
{
|
||||
if (vector_string_size(vstring) != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
FILE *f = fopen("tmp-serial.json", "wt");
|
||||
if (!f) abort();
|
||||
M_LET( (serial, f), m_serial_json_write_t) {
|
||||
vector_string_out_serial(serial, vstring);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
f = fopen("tmp-serial.json", "rt");
|
||||
if (!f) abort();
|
||||
M_LET( (serial, f), m_serial_json_read_t) {
|
||||
vector_string_in_serial(vstring2, serial);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (vector_string_size(vstring2) != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_clear(void)
|
||||
{
|
||||
if (!vector_string_equal_p(vstring, vstring2)) {
|
||||
printf("Array are different!\n");
|
||||
abort();
|
||||
}
|
||||
vector_string_clear(vstring);
|
||||
vector_string_clear(vstring2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
ARRAY_DEF(vector_ulong, unsigned long)
|
||||
|
||||
vector_ulong_t vulong;
|
||||
vector_ulong_t vulong2;
|
||||
|
||||
static void
|
||||
bench_vector_ulong_init(size_t n)
|
||||
{
|
||||
vector_ulong_init(vulong);
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
vector_ulong_push_back(vulong, i*i);
|
||||
}
|
||||
vector_ulong_init(vulong2);
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_ulong_bin_run(size_t n)
|
||||
{
|
||||
if (vector_ulong_size(vulong) != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
FILE *f = fopen("tmp-serial.dat", "wb");
|
||||
if (!f) abort();
|
||||
M_LET( (serial, f), m_serial_bin_write_t) {
|
||||
vector_ulong_out_serial(serial, vulong);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
f = fopen("tmp-serial.dat", "rb");
|
||||
if (!f) abort();
|
||||
M_LET( (serial, f), m_serial_bin_read_t) {
|
||||
vector_ulong_in_serial(vulong2, serial);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (vector_ulong_size(vulong2) != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_ulong_json_run(size_t n)
|
||||
{
|
||||
if (vector_ulong_size(vulong) != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
FILE *f = fopen("tmp-serial.json", "wt");
|
||||
if (!f) abort();
|
||||
M_LET( (serial, f), m_serial_json_write_t) {
|
||||
vector_ulong_out_serial(serial, vulong);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
f = fopen("tmp-serial.json", "rt");
|
||||
if (!f) abort();
|
||||
M_LET( (serial, f), m_serial_json_read_t) {
|
||||
vector_ulong_in_serial(vulong2, serial);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (vector_ulong_size(vulong2) != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_ulong_clear(void)
|
||||
{
|
||||
if (!vector_ulong_equal_p(vulong, vulong2)) {
|
||||
printf("Array are different!\n");
|
||||
abort();
|
||||
}
|
||||
vector_ulong_clear(vulong);
|
||||
vector_ulong_clear(vulong2);
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 11, "DPList", 10000000, 0, test_dlist, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 21, "Deque", 100000000, 0, test_deque, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 31, "B+tree", 1000000, 0, test_bptree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict(OA)", 1000000, 0, test_dict_oa, 0},
|
||||
{ 43,"DictStr", 1000000, 0, test_dict_str, 0},
|
||||
{ 46, "dictLinear(OA)", 1000000, 0, test_dict_oa_linear, 0},
|
||||
{ 47, "dictBig(OA)", 1000000, 0, test_dict_oa_big, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
{ 51, "Stable Sort",10000000, 0, test_stable_sort, 0},
|
||||
{ 60,"Buffer", 1000000, 0, test_buffer, 0},
|
||||
{ 61,"Queue MPMC", 1000000, 0, test_queue, 0},
|
||||
{ 62,"Buffer(P=2)", SIZE_LIMIT+1000000, 0, test_buffer, 0},
|
||||
{ 63,"Queue MPMC (P=2)", SIZE_LIMIT+1000000, 0, test_queue, 0},
|
||||
{ 64,"Queue SPSC (P=2)", 1000000, 0, test_queue_single, 0},
|
||||
{ 65,"Queue Concurrent", 1000000, 0, test_queue_concurrent, 0},
|
||||
{ 66,"Queue SPSC(Bulk)", 1000000, 0, test_queue_single_bulk, 0},
|
||||
{ 70,"M_HASH", 100000000, test_hash_prepare, test_hash, test_hash_final},
|
||||
{ 71,"Core Hash", 100000000, test_hash_prepare, test_core_hash, test_hash_final},
|
||||
{100, "serial-bin STR", 10000000, bench_vector_string_init, bench_vector_string_bin_run, bench_vector_string_clear},
|
||||
{101, "serial-bin STR.big", 10000000, bench_vector_string_init_big, bench_vector_string_bin_run, bench_vector_string_clear},
|
||||
{102, "serial-bin INT", 10000000, bench_vector_ulong_init, bench_vector_ulong_bin_run, bench_vector_ulong_clear},
|
||||
{110, "serial-json STR", 10000000, bench_vector_string_init, bench_vector_string_json_run, bench_vector_string_clear},
|
||||
{111, "serial-json STR.big", 10000000, bench_vector_string_init_big, bench_vector_string_json_run, bench_vector_string_clear},
|
||||
{112, "serial-json INT", 10000000, bench_vector_ulong_init, bench_vector_ulong_json_run, bench_vector_ulong_clear}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 42)
|
||||
test_function("DictOA time", 1000000, test_dict_oa);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS time", 1000000, test_dict_str);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
if (n == 51)
|
||||
test_function("Stable Sort time", 10000000, test_stable_sort);
|
||||
if (n == 60)
|
||||
test_function("Buffer time", 1000000, test_buffer);
|
||||
if (n == 61)
|
||||
test_function("Queue MPMC time", 1000000, test_queue);
|
||||
if (n == 70) {
|
||||
n = (argc > 2) ? atoi(argv[2]) : 100000000;
|
||||
test_hash_prepare(n);
|
||||
test_function("M_HASH time", n, test_hash);
|
||||
test_hash_final();
|
||||
}
|
||||
if (n == 71) {
|
||||
n = (argc > 2) ? atoi(argv[2]) : 100000000;
|
||||
test_hash_prepare(n);
|
||||
test_function("CORE_HASH time", n, test_core_hash);
|
||||
test_hash_final();
|
||||
}
|
||||
|
||||
test("M-LIB", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ static inline size_t fookeyfunct(const foo_t *r)
|
||||
NEDTRIE_GENERATE(static, foo_tree_s, foo_s, link, fookeyfunct, NEDTRIE_NOBBLEZEROS(foo_tree_s))
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
foo_tree_t footree;
|
||||
NEDTRIE_INIT(&footree);
|
||||
@@ -58,7 +58,7 @@ test_dict(unsigned long n)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_noalloc(unsigned long n)
|
||||
test_dict_noalloc(size_t n)
|
||||
{
|
||||
foo_tree_t footree;
|
||||
NEDTRIE_INIT(&footree);
|
||||
@@ -87,15 +87,13 @@ test_dict_noalloc(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 45, "dict(noalloc)", 1000000, 0, test_dict_noalloc, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 45)
|
||||
test_function("Dict No Alloc time", 1000000, test_dict_noalloc);
|
||||
|
||||
test("NEDTRIES", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+9
-7
@@ -98,14 +98,16 @@ static void test_rbtree(size_t n)
|
||||
|
||||
/* qlibc hash functions only accept a string as the key */
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
test("QLIBC", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+14
-18
@@ -72,7 +72,7 @@ static void test_rbtree(size_t n)
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict1(unsigned long n)
|
||||
test_dict1(size_t n)
|
||||
{
|
||||
QMap<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -92,7 +92,7 @@ test_dict1(unsigned long n)
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
QHash<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -126,7 +126,7 @@ uint qHash(const char_array_s &k)
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
QHash<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -162,22 +162,18 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict(m)", 1000000, 0, test_dict1, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict(u)", 1000000, 0, test_dict2, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict(m)time", 1000000, test_dict1);
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("QT", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
#define NDEBUG
|
||||
#define RAPIDJSON_HAS_STDSTRING 1
|
||||
|
||||
#include "rapidjson/reader.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/prettywriter.h"
|
||||
#include "rapidjson/filereadstream.h"
|
||||
#include "rapidjson/filewritestream.h"
|
||||
#include "rapidjson/encodedstream.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
vector<string> vstring;
|
||||
vector<string> vstring2;
|
||||
|
||||
static void
|
||||
bench_vector_string_init(size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
char buff[100];
|
||||
snprintf(buff, sizeof(buff), "%zu", i);
|
||||
std::string s(buff);
|
||||
vstring.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_init_big(size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
char buff[100];
|
||||
snprintf(buff, sizeof(buff), "%zu%zu%zu%zu%zu%zu", i, i, i, i, i, i);
|
||||
std::string s(buff);
|
||||
vstring.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_json_run(size_t n)
|
||||
{
|
||||
if (vstring.size() != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
FILE *f = fopen("tmp-serial.json", "wt");
|
||||
if (!f) abort();
|
||||
{
|
||||
// Prepare writer and output stream.
|
||||
char writeBuffer[65536];
|
||||
typedef rapidjson::AutoUTFOutputStream<unsigned, rapidjson::FileWriteStream> OutputStream;
|
||||
rapidjson::FileWriteStream os(f, writeBuffer, sizeof(writeBuffer));
|
||||
OutputStream eos(os, rapidjson::kUTF8, false);
|
||||
rapidjson::PrettyWriter<OutputStream, rapidjson::UTF8<>, rapidjson::AutoUTF<unsigned> > writer(eos);
|
||||
|
||||
writer.StartArray();
|
||||
for(size_t i = 0; i < n; i++){
|
||||
writer.String(vstring[i]);
|
||||
}
|
||||
writer.EndArray();
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
f = fopen("tmp-serial.json", "rt");
|
||||
if (!f) abort();
|
||||
{
|
||||
char readBuffer[65536];
|
||||
rapidjson::FileReadStream is(f, readBuffer, sizeof(readBuffer));
|
||||
rapidjson::Document d;
|
||||
d.ParseStream(is);
|
||||
for(size_t i = 0; i < d.Size(); i++) {
|
||||
rapidjson::Value& s = d[i];
|
||||
vstring2.push_back(s.GetString());
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (vstring2.size() != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_string_clear(void)
|
||||
{
|
||||
if (vstring != vstring2) {
|
||||
printf("Array are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
vector<unsigned long> vulong;
|
||||
vector<unsigned long> vulong2;
|
||||
|
||||
static void
|
||||
bench_vector_ulong_init(size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; i++)
|
||||
{
|
||||
vulong.push_back(i*i);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_ulong_json_run(size_t n)
|
||||
{
|
||||
if (vulong.size() != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
FILE *f = fopen("tmp-serial.json", "wt");
|
||||
if (!f) abort();
|
||||
{
|
||||
// Prepare writer and output stream.
|
||||
char writeBuffer[65536];
|
||||
typedef rapidjson::AutoUTFOutputStream<unsigned, rapidjson::FileWriteStream> OutputStream;
|
||||
rapidjson::FileWriteStream os(f, writeBuffer, sizeof(writeBuffer));
|
||||
OutputStream eos(os, rapidjson::kUTF8, false);
|
||||
rapidjson::PrettyWriter<OutputStream, rapidjson::UTF8<>, rapidjson::AutoUTF<unsigned> > writer(eos);
|
||||
|
||||
writer.StartArray();
|
||||
for(size_t i = 0; i < n; i++){
|
||||
writer.Int64(vulong[i]);
|
||||
}
|
||||
writer.EndArray();
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
f = fopen("tmp-serial.json", "rt");
|
||||
if (!f) abort();
|
||||
{
|
||||
char readBuffer[65536];
|
||||
rapidjson::FileReadStream is(f, readBuffer, sizeof(readBuffer));
|
||||
rapidjson::Document d;
|
||||
d.ParseStream(is);
|
||||
for(size_t i = 0; i < d.Size(); i++) {
|
||||
rapidjson::Value& s = d[i];
|
||||
vulong2.push_back(s.GetInt64());
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (vulong2.size() != n) {
|
||||
printf("Size are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bench_vector_ulong_clear(void)
|
||||
{
|
||||
if (vulong != vulong2) {
|
||||
printf("Array are different!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{110, "serial-json STR", 10000000, bench_vector_string_init, bench_vector_string_json_run, bench_vector_string_clear},
|
||||
{111, "serial-json STR.big", 10000000, bench_vector_string_init_big, bench_vector_string_json_run, bench_vector_string_clear},
|
||||
{112, "serial-json INT", 10000000, bench_vector_ulong_init, bench_vector_ulong_json_run, bench_vector_ulong_clear}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("RAPIDJSON", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rigtorp/HashMap.h"
|
||||
|
||||
using namespace rigtorp;
|
||||
using namespace std;
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
// Hash for using unsigned long as lookup key
|
||||
struct UL_Hash {
|
||||
size_t operator()(const unsigned long v) { return v * 7; }
|
||||
};
|
||||
|
||||
// Equal comparison for using unsigned long as lookup key
|
||||
struct UL_Equal {
|
||||
bool operator()(const unsigned long lhs, const unsigned long rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
HashMap<unsigned long, unsigned long, UL_Hash, UL_Equal> dict(16, -1UL);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[rand_get()] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
HashMap<unsigned long, unsigned long, UL_Hash, UL_Equal>::iterator it = dict.find(rand_get());
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
|
||||
struct char_array_s {
|
||||
char a[256];
|
||||
char_array_s () { a[0] = 0 ; }
|
||||
char_array_s ( const char_array_s & other) { strcpy(a, other.a); }
|
||||
bool operator==(const char_array_s &other) const { return strcmp(a, other.a) == 0; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template <> struct hash<char_array_s> {
|
||||
std::size_t operator()(const char_array_s &k) const {
|
||||
size_t hash = 0;
|
||||
const char *s = k.a;
|
||||
while (*s) hash = hash * 31421 + (*s++) + 6927;
|
||||
return hash;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
struct BIG_Hash {
|
||||
size_t operator()(const char_array_s v) { return hash<char_array_s>()(v); }
|
||||
};
|
||||
|
||||
struct BIG_Equal {
|
||||
bool operator()(const char_array_s lhs, const char_array_s rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
char_array_s empty;
|
||||
HashMap<char_array_s, char_array_s, BIG_Hash, BIG_Equal> dict(16, empty);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1, s2;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
sprintf(s2.a, "%u", rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char_array_s s1;
|
||||
sprintf(s1.a, "%u", rand_get());
|
||||
HashMap<char_array_s, char_array_s, BIG_Hash, BIG_Equal>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct STR_Hash {
|
||||
size_t operator()(const string v) { return hash<string>()(v); }
|
||||
};
|
||||
|
||||
struct STR_Equal {
|
||||
bool operator()(const string lhs, const string rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
string empty;
|
||||
HashMap<string, string, STR_Hash, STR_Equal> dict(16, empty);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
HashMap<string, string, STR_Hash, STR_Equal>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("RIGTORP-HASHMAP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <rigtorp/MPMCQueue.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/********************************************************************************************/
|
||||
rigtorp::MPMCQueue<unsigned int> g_buff(4*64);
|
||||
rigtorp::MPMCQueue<unsigned long long> g_final(4*64);
|
||||
|
||||
static void final(void *arg)
|
||||
{
|
||||
size_t *p_n = (size_t*) arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long j, s = 0;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
g_final.pop(j);
|
||||
s += j;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void conso(void *arg)
|
||||
{
|
||||
unsigned int j;
|
||||
size_t *p_n = (size_t*)arg;
|
||||
size_t n = *p_n;
|
||||
unsigned long long s = 0;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
g_buff.pop(j);
|
||||
s += j;
|
||||
}
|
||||
g_final.push(s);
|
||||
}
|
||||
|
||||
static void prod(void *arg)
|
||||
{
|
||||
size_t *p_n = (size_t*)arg;
|
||||
size_t n = *p_n;
|
||||
size_t r = n;
|
||||
for(unsigned int i = 0; i < n;i++) {
|
||||
g_buff.push(r);
|
||||
r = r * 31421U + 6927U;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_queue(size_t n)
|
||||
{
|
||||
const int cpu_count = get_cpu_count();
|
||||
const int prod_count = cpu_count/2;
|
||||
const int conso_count = cpu_count - prod_count;
|
||||
if (cpu_count < 2) {
|
||||
fprintf(stderr, "WARNING: Can not measure Buffer performance.\n");
|
||||
return;
|
||||
}
|
||||
rand_get();
|
||||
// Create thread
|
||||
m_thread_t idx_p[prod_count];
|
||||
m_thread_t idx_c[conso_count];
|
||||
m_thread_t idx_final;
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_create (idx_p[i], prod, &n);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_create (idx_c[i], conso, &n);
|
||||
}
|
||||
size_t n2 = conso_count;
|
||||
m_thread_create(idx_final, final, &n2);
|
||||
|
||||
// Wait for jobs to be done.
|
||||
for(int i = 0; i < prod_count; i++) {
|
||||
m_thread_join(idx_p[i]);
|
||||
}
|
||||
for(int i = 0; i < conso_count; i++) {
|
||||
m_thread_join(idx_c[i]);
|
||||
}
|
||||
m_thread_join(idx_final);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 60, "Queue MPMC", 1000000, 0, test_queue, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
test("RIGTORP-MPMC-QUEUE", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+10
-10
@@ -25,7 +25,7 @@ using namespace std;
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
sparse_hash_map<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
sparse_hash_map<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -87,7 +87,7 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
sparse_hash_map<string, string> dict;
|
||||
|
||||
@@ -109,14 +109,14 @@ test_dict_str(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict", 1000000, 0, test_dict2, 0},
|
||||
{ 43, "DictStr", 1000000, 0, test_dict_str, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
test("SPARSEPP", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+63
-32
@@ -1,6 +1,7 @@
|
||||
#define NDEBUG
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
@@ -53,6 +54,23 @@ static void test_array(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_deque(size_t n)
|
||||
{
|
||||
deque<unsigned int> a1, a2;
|
||||
for(size_t i = 0; i < n; i++) {
|
||||
a1.push_back(rand_get() );
|
||||
a2.push_back(rand_get() );
|
||||
}
|
||||
unsigned int s = 0;
|
||||
for(auto it1 = a1.cbegin(), it2 = a2.cbegin();
|
||||
it1 != a1.cend() ;it1++, it2++) {
|
||||
s += *it1 * *it2;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
static void test_rbtree(size_t n)
|
||||
{
|
||||
set<unsigned long> tree;
|
||||
@@ -73,7 +91,7 @@ static void test_rbtree(size_t n)
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict1(unsigned long n)
|
||||
test_dict1(size_t n)
|
||||
{
|
||||
map<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -93,7 +111,7 @@ test_dict1(unsigned long n)
|
||||
/********************************************************************************************/
|
||||
|
||||
static void
|
||||
test_dict2(unsigned long n)
|
||||
test_dict2(size_t n)
|
||||
{
|
||||
unordered_map<unsigned long, unsigned long> dict;
|
||||
|
||||
@@ -110,6 +128,24 @@ test_dict2(unsigned long n)
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict2_linear(size_t n)
|
||||
{
|
||||
unordered_map<unsigned long, unsigned long> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
dict[i] = rand_get();
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
unordered_map<unsigned long, unsigned long>::iterator it = dict.find(i);
|
||||
if (it != dict.end())
|
||||
s += it->second;
|
||||
}
|
||||
g_result = s;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
struct char_array_s {
|
||||
@@ -131,7 +167,7 @@ namespace std {
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
unordered_map<char_array_s, char_array_s> dict;
|
||||
|
||||
@@ -155,19 +191,19 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
static void
|
||||
test_dict_str(unsigned long n)
|
||||
test_dict_str(size_t n)
|
||||
{
|
||||
unordered_map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s1 = to_string(rand_get());
|
||||
string s2 = to_string(rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s1 = to_string (rand_get());
|
||||
unordered_map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
@@ -176,19 +212,19 @@ test_dict_str(unsigned long n)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_str2(unsigned long n)
|
||||
test_dict_str2(size_t n)
|
||||
{
|
||||
map<string, string> dict;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s2 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s1 = to_string(rand_get());
|
||||
string s2 = to_string(rand_get());
|
||||
dict[s1] = s2;
|
||||
}
|
||||
rand_init();
|
||||
unsigned int s = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
string s1 = static_cast<ostringstream*>( &(ostringstream() << rand_get()) )->str();
|
||||
string s1 = to_string(rand_get());
|
||||
map<string, string>::iterator it = dict.find(s1);
|
||||
if (it != dict.end())
|
||||
s ++;
|
||||
@@ -220,28 +256,23 @@ static void test_stable_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 21, "Deque", 100000000, 0, test_deque, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict(m)", 1000000, 0, test_dict1, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 42,"dict(u)", 1000000, 0, test_dict2, 0},
|
||||
{ 43,"DictStr(u)", 1000000, 0, test_dict_str, 0},
|
||||
{ 44,"DictStr(m)", 1000000, 0, test_dict_str2, 0},
|
||||
{ 46, "dictLinear(u)", 1000000, 0, test_dict2_linear, 0},
|
||||
{ 50, "Sort",10000000, 0, test_sort, 0},
|
||||
{ 51, "Stable Sort",10000000, 0, test_stable_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict(m)time", 1000000, test_dict1);
|
||||
if (n == 42)
|
||||
test_function("Dict(u)time", 1000000, test_dict2);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 43)
|
||||
test_function("DictS(u) time", 1000000, test_dict_str);
|
||||
if (n == 44)
|
||||
test_function("DictS(m) time", 1000000, test_dict_str2);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
if (n == 51)
|
||||
test_function("Stable Sort time", 10000000, test_stable_sort);
|
||||
test("STL", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* M*LIB - String Bench
|
||||
*
|
||||
* Copyright (c) 2017-2021, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "m-string.h"
|
||||
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
extern "C" {
|
||||
#include "sds.h"
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
#include "bstrlib.h"
|
||||
#include "bstrwrap.h"
|
||||
#include "bstraux.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* BENCH
|
||||
* 2000000
|
||||
* 1. Generate N strings of random numbers 32 bits.
|
||||
* 2. Concat all strings in a string usigned a random permutation of N.
|
||||
* 3. Replace "1234" in the string into "WELL" until nothing remains.
|
||||
* 4. Replace "56789" in the string into "DONE" until nothing remains.
|
||||
* Return the final string length.
|
||||
*/
|
||||
|
||||
unsigned *permutation_tab = NULL;
|
||||
|
||||
void random_permutation(unsigned n)
|
||||
{
|
||||
if (permutation_tab) free(permutation_tab);
|
||||
permutation_tab = (unsigned *) malloc(n * sizeof(unsigned));
|
||||
if (permutation_tab == NULL) abort();
|
||||
for(unsigned i = 0; i < n; i++)
|
||||
permutation_tab[i] = i;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
unsigned j = rand_get() % n;
|
||||
unsigned k = rand_get() % n;
|
||||
unsigned l = permutation_tab[j];
|
||||
permutation_tab[j] = permutation_tab[k];
|
||||
permutation_tab[k] = l;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
static void replace_all_stl(string &str, const char pattern[], const char replace[])
|
||||
{
|
||||
size_t pos = 0;
|
||||
do {
|
||||
pos = str.find(pattern, pos);
|
||||
if (pos != string::npos) {
|
||||
str.replace (pos, strlen(pattern), replace);
|
||||
}
|
||||
} while (pos != string::npos);
|
||||
}
|
||||
|
||||
size_t bench_stl(unsigned n)
|
||||
{
|
||||
vector<string> tab;
|
||||
tab.resize(n);
|
||||
for(unsigned i = 0; i < n; i++)
|
||||
tab[i] = to_string(rand_get());
|
||||
|
||||
string str;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str += tab[permutation_tab[i]];
|
||||
}
|
||||
|
||||
replace_all_stl(str, "1234", "WELL");
|
||||
replace_all_stl(str, "56789", "DONE");
|
||||
|
||||
return str.length();
|
||||
}
|
||||
|
||||
size_t bench_mlib(unsigned n)
|
||||
{
|
||||
string_t *tab = (string_t*) malloc (n * sizeof (string_t));
|
||||
assert (tab != 0);
|
||||
// P1
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_init(tab[i]);
|
||||
string_printf(tab[i], "%u", rand_get());
|
||||
}
|
||||
// P2
|
||||
string_t str;
|
||||
string_init(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_cat(str, tab[permutation_tab[i]]);
|
||||
}
|
||||
// P3
|
||||
string_replace_all_str(str, "1234", "WELL");
|
||||
string_replace_all_str(str, "56789", "DONE");
|
||||
size_t length = string_size(str);
|
||||
// Clean
|
||||
string_clear(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
string_clear(tab[i]);
|
||||
}
|
||||
free(tab);
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
|
||||
static void replace_all_bstrlib(CBString &str, const char pattern[], const char replace[])
|
||||
{
|
||||
size_t pos = 0;
|
||||
do {
|
||||
pos = str.find(pattern, pos);
|
||||
if (pos != string::npos) {
|
||||
str.replace (pos, strlen(pattern), replace);
|
||||
}
|
||||
} while (pos != string::npos);
|
||||
}
|
||||
|
||||
size_t bench_bstrlib(unsigned n)
|
||||
{
|
||||
vector<CBString> tab;
|
||||
tab.resize(n);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
char tmp[10];
|
||||
sprintf(tmp, "%u", rand_get());
|
||||
tab[i] = tmp;
|
||||
}
|
||||
|
||||
CBString str;
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str += tab[permutation_tab[i]];
|
||||
}
|
||||
|
||||
replace_all_bstrlib(str, "1234", "WELL");
|
||||
replace_all_bstrlib(str, "56789", "DONE");
|
||||
|
||||
return str.length();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
sds SDS_replace_at(sds str, size_t pos, size_t len, const char str2[])
|
||||
{
|
||||
// simple implementation as replace is not available
|
||||
sds a = sdsnewlen((const void*) str, pos);
|
||||
a = sdscat(a, str2);
|
||||
a = sdscat(a, &str[pos+len]);
|
||||
sdsfree(str);
|
||||
return a;
|
||||
}
|
||||
|
||||
sds replace_all_sds(sds str, const char pattern[], const char replace[])
|
||||
{
|
||||
size_t pos = 0;
|
||||
do {
|
||||
char *p = strstr(&str[pos], pattern);
|
||||
pos = p == NULL ? -1U : (p - str);
|
||||
if (pos != -1U) {
|
||||
str = SDS_replace_at(str, pos, strlen(pattern), replace);
|
||||
}
|
||||
} while (pos != -1U);
|
||||
return str;
|
||||
}
|
||||
|
||||
size_t bench_sds(unsigned n)
|
||||
{
|
||||
sds *tab = (sds*) malloc (n * sizeof (sds));
|
||||
assert (tab != 0);
|
||||
// P1
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
tab[i] = sdsfromlonglong(rand_get());
|
||||
}
|
||||
// P2
|
||||
sds str = sdsempty();
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
str = sdscat(str, tab[permutation_tab[i]]);
|
||||
}
|
||||
// P3
|
||||
str = replace_all_sds(str, "1234", "WELL");
|
||||
str = replace_all_sds(str, "56789", "DONE");
|
||||
// Clean
|
||||
size_t length = sdslen(str);
|
||||
sdsfree(str);
|
||||
for(unsigned i = 0; i < n; i++) {
|
||||
sdsfree(tab[i]);
|
||||
}
|
||||
free(tab);
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
unsigned length, n = atoi(argv[1]);
|
||||
unsigned select = atoi(argv[2]);
|
||||
const char *name;
|
||||
random_permutation(n);
|
||||
rand_init();
|
||||
unsigned long long t0 = cputime();
|
||||
|
||||
switch (select) {
|
||||
case 0:
|
||||
length = bench_mlib(n);
|
||||
name = "MLIB";
|
||||
break;
|
||||
case 1:
|
||||
length = bench_stl(n);
|
||||
name = "STL";
|
||||
break;
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
case 2:
|
||||
length = bench_sds(n);
|
||||
name = "SDS";
|
||||
break;
|
||||
#endif
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
case 4:
|
||||
length = bench_bstrlib(n);
|
||||
name = "BSTRLIB";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
exit(0);
|
||||
}
|
||||
unsigned long long t1 = cputime();
|
||||
printf("%5.5s LENGTH=%u T= %f s\n", name, length, (t1-t0) / 1000000.0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
+590
-168
@@ -35,14 +35,27 @@
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Update of integrating M*LIB done by Patrick Pelissier */
|
||||
/* Update of integrating M*LIB, LIBSRT, SDS & GLIB
|
||||
Copyright (c) 2018-2021 Patrick Pelissier
|
||||
*/
|
||||
|
||||
/*
|
||||
* Benchmark based on the features tested by benchmark seen here:
|
||||
* http://www.utilitycode.com/str/performance.aspx
|
||||
*
|
||||
* The idea is to measure the performance empty constructors, char *
|
||||
* constructors, assignment, concatenation and scanning.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef BENCH_CAN_USE_MSTARLIB
|
||||
#define NDEBUG
|
||||
#include "common.h"
|
||||
|
||||
#ifdef BENCH_CAN_USE_MSTARLIB
|
||||
#include "m-string.h"
|
||||
#endif
|
||||
|
||||
@@ -56,55 +69,66 @@
|
||||
#include "sstring.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Benchmark based on the features tested by benchmark seen here:
|
||||
* http://www.utilitycode.com/str/performance.aspx
|
||||
*
|
||||
* The idea is to measure the performance empty constructors, char *
|
||||
* constructors, assignment, concatenation and scanning. But rather than
|
||||
* comparing MFC's CString to Str Library, we are going to compare STL to
|
||||
* CBString.
|
||||
*
|
||||
* The results are interesting. CBString basically beats std::string quite
|
||||
* severely, except for the empty constructor.
|
||||
*/
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
extern "C" {
|
||||
#include "sds.h"
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined (BENCH_CAN_USE_STL)
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#if defined(BENCH_CAN_USE_GLIB)
|
||||
#include "gmodule.h"
|
||||
#endif
|
||||
|
||||
/* Time of a test */
|
||||
#define TEST_SECONDS (5)
|
||||
|
||||
/*
|
||||
* This macro puts a memory barrier and prevents the optimizer from
|
||||
* optimizing too much the inline functions and literraly breaking
|
||||
* the benchmark by doing nothing.
|
||||
* Better than nothing, but far from perfect.
|
||||
*/
|
||||
#define BARRIER(addr) compiler_barrier((void*) addr)
|
||||
|
||||
bool print_csv = false;
|
||||
|
||||
int timeTest (double &res, int (*testfn) (int count), int count) {
|
||||
clock_t t0, t1;
|
||||
int p = 0;
|
||||
double x, c = 0;
|
||||
|
||||
t0 = clock ();
|
||||
|
||||
do {
|
||||
p += testfn (count);
|
||||
t1 = clock ();
|
||||
|
||||
c += count;
|
||||
if (t1 == t0) {
|
||||
if (count < (INT_MAX / 2)) count += count;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t1 - t0 >= TEST_SECONDS*CLOCKS_PER_SEC) break;
|
||||
|
||||
x = (TEST_SECONDS*CLOCKS_PER_SEC - (t1 - t0)) * c / (t1 - t0);
|
||||
|
||||
if (x > INT_MAX) x = INT_MAX;
|
||||
count = (int) x;
|
||||
if (count < 1000) count = 1000;
|
||||
|
||||
} while (1);
|
||||
|
||||
res = c * CLOCKS_PER_SEC / (t1 - t0);
|
||||
return p;
|
||||
clock_t t0, t1;
|
||||
int p = 0;
|
||||
double x, c = 0;
|
||||
|
||||
t0 = clock ();
|
||||
|
||||
do {
|
||||
p += testfn (count);
|
||||
t1 = clock ();
|
||||
|
||||
c += count;
|
||||
if (t1 == t0) {
|
||||
if (count < (INT_MAX / 2)) count += count;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t1 - t0 >= TEST_SECONDS*CLOCKS_PER_SEC) break;
|
||||
|
||||
x = (TEST_SECONDS*CLOCKS_PER_SEC - (t1 - t0)) * c / (t1 - t0);
|
||||
|
||||
if (x > INT_MAX) x = INT_MAX;
|
||||
count = (int) x;
|
||||
if (count < 1000) count = 1000;
|
||||
|
||||
} while (1);
|
||||
|
||||
res = c * CLOCKS_PER_SEC / (t1 - t0);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define SMALLTESTSTRING1 ("hello")
|
||||
|
||||
#define TESTSTRING1 ("<sometag name=\"John Doe\" position=\"Executive VP Marketing\"/>")
|
||||
|
||||
#if defined (BENCH_CAN_USE_STL)
|
||||
@@ -112,6 +136,7 @@ int testSTL_emptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (i=0; i < count; i++) {
|
||||
std::string b;
|
||||
BARRIER(&b);
|
||||
c += b.length () ^ i;
|
||||
}
|
||||
return c;
|
||||
@@ -121,6 +146,17 @@ int testSTL_nonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (i=0; i < count; i++) {
|
||||
std::string b (TESTSTRING1);
|
||||
BARRIER(&b);
|
||||
c += b.length () ^ i;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSTL_smallnonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (i=0; i < count; i++) {
|
||||
std::string b (SMALLTESTSTRING1);
|
||||
BARRIER(&b);
|
||||
c += b.length () ^ i;
|
||||
}
|
||||
return c;
|
||||
@@ -132,6 +168,7 @@ int testSTL_cstrAssignment (int count) {
|
||||
|
||||
for (i=0; i < count; i++) {
|
||||
b = TESTSTRING1;
|
||||
BARRIER(&b);
|
||||
c += b.length () ^ i;
|
||||
}
|
||||
return c;
|
||||
@@ -145,6 +182,7 @@ int testSTL_extraction (int count) {
|
||||
c += b[(i & 7)];
|
||||
c += b[(i & 7) ^ 8];
|
||||
c += b.c_str()[(i & 7) ^ 4] ^ i;
|
||||
BARRIER(&b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -157,6 +195,7 @@ int i, c = 0;
|
||||
c += b.find ('.');
|
||||
c += b.find ("123");
|
||||
c += b.find_first_of ("sm") ^i;
|
||||
BARRIER(&b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -171,6 +210,7 @@ int testSTL_concat (int count) {
|
||||
for (i=0; i < 250; i++) {
|
||||
accum += a;
|
||||
accum += "!!";
|
||||
BARRIER(&accum);
|
||||
c += accum.length() ^i;
|
||||
}
|
||||
}
|
||||
@@ -185,6 +225,7 @@ int testSTL_replace (int count) {
|
||||
a.replace (11, 4, "XXXXXX");
|
||||
a.replace (23, 2, "XXXXXX");
|
||||
a.replace ( 4, 8, "XX");
|
||||
BARRIER(&a);
|
||||
c += a.length () ^ j;
|
||||
}
|
||||
return c;
|
||||
@@ -197,6 +238,7 @@ int testCBS_emptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (i=0; i < count; i++) {
|
||||
CBString b;
|
||||
BARRIER(&b);
|
||||
c += b.length ()^i;
|
||||
}
|
||||
return c;
|
||||
@@ -206,6 +248,17 @@ int testCBS_nonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (i=0; i < count; i++) {
|
||||
CBString b (TESTSTRING1);
|
||||
BARRIER(&b);
|
||||
c += b.length () ^i;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testCBS_smallnonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (i=0; i < count; i++) {
|
||||
CBString b (SMALLTESTSTRING1);
|
||||
BARRIER(&b);
|
||||
c += b.length () ^i;
|
||||
}
|
||||
return c;
|
||||
@@ -217,6 +270,7 @@ int testCBS_cstrAssignment (int count) {
|
||||
|
||||
for (i=0; i < count; i++) {
|
||||
b = TESTSTRING1;
|
||||
BARRIER(&b);
|
||||
c += b.length () ^i;
|
||||
}
|
||||
return c;
|
||||
@@ -230,6 +284,7 @@ int testCBS_extraction (int count) {
|
||||
c += b[(i & 7)];
|
||||
c += b[(i & 7) ^ 8];
|
||||
c += ((const char *)b)[(i & 7) ^ 4] ^i;
|
||||
BARRIER(&b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -242,6 +297,7 @@ int testCBS_scan (int count) {
|
||||
c += b.find ('.');
|
||||
c += b.find ("123");
|
||||
c += b.findchr ("sm") ^i;
|
||||
BARRIER(&b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -256,6 +312,7 @@ int testCBS_concat (int count) {
|
||||
for (i=0; i < 250; i++) {
|
||||
accum += a;
|
||||
accum += "!!";
|
||||
BARRIER(&accum);
|
||||
c+= accum.length() ^i;
|
||||
}
|
||||
}
|
||||
@@ -270,6 +327,7 @@ int testCBS_replace (int count) {
|
||||
a.replace (11, 4, "XXXXXX");
|
||||
a.replace (23, 2, "XXXXXX");
|
||||
a.replace ( 4, 8, "XX");
|
||||
BARRIER(&a);
|
||||
c += a.length () ^j;
|
||||
}
|
||||
return c;
|
||||
@@ -280,7 +338,8 @@ int testCBS_replace (int count) {
|
||||
int testSRT_emptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
ss_t *b = ss_alloc(0);
|
||||
srt_string *b = ss_alloc(0);
|
||||
BARRIER(b);
|
||||
c += ss_size(b) ^i;
|
||||
ss_free(&b);
|
||||
}
|
||||
@@ -290,7 +349,19 @@ int testSRT_emptyCtor (int count) {
|
||||
int testSRT_nonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
ss_t *b = ss_dup_c(TESTSTRING1);
|
||||
srt_string *b = ss_dup_c(TESTSTRING1);
|
||||
BARRIER(b);
|
||||
c += ss_size(b) ^i;
|
||||
ss_free(&b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSRT_smallnonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
srt_string *b = ss_dup_c(SMALLTESTSTRING1);
|
||||
BARRIER(b);
|
||||
c += ss_size(b) ^i;
|
||||
ss_free(&b);
|
||||
}
|
||||
@@ -299,9 +370,10 @@ int testSRT_nonemptyCtor (int count) {
|
||||
|
||||
int testSRT_cstrAssignment (int count) {
|
||||
int i, c = 0;
|
||||
ss_t *b = ss_alloc(0);
|
||||
srt_string *b = ss_alloc(0);
|
||||
for (c=i=0; i < count; i++) {
|
||||
ss_cpy_cn(&b, TESTSTRING1, strlen(TESTSTRING1));
|
||||
BARRIER(b);
|
||||
c += ss_size(b) ^i;
|
||||
}
|
||||
ss_free(&b);
|
||||
@@ -310,11 +382,12 @@ int testSRT_cstrAssignment (int count) {
|
||||
|
||||
int testSRT_extraction (int count) {
|
||||
int i, c = 0;
|
||||
ss_t *b = ss_dup_c(TESTSTRING1);
|
||||
srt_string *b = ss_dup_c(TESTSTRING1);
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += ss_at(b,(i & 7));
|
||||
c += ss_at(b,(i & 7) ^ 8);
|
||||
c += ss_at(b,(i & 7) ^ 4) ^i;
|
||||
BARRIER(b);
|
||||
}
|
||||
ss_free(&b);
|
||||
return c;
|
||||
@@ -322,12 +395,13 @@ int testSRT_extraction (int count) {
|
||||
|
||||
int testSRT_scan (int count) {
|
||||
int i, c = 0;
|
||||
ss_t *b = ss_dup_c("Dot. 123. Some more data.");
|
||||
srt_string *b = ss_dup_c("Dot. 123. Some more data.");
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += ss_findc (b, 0, '.');
|
||||
c += ss_find_cn (b, 0, "123", strlen("123"));
|
||||
//c += ss_pbrk (b, 0, "sm") ^i;
|
||||
c += ss_findb (b, 0) ^i;
|
||||
BARRIER(b);
|
||||
}
|
||||
ss_free(&b);
|
||||
return c;
|
||||
@@ -336,14 +410,15 @@ int testSRT_scan (int count) {
|
||||
|
||||
int testSRT_concat (int count) {
|
||||
int i, j, c = 0;
|
||||
ss_t *a = ss_dup_c(TESTSTRING1);
|
||||
ss_t *accum = ss_alloc(0);
|
||||
srt_string *a = ss_dup_c(TESTSTRING1);
|
||||
srt_string *accum = ss_alloc(0);
|
||||
|
||||
for (j=0; j < count; j++) {
|
||||
ss_cpy_c(&accum, "");
|
||||
for (i=0; i < 250; i++) {
|
||||
ss_cat(&accum, a, NULL);
|
||||
ss_cat_c(&accum, "!!", NULL);
|
||||
BARRIER(accum);
|
||||
c += ss_size(accum) ^i;
|
||||
}
|
||||
}
|
||||
@@ -360,6 +435,7 @@ int testMLIB_emptyCtor (int count) {
|
||||
for (c=i=0; i < count; i++) {
|
||||
string_t b;
|
||||
string_init(b);
|
||||
BARRIER(&b);
|
||||
c += string_size(b) ^i;
|
||||
string_clear(b);
|
||||
}
|
||||
@@ -371,6 +447,19 @@ int testMLIB_nonemptyCtor (int count) {
|
||||
for (c=i=0; i < count; i++) {
|
||||
string_t b;
|
||||
string_init_set_str(b, TESTSTRING1);
|
||||
BARRIER(&b);
|
||||
c += string_size(b) ^i;
|
||||
string_clear(b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testMLIB_smallnonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
string_t b;
|
||||
string_init_set_str(b, SMALLTESTSTRING1);
|
||||
BARRIER(&b);
|
||||
c += string_size(b) ^i;
|
||||
string_clear(b);
|
||||
}
|
||||
@@ -383,6 +472,7 @@ int testMLIB_cstrAssignment (int count) {
|
||||
string_init(b);
|
||||
for (c=i=0; i < count; i++) {
|
||||
string_set_str(b, TESTSTRING1);
|
||||
BARRIER(&b);
|
||||
c += string_size(b) ^i;
|
||||
}
|
||||
string_clear(b);
|
||||
@@ -399,6 +489,7 @@ int testMLIB_extraction (int count) {
|
||||
c += string_get_char(b, (i & 7));
|
||||
c += string_get_char(b, (i & 7) ^ 8);
|
||||
c += string_get_char(b, (i & 7) ^ 4) ^i;
|
||||
BARRIER(&b);
|
||||
}
|
||||
string_clear(b);
|
||||
return c;
|
||||
@@ -413,12 +504,12 @@ int testMLIB_scan (int count) {
|
||||
c += string_search_char (b, '.');
|
||||
c += string_search_str (b, "123");
|
||||
c += string_search_pbrk (b, "sm") ^i;
|
||||
BARRIER(&b);
|
||||
}
|
||||
string_clear(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
int testMLIB_concat (int count) {
|
||||
int i, j, c = 0;
|
||||
string_t a, accum;
|
||||
@@ -430,6 +521,7 @@ int testMLIB_concat (int count) {
|
||||
for (i=0; i < 250; i++) {
|
||||
string_cat(accum, a);
|
||||
string_cat_str(accum, "!!");
|
||||
BARRIER(&accum);
|
||||
c += string_size(accum) ^i;
|
||||
}
|
||||
}
|
||||
@@ -447,6 +539,7 @@ int testMLIB_replace (int count) {
|
||||
string_replace_at(a, 11, 4, "XXXXXX");
|
||||
string_replace_at(a, 23, 2, "XXXXXX");
|
||||
string_replace_at(a, 4, 8, "XX");
|
||||
BARRIER(&a);
|
||||
c += string_size(a) ^j;
|
||||
}
|
||||
|
||||
@@ -455,141 +548,470 @@ int testMLIB_replace (int count) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#define NTESTS 7
|
||||
struct flags {
|
||||
int runtest[NTESTS];
|
||||
};
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
int testSDS_emptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
sds b;
|
||||
b = sdsempty();
|
||||
BARRIER(b);
|
||||
c += sdslen(b) ^ i;
|
||||
sdsfree(b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int benchTest (const struct flags * runflags) {
|
||||
int c = 0;
|
||||
double cps;
|
||||
int testSDS_nonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
sds b;
|
||||
b = sdsnew(TESTSTRING1);
|
||||
BARRIER(b);
|
||||
c += sdslen(b) ^i;
|
||||
sdsfree(b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSDS_smallnonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
sds b;
|
||||
b = sdsnew(SMALLTESTSTRING1);
|
||||
BARRIER(b);
|
||||
c += sdslen(b) ^i;
|
||||
sdsfree(b);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSDS_cstrAssignment (int count) {
|
||||
int i, c = 0;
|
||||
sds b;
|
||||
b = sdsempty();
|
||||
for (c=i=0; i < count; i++) {
|
||||
b = sdscpy(b, TESTSTRING1);
|
||||
BARRIER(b);
|
||||
c += sdslen(b) ^i;
|
||||
}
|
||||
sdsfree(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSDS_extraction (int count) {
|
||||
int i, c = 0;
|
||||
|
||||
sds b;
|
||||
b = sdsnew(TESTSTRING1);
|
||||
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += b[(i & 7)];
|
||||
c += b[(i & 7) ^ 8];
|
||||
c += b[(i & 7) ^ 4] ^i;
|
||||
BARRIER(b);
|
||||
}
|
||||
sdsfree(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
int testSDS_scan (int count) {
|
||||
int i, c = 0;
|
||||
sds b;
|
||||
b = sdsnew("Dot. 123. Some more data.");
|
||||
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += (intptr_t) strchr(b, '.');
|
||||
c += (intptr_t) strstr (b, "123");
|
||||
c += (intptr_t) strpbrk (b, "sm") ^i;
|
||||
BARRIER(b);
|
||||
}
|
||||
sdsfree(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
int testSDS_concat (int count) {
|
||||
int i, j, c = 0;
|
||||
sds a, accum;
|
||||
a = sdsnew(TESTSTRING1);
|
||||
accum = sdsempty();
|
||||
|
||||
for (j=0; j < count; j++) {
|
||||
accum = sdscpy(accum, "");
|
||||
for (i=0; i < 250; i++) {
|
||||
accum = sdscat(accum, a);
|
||||
accum = sdscat(accum, "!!");
|
||||
BARRIER(accum);
|
||||
c += sdslen(accum) ^i;
|
||||
}
|
||||
}
|
||||
sdsfree(a);
|
||||
sdsfree(accum);
|
||||
return c;
|
||||
}
|
||||
|
||||
sds SDS_replace_at(sds str, size_t pos, size_t len, const char str2[])
|
||||
{
|
||||
// simple implementation as replace is not available
|
||||
sds a = sdsnewlen((const void*) str, pos);
|
||||
a = sdscat(a, str2);
|
||||
a = sdscat(a, &str[pos+len]);
|
||||
sdsfree(str);
|
||||
return a;
|
||||
}
|
||||
|
||||
int testSDS_replace (int count) {
|
||||
int j, c = 0;
|
||||
sds a;
|
||||
a = sdsnew(TESTSTRING1);
|
||||
for (j=0; j < count; j++) {
|
||||
a = SDS_replace_at(a, 11, 4, "XXXXXX");
|
||||
a = SDS_replace_at(a, 23, 2, "XXXXXX");
|
||||
a = SDS_replace_at(a, 4, 8, "XX");
|
||||
BARRIER(&a);
|
||||
c += sdslen(a) ^j;
|
||||
}
|
||||
sdsfree(a);
|
||||
return c;
|
||||
}
|
||||
|
||||
#if defined (BENCH_CAN_USE_STL)
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testSTL_emptyCtor, 100000);
|
||||
printf ("std::string empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testSTL_nonemptyCtor, 100000);
|
||||
printf ("std::string non-empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testSTL_cstrAssignment, 100000);
|
||||
printf ("std::string char * assignment: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testSTL_extraction, 100000);
|
||||
printf ("std::string char extraction: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testSTL_scan, 100000);
|
||||
printf ("std::string scan: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testSTL_concat, 10);
|
||||
printf ("std::string concatenation: %20.1f per second\n", cps * 250);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testSTL_replace, 10000);
|
||||
printf ("std::string replace: %20.1f per second\n", cps);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef BENCH_CAN_USE_GLIB
|
||||
int testGLIB_emptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
GString *b = g_string_new("");
|
||||
BARRIER(b);
|
||||
c += b->len;
|
||||
g_string_free(b, TRUE);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testGLIB_nonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
GString *b = g_string_new(TESTSTRING1);
|
||||
BARRIER(b);
|
||||
c += b->len;
|
||||
g_string_free(b, TRUE);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testGLIB_smallnonemptyCtor (int count) {
|
||||
int i, c = 0;
|
||||
for (c=i=0; i < count; i++) {
|
||||
GString *b = g_string_new(SMALLTESTSTRING1);
|
||||
BARRIER(b);
|
||||
c += b->len;
|
||||
g_string_free(b, TRUE);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int testGLIB_cstrAssignment (int count) {
|
||||
int i, c = 0;
|
||||
GString *b = g_string_new("");
|
||||
for (c=i=0; i < count; i++) {
|
||||
b = g_string_assign(b, TESTSTRING1);
|
||||
BARRIER(b);
|
||||
c += b->len ^i;
|
||||
}
|
||||
g_string_free(b, TRUE);
|
||||
return c;
|
||||
}
|
||||
|
||||
int testGLIB_extraction (int count) {
|
||||
int i, c = 0;
|
||||
GString *b = g_string_new(TESTSTRING1);
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += b->str[(i & 7)];
|
||||
c += b->str[(i & 7) ^ 8];
|
||||
c += b->str[(i & 7) ^ 4] ^i;
|
||||
BARRIER(b);
|
||||
}
|
||||
g_string_free(b, TRUE);
|
||||
return c;
|
||||
}
|
||||
|
||||
int testGLIB_scan (int count) {
|
||||
int i, c = 0;
|
||||
GString *b = g_string_new("Dot. 123. Some more data.");
|
||||
|
||||
for (c=i=0; i < count; i++) {
|
||||
c += (strchr(b->str, '.')-b->str);
|
||||
c += (strstr (b->str, "123")-b->str);
|
||||
c += (strpbrk (b->str, "sm")-b->str) ^i;
|
||||
BARRIER(b);
|
||||
}
|
||||
g_string_free(b, TRUE);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
int testGLIB_concat (int count) {
|
||||
int i, j, c = 0;
|
||||
GString *a = g_string_new(TESTSTRING1);
|
||||
GString *accum = g_string_new("");
|
||||
|
||||
for (j=0; j < count; j++) {
|
||||
accum = g_string_assign(accum, "");
|
||||
for (i=0; i < 250; i++) {
|
||||
accum = g_string_append(accum, a->str);
|
||||
accum = g_string_append(accum, "!!");
|
||||
BARRIER(accum);
|
||||
c += accum->len ^i;
|
||||
}
|
||||
}
|
||||
g_string_free(a, TRUE);
|
||||
g_string_free(accum, TRUE);
|
||||
return c;
|
||||
}
|
||||
|
||||
//int testGLIB_replace (int count): not available
|
||||
#endif
|
||||
|
||||
|
||||
#define NTESTS 8
|
||||
struct flags {
|
||||
int runtest[NTESTS];
|
||||
};
|
||||
|
||||
static void
|
||||
print(const char library[], const char function[], double cps)
|
||||
{
|
||||
if (print_csv == false)
|
||||
{
|
||||
printf ("%16s %30s : %20.1f per second\n", library, function, cps);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("\"%s\";\"%s\";%f\n", library, function, cps);
|
||||
}
|
||||
}
|
||||
|
||||
int benchTest (const struct flags * runflags) {
|
||||
int c = 0;
|
||||
double cps;
|
||||
|
||||
#if defined (BENCH_CAN_USE_STL)
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testSTL_emptyCtor, 100000);
|
||||
print ("std::string", "empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testSTL_nonemptyCtor, 100000);
|
||||
print ("std::string", "non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testSTL_smallnonemptyCtor, 100000);
|
||||
print ("std::string", "small non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testSTL_cstrAssignment, 100000);
|
||||
print ("std::string", "char * assignment", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testSTL_extraction, 100000);
|
||||
print ("std::string", "char extraction", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testSTL_scan, 100000);
|
||||
print ("std::string", "scan", cps);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testSTL_concat, 10);
|
||||
print ("std::string", "concatenation", cps * 250);
|
||||
}
|
||||
if (runflags->runtest[7]) {
|
||||
c += timeTest (cps, testSTL_replace, 10000);
|
||||
print ("std::string", "replace", cps);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_BSTRLIB
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testCBS_emptyCtor, 100000);
|
||||
printf ("CBString empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testCBS_nonemptyCtor, 100000);
|
||||
printf ("CBString non-empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testCBS_cstrAssignment, 100000);
|
||||
printf ("CBString char * assignment: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testCBS_extraction, 100000);
|
||||
printf ("CBString char extraction: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testCBS_scan, 100000);
|
||||
printf ("CBString scan: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testCBS_concat, 10);
|
||||
printf ("CBString concatenation: %20.1f per second\n", cps * 250);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testCBS_replace, 10000);
|
||||
printf ("CBString replace: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testCBS_emptyCtor, 100000);
|
||||
print ("CBString", "empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testCBS_nonemptyCtor, 100000);
|
||||
print ("CBString", "non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testCBS_smallnonemptyCtor, 100000);
|
||||
print ("CBString", "small non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testCBS_cstrAssignment, 100000);
|
||||
print ("CBString", "char * assignment", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testCBS_extraction, 100000);
|
||||
print ("CBString", "char extraction", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testCBS_scan, 100000);
|
||||
print ("CBString", "scan", cps);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testCBS_concat, 10);
|
||||
print ("CBString", "concatenation", cps * 250);
|
||||
}
|
||||
if (runflags->runtest[7]) {
|
||||
c += timeTest (cps, testCBS_replace, 10000);
|
||||
print ("CBString", "replace", cps);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_LIBSRT
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testSRT_emptyCtor, 100000);
|
||||
printf ("LIBSRT string empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testSRT_nonemptyCtor, 100000);
|
||||
printf ("LIBSRT string non-empty constructor:%20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testSRT_cstrAssignment, 100000);
|
||||
printf ("LIBSRT string char * assignment: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testSRT_extraction, 100000);
|
||||
printf ("LIBSRT string char extraction: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testSRT_scan, 100000);
|
||||
printf ("LIBSRT string scan: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testSRT_concat, 10);
|
||||
printf ("LIBSRT string concatenation: %20.1f per second\n", cps * 250);
|
||||
}
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testSRT_emptyCtor, 100000);
|
||||
print ("LIBSRT", "empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testSRT_nonemptyCtor, 100000);
|
||||
print ("LIBSRT", "non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testSRT_smallnonemptyCtor, 100000);
|
||||
print ("LIBSRT", "small non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testSRT_cstrAssignment, 100000);
|
||||
print ("LIBSRT", "char * assignment", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testSRT_extraction, 100000);
|
||||
print ("LIBSRT", "char extraction", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testSRT_scan, 100000);
|
||||
print ("LIBSRT", "scan", cps);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testSRT_concat, 10);
|
||||
print ("LIBSRT", "concatenation", cps * 250);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_MSTARLIB
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testMLIB_emptyCtor, 100000);
|
||||
print ("M*LIB", "empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testMLIB_nonemptyCtor, 100000);
|
||||
print ("M*LIB", "non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testMLIB_smallnonemptyCtor, 100000);
|
||||
print ("M*LIB", "small non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testMLIB_cstrAssignment, 100000);
|
||||
print ("M*LIB", "Char * assignment", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testMLIB_extraction, 100000);
|
||||
print ("M*LIB", "char extraction", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testMLIB_scan, 100000);
|
||||
print ("M*LIB", "scan", cps);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testMLIB_concat, 10);
|
||||
print ("M*LIB", "concatenation", cps * 250);
|
||||
}
|
||||
if (runflags->runtest[7]) {
|
||||
c += timeTest (cps, testMLIB_replace, 10000);
|
||||
print ("M*LIB", "replace", cps);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BENCH_CAN_USE_MSTARLIB
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testMLIB_emptyCtor, 100000);
|
||||
printf ("M*LIB string empty constructor: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testMLIB_nonemptyCtor, 100000);
|
||||
printf ("M*LIB string non-empty constructor:%20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testMLIB_cstrAssignment, 100000);
|
||||
printf ("M*LIB string char * assignment: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testMLIB_extraction, 100000);
|
||||
printf ("M*LIB string char extraction: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testMLIB_scan, 100000);
|
||||
printf ("M*LIB string scan: %20.1f per second\n", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testMLIB_concat, 10);
|
||||
printf ("M*LIB string concatenation: %20.1f per second\n", cps * 250);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testMLIB_replace, 10000);
|
||||
printf ("M*LIB string replace: %20.1f per second\n", cps);
|
||||
}
|
||||
#ifdef BENCH_CAN_USE_SDS
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testSDS_emptyCtor, 100000);
|
||||
print ("SDS", "empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testSDS_nonemptyCtor, 100000);
|
||||
print ("SDS", "non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testSDS_smallnonemptyCtor, 100000);
|
||||
print ("SDS", "small non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testSDS_cstrAssignment, 100000);
|
||||
print ("SDS", "char * assignment", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testSDS_extraction, 100000);
|
||||
print ("SDS", "char extraction", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testSDS_scan, 100000);
|
||||
print ("SDS", "scan", cps);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testSDS_concat, 10);
|
||||
print ("SDS", "concatenation", cps * 250);
|
||||
}
|
||||
if (runflags->runtest[7]) {
|
||||
c += timeTest (cps, testSDS_replace, 10);
|
||||
print ("SDS", "replace", cps);
|
||||
}
|
||||
#endif
|
||||
return c;
|
||||
|
||||
#ifdef BENCH_CAN_USE_GLIB
|
||||
if (runflags->runtest[0]) {
|
||||
c += timeTest (cps, testGLIB_emptyCtor, 100000);
|
||||
print ("GLIB", "empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[1]) {
|
||||
c += timeTest (cps, testGLIB_nonemptyCtor, 100000);
|
||||
print ("GLIB", "non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[2]) {
|
||||
c += timeTest (cps, testGLIB_smallnonemptyCtor, 100000);
|
||||
print ("GLIB", "small non-empty constructor", cps);
|
||||
}
|
||||
if (runflags->runtest[3]) {
|
||||
c += timeTest (cps, testGLIB_cstrAssignment, 100000);
|
||||
print ("GLIB", "Char * assignment", cps);
|
||||
}
|
||||
if (runflags->runtest[4]) {
|
||||
c += timeTest (cps, testGLIB_extraction, 100000);
|
||||
print ("GLIB", "char extraction", cps);
|
||||
}
|
||||
if (runflags->runtest[5]) {
|
||||
c += timeTest (cps, testGLIB_scan, 100000);
|
||||
print ("GLIB", "scan", cps);
|
||||
}
|
||||
if (runflags->runtest[6]) {
|
||||
c += timeTest (cps, testGLIB_concat, 10);
|
||||
print ("GLIB", "concatenation", cps * 250);
|
||||
}
|
||||
#endif
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int main (int argc, char * argv[]) {
|
||||
struct flags runflags;
|
||||
int i, j;
|
||||
|
||||
if (argc > 1 && strcmp(argv[1], "--csv") == 0) {
|
||||
print_csv = true;
|
||||
argc --;
|
||||
argv++;
|
||||
}
|
||||
|
||||
for (i=0; i < NTESTS; i++) runflags.runtest[i] = argc < 2;
|
||||
for (i=1; i < argc; i++) {
|
||||
if (1 == sscanf (argv[i], "%d", &j)) {
|
||||
|
||||
+11
-13
@@ -124,7 +124,7 @@ static int equal_func(const void* arg, const void *obj_p)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
tommy_hashlin dict;
|
||||
tommy_hashlin_init(&dict);
|
||||
@@ -174,7 +174,7 @@ static size_t char_hash(const char *s)
|
||||
}
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
tommy_hashlin dict;
|
||||
tommy_hashlin_init(&dict);
|
||||
@@ -206,18 +206,16 @@ test_dict_big(unsigned long n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 30, "Rbtree", 1000000, 0, test_rbtree, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 30)
|
||||
test_function("Rbtree time", 1000000, test_rbtree);
|
||||
if (n == 40)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
test("TommyDS", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
+12
-14
@@ -1,4 +1,5 @@
|
||||
#define NDEBUG
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -85,7 +86,7 @@ struct hash_i_node_s {
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict(unsigned long n)
|
||||
test_dict(size_t n)
|
||||
{
|
||||
struct hash_i_node_s *dict = NULL;
|
||||
|
||||
@@ -131,7 +132,7 @@ struct hash_b_node_s {
|
||||
};
|
||||
|
||||
static void
|
||||
test_dict_big(unsigned long n)
|
||||
test_dict_big(size_t n)
|
||||
{
|
||||
struct hash_b_node_s *dict = NULL;
|
||||
|
||||
@@ -199,19 +200,16 @@ static void test_sort(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 10, "List", 10000000, 0, test_list, 0},
|
||||
{ 20, "Array", 100000000, 0, test_array, 0},
|
||||
{ 40, "dict", 1000000, 0, test_dict, 0},
|
||||
{ 41, "dictBig", 1000000, 0, test_dict_big, 0},
|
||||
{ 50, "sort", 10000000, 0, test_sort, 0}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]): 0;
|
||||
if (n == 10)
|
||||
test_function("List time",10000000, test_list);
|
||||
if (n == 20)
|
||||
test_function("Array time", 100000000, test_array);
|
||||
if (n == 40 || n == 42)
|
||||
test_function("Dict time", 1000000, test_dict);
|
||||
if (n == 41)
|
||||
test_function("DictB time", 1000000, test_dict_big);
|
||||
if (n == 50)
|
||||
test_function("Sort time", 10000000, test_sort);
|
||||
test("UT-HASH", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -31,17 +31,12 @@ static void test_hash(size_t n)
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
const config_func_t table[] = {
|
||||
{ 70,"XXHASH64", 100000000, test_hash_prepare, test_hash, test_hash_final}
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int n = (argc > 1) ? atoi(argv[1]) : 0;
|
||||
if (n == 70) {
|
||||
n = (argc > 2) ? atoi(argv[2]) : 100000000;
|
||||
test_hash_prepare(n);
|
||||
test_function("XXHASH64 time", n, test_hash);
|
||||
test_hash_final();
|
||||
}
|
||||
|
||||
test("XXHASH", numberof(table), table, argc, argv);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* M*LIB - common functions for bench module
|
||||
*
|
||||
* Copyright (c) 2017-2021, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "common.h"
|
||||
|
||||
// Maximum supported value for repeat argument
|
||||
#define MAX_REPEAT 10000
|
||||
|
||||
// The global result
|
||||
unsigned long g_result;
|
||||
|
||||
// The rand value
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
M_THREAD_ATTR
|
||||
#endif
|
||||
unsigned int randValue = 0;
|
||||
|
||||
void compiler_barrier(void *p)
|
||||
{
|
||||
// Do nothing, but let the compiler thinks that we did.
|
||||
(void) p;
|
||||
// Can be used for FLTO building
|
||||
// asm volatile("": "+r" (p) : :"memory");
|
||||
}
|
||||
|
||||
struct parse_opt_s {
|
||||
int test_function;
|
||||
double from, to, step, grow;
|
||||
double within;
|
||||
unsigned repeat;
|
||||
bool graph, best, best_within, average, quiet;
|
||||
};
|
||||
|
||||
static void
|
||||
parse_config(struct parse_opt_s *opt, int argc, const char *argv[])
|
||||
{
|
||||
char *end;
|
||||
|
||||
opt->test_function = 0;
|
||||
opt->from = 0.0;
|
||||
opt->to = 0.0;
|
||||
opt->step = 0.0;
|
||||
opt->grow = 1.1;
|
||||
opt->graph = false;
|
||||
opt->best = false;
|
||||
opt->best_within = false;
|
||||
opt->average = false;
|
||||
opt->quiet = false;
|
||||
opt->repeat = 1;
|
||||
opt->within = .05;
|
||||
|
||||
for(int i = 1; i < argc ; i++)
|
||||
{
|
||||
if (argv[i][0] == '-') {
|
||||
if (strcmp(argv[i], "--from") == 0){
|
||||
i++;
|
||||
opt->from = strtod(argv[i], &end);
|
||||
opt->to = opt->from;
|
||||
} else if (strcmp(argv[i], "--to") == 0) {
|
||||
i++;
|
||||
opt->to = strtod(argv[i], &end);
|
||||
opt->grow = 1.1;
|
||||
} else if (strcmp(argv[i], "--step") == 0) {
|
||||
i++;
|
||||
opt->step = strtod(argv[i], &end);
|
||||
opt->grow = 0;
|
||||
} else if (strcmp(argv[i], "--grow") == 0) {
|
||||
i++;
|
||||
opt->grow = strtod(argv[i], &end);
|
||||
opt->step = 0;
|
||||
} else if (strcmp(argv[i], "--repeat") == 0) {
|
||||
i++;
|
||||
opt->repeat = strtol(argv[i], &end, 10);
|
||||
opt->repeat = (opt->repeat > MAX_REPEAT) ? MAX_REPEAT : opt->repeat;
|
||||
} else if (strcmp(argv[i], "--graph") == 0) {
|
||||
opt->graph = true;
|
||||
} else if (strcmp(argv[i], "--best") == 0) {
|
||||
opt->best = true;
|
||||
opt->average = false;
|
||||
opt->best_within = false;
|
||||
} else if (strcmp(argv[i], "--best-within") == 0) {
|
||||
opt->best_within = true;
|
||||
opt->best = false;
|
||||
opt->average = false;
|
||||
i++;
|
||||
opt->within = strtod(argv[i], &end) / 100.0;
|
||||
} else if (strcmp(argv[i], "--average") == 0) {
|
||||
opt->average = true;
|
||||
opt->best = false;
|
||||
opt->best_within = false;
|
||||
} else if (strcmp(argv[i], "--quiet") == 0) {
|
||||
opt->quiet = true;
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Option unkown: %s.\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
if (*end != 0) {
|
||||
fprintf(stderr, "ERROR: Cannot parse %s. Expected float\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
} else {
|
||||
opt->test_function = strtol (argv[i], &end, 10);
|
||||
if (*end != 0) {
|
||||
fprintf(stderr, "ERROR: Cannot parse %s. Expected function number\n",
|
||||
argv[i]);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
select_config(int func, size_t n, const config_func_t functions[])
|
||||
{
|
||||
for(size_t i = 0; i < n;i++) {
|
||||
if (functions[i].num == func)
|
||||
return i;
|
||||
}
|
||||
fprintf(stderr, "ERROR: Function number %d not found. Available functions are:\n", func);
|
||||
for(size_t i = 0; i < n;i++) {
|
||||
fprintf(stderr, " %d: %s\n", functions[i].num, functions[i].funcname);
|
||||
}
|
||||
fprintf(stderr,
|
||||
"USAGE: FUNC_NUMBER [--from number --to number (--grow number | --step number)] [--grapĥ]\n"
|
||||
"[--repeat number] [--best|--average|--best-within] [--quiet]\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Utility function for qsort
|
||||
static int cmp_double(const void *pa, const void *pb)
|
||||
{
|
||||
const double *a = (const double *) pa, *b = (const double *)pb;
|
||||
return (*a < *b) ? -1 : *a > *b;
|
||||
}
|
||||
|
||||
/* Get the longest sequence of values within 'f'% */
|
||||
static int
|
||||
get_sequence(int i, int n, double tab[], double f)
|
||||
{
|
||||
int j;
|
||||
for(j = i + 1; j < n; j++) {
|
||||
if (tab[j] > f * tab[i])
|
||||
break;
|
||||
}
|
||||
return j-i;
|
||||
}
|
||||
|
||||
/* Return the minimum of the longest sequence of values with 'f'% */
|
||||
static double
|
||||
get_best_within(int n, double tab[], double f)
|
||||
{
|
||||
qsort(tab, n, sizeof(double), cmp_double);
|
||||
int best_i = 0;
|
||||
int best_seq = get_sequence(0, n, tab, f);
|
||||
for(int i = 1; i < n && (n-i) > best_seq ; i++) {
|
||||
int seq = get_sequence(i, n, tab, f);
|
||||
if (seq > best_seq) {
|
||||
best_i = i;
|
||||
best_seq = seq;
|
||||
}
|
||||
}
|
||||
return tab[best_i];
|
||||
}
|
||||
|
||||
// Integer sqrt function (to avoid depending on libm)
|
||||
static unsigned integer_sqrt(unsigned n)
|
||||
{
|
||||
// Find greatest shift.
|
||||
int shift = 2;
|
||||
unsigned nShifted = n >> shift;
|
||||
// We check for nShifted being n, since some implementations
|
||||
// perform shift operations modulo the word size.
|
||||
while (nShifted != 0 && nShifted != n){
|
||||
shift += 2;
|
||||
nShifted = n >> shift;
|
||||
}
|
||||
shift -= 2;
|
||||
// Find digits of result.
|
||||
unsigned result = 0;
|
||||
while (shift >= 0) {
|
||||
result = result << 1;
|
||||
unsigned candidateResult = result + 1;
|
||||
if (candidateResult*candidateResult <= n >> shift) {
|
||||
result = candidateResult;
|
||||
}
|
||||
shift -= 2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[])
|
||||
{
|
||||
// Init & parse arguments
|
||||
// NUMBER
|
||||
// --from NUMBER --to NUMBER --step NUMBER --grow NUMBER
|
||||
// --graph --best --average --quiet
|
||||
// --repeat N
|
||||
struct parse_opt_s arg;
|
||||
parse_config(&arg, argc, argv);
|
||||
int i = select_config(arg.test_function, n, functions);
|
||||
double from = (arg.from == 0) ? functions[i].default_n : arg.from;
|
||||
double to = (arg.to == 0) ? functions[i].default_n : arg.to;
|
||||
|
||||
// Open plot file if needed
|
||||
FILE *graph_file = NULL;
|
||||
if (arg.graph) {
|
||||
char filename[100];
|
||||
sprintf(filename, "plot-%s-%d.dat", library, arg.test_function);
|
||||
graph_file = fopen(filename, "wt");
|
||||
if (!graph_file) {
|
||||
fprintf(stderr, "ERROR: Cannot create plot.dat\n");
|
||||
exit(-2);
|
||||
}
|
||||
fprintf(graph_file, "# plotting %s-%d : %s\n# N T", library, arg.test_function, functions[i].funcname);
|
||||
}
|
||||
|
||||
// Do the bench
|
||||
for(double n = from; n <= to ; n = ((arg.grow == 0) ? n + arg.step : n * arg.grow))
|
||||
{
|
||||
double best = (1.0/0.0);
|
||||
double avg = 0.0;
|
||||
double variance = 0.0;
|
||||
static double measure[MAX_REPEAT];
|
||||
double ba = 0.0;
|
||||
|
||||
if (functions[i].init != NULL)
|
||||
functions[i].init(n);
|
||||
|
||||
// Measure the time of the test_function
|
||||
for(unsigned r = 0; r < arg.repeat; r++) {
|
||||
double t0 = test_function( (arg.graph|arg.best|arg.average|arg.quiet) ? NULL : functions[i].funcname, (size_t) n, functions[i].func);
|
||||
best = (t0 < best) ? t0 : best;
|
||||
avg += t0;
|
||||
variance += t0 * t0;
|
||||
measure[r] = t0;
|
||||
}
|
||||
|
||||
// Closure of the tests
|
||||
if (functions[i].clear != NULL)
|
||||
functions[i].clear();
|
||||
|
||||
// Finish computing the different times
|
||||
if (arg.repeat > 1) {
|
||||
avg /= arg.repeat;
|
||||
variance = (variance - arg.repeat * avg * avg) / (arg.repeat - 1);
|
||||
ba = get_best_within(arg.repeat, measure, 1. + arg.within);
|
||||
}
|
||||
|
||||
// The average value shall be around best_within (and greater)
|
||||
// otherwise the result doesn't seem reliable.
|
||||
bool reliable = (ba <= avg && ba * (1.0+arg.within/2) + 0.5 > avg);
|
||||
|
||||
// Print the result
|
||||
if (arg.graph) {
|
||||
fprintf(graph_file, "%f %f\n", n, arg.average ? avg : arg.best ? best : ba);
|
||||
} else if (arg.quiet) {
|
||||
printf("%ld\n", (long)(arg.average ? avg : arg.best ? best : reliable ? ba : -1));
|
||||
} else if (arg.repeat > 1) {
|
||||
if (arg.average == false && arg.best_within == false)
|
||||
printf ("%20.20s time %lu ms for n = %lu *** BEST ***\n", functions[i].funcname, (unsigned long) best, (unsigned long) n);
|
||||
if (arg.best == false && arg.best_within == false)
|
||||
printf ("%20.20s time %lu ms +/- %u ms for n = %lu *** AVERAGE ***\n", functions[i].funcname, (unsigned long) avg, 2*integer_sqrt(variance), (unsigned long) n);
|
||||
if (arg.best == false && arg.average == false)
|
||||
printf ("%20.20s time %lu ms for n = %lu *** BEST within %d%% *** %s\n", functions[i].funcname, (unsigned long) ba, (unsigned long) n, (int)(100*arg.within), reliable ? "" : "(unreliable result)");
|
||||
}
|
||||
}
|
||||
|
||||
// Close the FILE if needed
|
||||
if (arg.graph) {
|
||||
fclose(graph_file);
|
||||
printf("File plot-%s-%d.dat generated.\n"
|
||||
"Run in gnuplot the following command:\n"
|
||||
"\tplot 'plot-%s-%d.dat' with linespoints linestyle 1\n",
|
||||
library, arg.test_function, library, arg.test_function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
+81
-32
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - common functions for bench module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2021, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -25,35 +25,59 @@
|
||||
#ifndef __COMMON_BENCH__H
|
||||
#define __COMMON_BENCH__H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Needed for M_THREAD_ATTR
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
#include "m-mutex.h"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) \
|
||||
|| defined(__DragonFly__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/types.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/time.h>
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
# define USE_SYSCTL
|
||||
#else
|
||||
# include <sys/types.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/time.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
// Number of elements of a table
|
||||
#define numberof(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
/* Get the current CPU time. */
|
||||
#if defined(_WIN32)
|
||||
/* WINDOWS variant */
|
||||
#include <windows.h>
|
||||
|
||||
static unsigned long long
|
||||
static inline unsigned long long
|
||||
cputime (void)
|
||||
{
|
||||
LARGE_INTEGER freq, val;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&val);
|
||||
return (unsigned long long) (1000000. * (double) val.QuadPart / (double) freq.QuadPart);
|
||||
LARGE_INTEGER freq, val;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&val);
|
||||
return (unsigned long long) (1000000. * (double) val.QuadPart / (double) freq.QuadPart);
|
||||
}
|
||||
|
||||
#else
|
||||
/* UNIX variant */
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static unsigned long long
|
||||
static inline unsigned long long
|
||||
cputime (void)
|
||||
{
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
// Multi thread
|
||||
// Multi thread: Uses real time
|
||||
struct timeval tv;
|
||||
gettimeofday (&tv, NULL);
|
||||
return tv.tv_sec * 1000000ULL + tv.tv_usec;
|
||||
#else
|
||||
// Single thread
|
||||
// Single thread: Uses process usage time
|
||||
struct rusage rus;
|
||||
getrusage (0, &rus);
|
||||
return rus.ru_utime.tv_sec * 1000000ULL + rus.ru_utime.tv_usec;
|
||||
@@ -62,41 +86,61 @@ cputime (void)
|
||||
|
||||
#endif
|
||||
|
||||
unsigned long g_result;
|
||||
unsigned int randValue = 0;
|
||||
// The result of the bench
|
||||
extern unsigned long g_result;
|
||||
|
||||
static void rand_init(void)
|
||||
// The rand value
|
||||
extern
|
||||
#if defined(MULTI_THREAD_MEASURE)
|
||||
M_THREAD_ATTR
|
||||
#endif
|
||||
unsigned int randValue;
|
||||
|
||||
// Pseudo rand value generator.
|
||||
// Not very random but sufficient and fast enough for the bench
|
||||
static inline void rand_init(void)
|
||||
{
|
||||
randValue = 0;
|
||||
}
|
||||
static unsigned int rand_get(void)
|
||||
static inline unsigned int rand_get(void)
|
||||
{
|
||||
randValue = randValue * 31421U + 6927U;
|
||||
return randValue;
|
||||
}
|
||||
|
||||
static void test_function(const char str[], size_t n, void (*func)(size_t))
|
||||
// The function that performs the bench
|
||||
static inline double
|
||||
test_function(const char *str, size_t n, void (*func)(size_t))
|
||||
{
|
||||
double result;
|
||||
unsigned long long start, end;
|
||||
rand_init();
|
||||
start = cputime();
|
||||
(*func)(n);
|
||||
end = cputime();
|
||||
result = (double)(end-start) / 1000.0;
|
||||
end = (end - start) / 1000U;
|
||||
printf ("%s %lu ms for n = %lu [r=%lu]\n", str, (unsigned long) end, (unsigned long) n, g_result);
|
||||
if (str != NULL) {
|
||||
printf ("%20.20s time %lu ms for n = %lu [r=%lu]\n", str, (unsigned long) end, (unsigned long) n, g_result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <windows.h>
|
||||
#elif (defined(__APPLE__) && defined(__MACH__)) \
|
||||
|| defined(__DragonFly__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/param.h>
|
||||
# include <sys/sysctl.h>
|
||||
# define USE_SYSCTL
|
||||
#else
|
||||
# include <unistd.h>
|
||||
// Configuration table
|
||||
typedef struct {
|
||||
int num;
|
||||
const char *funcname;
|
||||
size_t default_n;
|
||||
void (*init)(size_t);
|
||||
void (*func)(size_t);
|
||||
void (*clear)(void);
|
||||
} config_func_t;
|
||||
|
||||
// Perform a parsing of the given arguments and do the bench according to the table
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
void test(const char library[], size_t n, const config_func_t functions[], int argc, const char *argv[]);
|
||||
|
||||
/* Return the number of CPU of the system */
|
||||
static inline int get_cpu_count(void)
|
||||
@@ -120,4 +164,9 @@ static inline int get_cpu_count(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
void compiler_barrier(void *p);
|
||||
|
||||
#endif
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MLIB - TEST module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2021, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -59,7 +59,7 @@ static void find_max(void)
|
||||
dict_int64_set_at (dict, get_rand(), get_rand());
|
||||
n++;
|
||||
if ((n & ((1<<20)-1)) == 0) {
|
||||
printf ("n=%lu\n", n);
|
||||
printf ("n=%zu\n", n);
|
||||
}
|
||||
}
|
||||
dict_int64_clear(dict);
|
||||
|
||||
+178
-205
@@ -3,50 +3,50 @@
|
||||
//
|
||||
// Original version Contributed by Jeremy Zerfas
|
||||
// M*LIB port contributed by Patrick Pélissier
|
||||
// gcc -O2 -march=native nucleotide.c -I../.. -std=c11 -lpthread -DNDEBUG
|
||||
|
||||
#define NDEBUG
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "m-worker.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-bptree.h"
|
||||
|
||||
// This controls the maximum length for each set of oligonucleotide frequencies
|
||||
// and each oligonucleotide count output by this program.
|
||||
#define MAXIMUM_OUTPUT_LENGTH 4096
|
||||
|
||||
// Define Open Addressing dictionnary. Need to define how to handle out of bound data.
|
||||
static inline bool oor_equal_p(uint64_t k, unsigned char n)
|
||||
// Number of outputs to display
|
||||
#define MAXMUM_NUMBER_OUTPUT 7
|
||||
|
||||
/* Define the needed containers */
|
||||
/* Define Open Addressing dictionary of uint64_t --> uint32_t. Need to define how to handle out of bound data. */
|
||||
static inline bool my_oor_equal_p(uint64_t k, unsigned char n)
|
||||
{
|
||||
return k == ~(uint64_t) n;
|
||||
}
|
||||
static inline void oor_set(uint64_t *k, unsigned char n)
|
||||
static inline void my_oor_set(uint64_t *k, unsigned char n)
|
||||
{
|
||||
*k = ~(uint64_t)n;
|
||||
}
|
||||
// Custom hash function.
|
||||
static inline size_t my_hash(uint64_t key)
|
||||
{
|
||||
return ((key) ^ (key)>>7);
|
||||
}
|
||||
// Define a dictionnary of uint64_t --> uint32_t
|
||||
DICT_OA_DEF2(dict_oligonucleotide,
|
||||
uint64_t, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
|
||||
uint64_t, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(my_oor_equal_p), OOR_SET(my_oor_set M_IPTR)),
|
||||
uint32_t, M_DEFAULT_OPLIST)
|
||||
// Define oplist of dictionary
|
||||
#define OLIGONUCLEOTIDE_OPLIST DICT_OPLIST(dict_oligonucleotide, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(oor_hash), OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)), M_DEFAULT_OPLIST)
|
||||
#define M_OPL_dict_oligonucleotide_t() DICT_OPLIST(dict_oligonucleotide, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(my_hash), OOR_EQUAL(my_oor_equal_p), OOR_SET(my_oor_set M_IPTR)), M_DEFAULT_OPLIST)
|
||||
|
||||
typedef struct {
|
||||
uint64_t key;
|
||||
uint32_t value;
|
||||
} element;
|
||||
// Define a B*TREE-21 of uint32_t --> uint64_t and register its oplist
|
||||
#define TREE_CMP(a,b) ((a) > (b) ? -1 : (a) < (b)) /* reverse natural order */
|
||||
BPTREE_DEF2(tree_dict_oligonucleotide, 21, uint32_t, M_OPEXTEND(M_DEFAULT_OPLIST, CMP(TREE_CMP)), uint64_t, M_DEFAULT_OPLIST)
|
||||
#define M_OPL_tree_dict_oligonucleotide_t() BPTREE_OPLIST2(tree_dict_oligonucleotide, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
/* Define the data to analyse (a plain array) */
|
||||
ARRAY_DEF(polynucleotide, unsigned char)
|
||||
|
||||
// Macro to convert a nucleotide character to a code. Note that upper and lower
|
||||
// case ASCII letters only differ in the fifth bit from the right and we only
|
||||
@@ -55,143 +55,167 @@ typedef struct {
|
||||
// characters other than 'A', 'C', 'G', and 'T' aren't used.
|
||||
#define CODE_FOR_NUCLEOTIDE(nucleotide) (" \0 \1\3 \2"[nucleotide & 0x7])
|
||||
|
||||
|
||||
// And one more macro to convert the codes back to nucleotide characters.
|
||||
#define NUCLEOTIDE_FOR_CODE(code) ("ACGT"[code & 0x3])
|
||||
|
||||
static worker_t worker;
|
||||
|
||||
// Function to use when sorting elements with qsort() later. Elements with
|
||||
// larger values will come first and in cases of identical values then elements
|
||||
// with smaller keys will come first.
|
||||
static int element_Compare(const void *a, const void *b)
|
||||
// Read from the given stream the data and fill in the array with the converted one.
|
||||
static void polynucleotide_init_stream(polynucleotide_t p, FILE *stream)
|
||||
{
|
||||
const element * const left_Element = a;
|
||||
const element * const right_Element = b;
|
||||
// Sort based on element values.
|
||||
if(left_Element->value < right_Element->value) return 1;
|
||||
if(left_Element->value > right_Element->value) return -1;
|
||||
|
||||
// If we got here then both items have the same value so then sort based on
|
||||
// key.
|
||||
return left_Element->key > right_Element->key ? 1 : -1;
|
||||
char buffer[MAXIMUM_OUTPUT_LENGTH], c;
|
||||
|
||||
polynucleotide_init(p);
|
||||
polynucleotide_reserve(p, MAXIMUM_OUTPUT_LENGTH);
|
||||
// Find the start of the third polynucleotide.
|
||||
while(fgets(buffer, sizeof(buffer), stream) && memcmp(">THREE", buffer, 6));
|
||||
// Start reading and encoding the third polynucleotide.
|
||||
while(fgets(buffer, sizeof(buffer), stream) && buffer[0]!='>'){
|
||||
for(size_t i = 0; (c = buffer[i]) !='\0'; i++) {
|
||||
if (c != '\n') {
|
||||
polynucleotide_push_back(p, CODE_FOR_NUCLEOTIDE(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generate frequencies for all oligonucleotides in polynucleotide that are of
|
||||
// desired_Length_For_Oligonucleotides and then save it to output.
|
||||
static void
|
||||
generate_Frequencies_For_Desired_Length_Oligonucleotides(const char polynucleotide[],
|
||||
const int polynucleotide_Length,
|
||||
const int desired_Length_For_Oligonucleotides,
|
||||
char * const output)
|
||||
{
|
||||
assert (2*desired_Length_For_Oligonucleotides <= 64);
|
||||
|
||||
dict_oligonucleotide_t hash_Table;
|
||||
dict_oligonucleotide_init(hash_Table);
|
||||
|
||||
uint64_t key=0;
|
||||
const uint64_t mask=(1ULL<<(2*desired_Length_For_Oligonucleotides))-1;
|
||||
// This is the function which does 95% of the work.
|
||||
/* NOTE: paralelisation is not done by cutting the input set in two.
|
||||
This is not efficient as the size of the output table will be the same
|
||||
as for the whole table. */
|
||||
static void init_hash(dict_oligonucleotide_t hash_Table, const size_t size, const unsigned char tab[size], const size_t desiredLength, const size_t offset, const size_t skip)
|
||||
{
|
||||
uint64_t key = 0;
|
||||
const uint64_t mask = (1ULL<<(2*desiredLength))-1;
|
||||
assert (2*desiredLength < 64 && desiredLength < size);
|
||||
assert ((1 == skip || 4 == skip) && offset < size);
|
||||
|
||||
// For the first several nucleotides we only need to append them to key in
|
||||
// preparation for the insertion of complete oligonucleotides to hash_Table.
|
||||
for(int i=0; i<desired_Length_For_Oligonucleotides-1; i++)
|
||||
key=(key<<2 & mask) | polynucleotide[i];
|
||||
// preparation for the insertion of complete oligonucleotides to hash.
|
||||
for(size_t i = 0; i < desiredLength - 1 + offset; i++)
|
||||
key = (key<<2) | tab[i];
|
||||
key &= mask;
|
||||
|
||||
dict_oligonucleotide_t hash;
|
||||
dict_oligonucleotide_init(hash);
|
||||
|
||||
// Add all the complete oligonucleotides of
|
||||
// desired_Length_For_Oligonucleotides to hash_Table and update the count
|
||||
// for each oligonucleotide.
|
||||
for(int i=desired_Length_For_Oligonucleotides-1
|
||||
; i<polynucleotide_Length; i++){
|
||||
|
||||
key=(key<<2 & mask) | polynucleotide[i];
|
||||
|
||||
uint32_t *p = dict_oligonucleotide_get(hash_Table, key);
|
||||
if (p != NULL)
|
||||
(*p) ++;
|
||||
else {
|
||||
dict_oligonucleotide_set_at(hash_Table, key, 1);
|
||||
// desiredLength to hash, updating their count
|
||||
if (skip == 1) {
|
||||
for(size_t i = desiredLength-1+offset ; i < size; i++) {
|
||||
key = (key<<2) | tab[i+0];
|
||||
key &= mask;
|
||||
uint32_t *p = dict_oligonucleotide_get(hash, key);
|
||||
if (p != NULL) {
|
||||
(*p) ++;
|
||||
} else {
|
||||
dict_oligonucleotide_set_at(hash, key, 1);
|
||||
}
|
||||
}
|
||||
} else { // skip = 4
|
||||
for(size_t i = desiredLength-1+offset ; i < size; i+=4) {
|
||||
for (size_t j = 0; j < 4; j++)
|
||||
key = (key<<2) | tab[i+j];
|
||||
key &= mask;
|
||||
uint32_t *p = dict_oligonucleotide_get(hash, key);
|
||||
if (p != NULL) {
|
||||
(*p) ++;
|
||||
} else {
|
||||
dict_oligonucleotide_set_at(hash, key, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array of elements from hash_Table.
|
||||
int elements_Array_Size=dict_oligonucleotide_size(hash_Table), i=0;
|
||||
element * elements_Array=malloc(elements_Array_Size*sizeof(element));
|
||||
if (!elements_Array) abort();
|
||||
for M_EACH(item, hash_Table, OLIGONUCLEOTIDE_OPLIST) {
|
||||
elements_Array[i++]=((element){item->key, item->value});
|
||||
}
|
||||
// Clear the hash table
|
||||
dict_oligonucleotide_clear(hash_Table);
|
||||
|
||||
// Sort elements_Array.
|
||||
qsort(elements_Array, elements_Array_Size, sizeof(element), element_Compare);
|
||||
|
||||
// Print the frequencies for each oligonucleotide.
|
||||
for(int output_Position=0, i=0; i<elements_Array_Size; i++) {
|
||||
|
||||
// Convert the key for the oligonucleotide to a string.
|
||||
char oligonucleotide[desired_Length_For_Oligonucleotides+1];
|
||||
for(int j=desired_Length_For_Oligonucleotides-1; j>-1; j--){
|
||||
oligonucleotide[j]=NUCLEOTIDE_FOR_CODE(elements_Array[i].key);
|
||||
elements_Array[i].key>>=2;
|
||||
}
|
||||
oligonucleotide[desired_Length_For_Oligonucleotides]='\0';
|
||||
|
||||
// Output the frequency for oligonucleotide to output.
|
||||
output_Position+=snprintf(output+output_Position
|
||||
, MAXIMUM_OUTPUT_LENGTH-output_Position, "%s %.3f\n", oligonucleotide
|
||||
, 100.0f*elements_Array[i].value
|
||||
/(polynucleotide_Length-desired_Length_For_Oligonucleotides+1));
|
||||
}
|
||||
|
||||
free(elements_Array);
|
||||
dict_oligonucleotide_init_move(hash_Table, hash);
|
||||
}
|
||||
|
||||
static void compute_hash(dict_oligonucleotide_t hash_Table, const polynucleotide_t p, const size_t desiredLength)
|
||||
{
|
||||
size_t size = polynucleotide_size(p);
|
||||
unsigned char *tab = polynucleotide_get(p, 0);
|
||||
if (desiredLength < 8) {
|
||||
init_hash(hash_Table, size, tab, desiredLength, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
worker_sync_t block; // Define a synchronization block
|
||||
worker_start(block, worker);
|
||||
dict_oligonucleotide_t hash_Table3, *p_hash_Table3 = &hash_Table3;
|
||||
dict_oligonucleotide_t hash_Table2, *p_hash_Table2 = &hash_Table2;
|
||||
dict_oligonucleotide_t hash_Table1, *p_hash_Table1 = &hash_Table1;
|
||||
WORKER_SPAWN(block, (p_hash_Table1, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table1, size, tab, desiredLength, 3, 4);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (p_hash_Table2, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table2, size, tab, desiredLength, 2, 4);
|
||||
}, (/*no output*/));
|
||||
WORKER_SPAWN(block, (p_hash_Table3, size, tab, desiredLength), {
|
||||
init_hash(*p_hash_Table3, size, tab, desiredLength, 1, 4);
|
||||
}, (/*no output*/));
|
||||
init_hash(hash_Table, size, tab, desiredLength, 0, 4);
|
||||
worker_sync(block);
|
||||
|
||||
// Merge all hashtables
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table1);
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table2);
|
||||
dict_oligonucleotide_splice(hash_Table, hash_Table3);
|
||||
// Clear extra hashtables
|
||||
dict_oligonucleotide_clear(hash_Table3);
|
||||
dict_oligonucleotide_clear(hash_Table2);
|
||||
dict_oligonucleotide_clear(hash_Table1);
|
||||
}
|
||||
|
||||
// Generate frequencies for all oligonucleotides in polynucleotide that are of
|
||||
// desiredLength and then save it to output.
|
||||
static void compute_freq(const polynucleotide_t p, const size_t desiredLength, char output[])
|
||||
{
|
||||
dict_oligonucleotide_t hash_Table;
|
||||
compute_hash(hash_Table, p, desiredLength);
|
||||
|
||||
// Order key is the count frequency (inverse value & key)
|
||||
tree_dict_oligonucleotide_t tree;
|
||||
tree_dict_oligonucleotide_init(tree);
|
||||
for M_EACH(item, hash_Table, dict_oligonucleotide_t) {
|
||||
tree_dict_oligonucleotide_set_at (tree, item->value, item->key);
|
||||
}
|
||||
|
||||
// Create Output
|
||||
char oligonucleotide[64];
|
||||
const double invSum = 100.0 / (double) (polynucleotide_size(p) - desiredLength + 1);
|
||||
int output_Position = 0;
|
||||
for M_EACH(item, tree, tree_dict_oligonucleotide_t) {
|
||||
// Convert the key for the oligonucleotide to a string.
|
||||
uint64_t freq = *item->value_ptr;
|
||||
uint32_t count = *item->key_ptr;
|
||||
for(size_t j = desiredLength-1; j < desiredLength; j--){
|
||||
oligonucleotide[j]=NUCLEOTIDE_FOR_CODE(freq);
|
||||
freq>>=2;
|
||||
}
|
||||
oligonucleotide[desiredLength]='\0';
|
||||
// Output the frequency for oligonucleotide to output.
|
||||
output_Position+=snprintf(output+output_Position, MAXIMUM_OUTPUT_LENGTH-output_Position,
|
||||
"%s %.3f\n", oligonucleotide, invSum * count);
|
||||
}
|
||||
|
||||
dict_oligonucleotide_clear(hash_Table);
|
||||
tree_dict_oligonucleotide_clear(tree);
|
||||
}
|
||||
|
||||
// Generate a count for the number of times oligonucleotide appears in
|
||||
// polynucleotide and then save it to output.
|
||||
static void
|
||||
generate_Count_For_Oligonucleotide(const char * const polynucleotide,
|
||||
const int polynucleotide_Length,
|
||||
const char * const oligonucleotide,
|
||||
char * const output)
|
||||
static void compute_count(polynucleotide_t p, const char oligonucleotide[], char output[])
|
||||
{
|
||||
const int oligonucleotide_Length=strlen(oligonucleotide);
|
||||
assert (2*oligonucleotide_Length <= 64);
|
||||
|
||||
const size_t oligonucleotide_Length = strlen(oligonucleotide);
|
||||
dict_oligonucleotide_t hash_Table;
|
||||
dict_oligonucleotide_init(hash_Table);
|
||||
|
||||
uint64_t key = 0;
|
||||
const uint64_t mask=(1ULL<<(2*oligonucleotide_Length))-1;
|
||||
|
||||
// For the first several nucleotides we only need to append them to key in
|
||||
// preparation for the insertion of complete oligonucleotides to hash_Table.
|
||||
for(int i=0; i<oligonucleotide_Length-1; i++)
|
||||
key=(key<<2 & mask) | polynucleotide[i];
|
||||
|
||||
// Add all the complete oligonucleotides of oligonucleotide_Length to
|
||||
// hash_Table and update the count for each oligonucleotide.
|
||||
for(int i=oligonucleotide_Length-1; i<polynucleotide_Length; i++){
|
||||
key=(key<<2 & mask) | polynucleotide[i];
|
||||
|
||||
uint32_t *p = dict_oligonucleotide_get(hash_Table, key);
|
||||
if (p != NULL)
|
||||
(*p) ++;
|
||||
else {
|
||||
dict_oligonucleotide_set_at(hash_Table, key, 1);
|
||||
}
|
||||
}
|
||||
compute_hash(hash_Table, p, oligonucleotide_Length);
|
||||
|
||||
// Generate the key for oligonucleotide.
|
||||
key=0;
|
||||
for(int i=0; i<oligonucleotide_Length; i++)
|
||||
key=(key<<2) | CODE_FOR_NUCLEOTIDE(oligonucleotide[i]);
|
||||
uint64_t key=0;
|
||||
for(size_t i=0; i<oligonucleotide_Length; i++)
|
||||
key = (key<<2) | CODE_FOR_NUCLEOTIDE(oligonucleotide[i]);
|
||||
|
||||
// Output the count for oligonucleotide to output.
|
||||
uint32_t *p = dict_oligonucleotide_get(hash_Table, key);
|
||||
uintmax_t count= p== NULL ? 0 : *p;
|
||||
uint32_t *val = dict_oligonucleotide_get(hash_Table, key);
|
||||
uintmax_t count= val== NULL ? 0 : *val;
|
||||
snprintf(output, MAXIMUM_OUTPUT_LENGTH, "%ju\t%s", count, oligonucleotide);
|
||||
|
||||
dict_oligonucleotide_clear(hash_Table);
|
||||
@@ -199,94 +223,43 @@ generate_Count_For_Oligonucleotide(const char * const polynucleotide,
|
||||
|
||||
int main()
|
||||
{
|
||||
char buffer[MAXIMUM_OUTPUT_LENGTH];
|
||||
worker_t worker;
|
||||
|
||||
// Initialize the different worker threads by auto-detecting the number of cores of the system
|
||||
worker_init(worker, 0, 1, NULL);
|
||||
worker_init(worker, -1, 0, NULL);
|
||||
|
||||
// Read the input
|
||||
polynucleotide_t p, *ptr = &p;
|
||||
polynucleotide_init_stream(p, stdin);
|
||||
|
||||
// Find the start of the third polynucleotide.
|
||||
while(fgets(buffer, sizeof(buffer), stdin) && memcmp(">THREE", buffer
|
||||
, sizeof(">THREE")-1));
|
||||
|
||||
// Start with 1 MB of storage for reading in the polynucleotide and grow
|
||||
// geometrically.
|
||||
int polynucleotide_Capacity=1048576;
|
||||
int polynucleotide_Length=0;
|
||||
char * polynucleotide=malloc(polynucleotide_Capacity);
|
||||
if (!polynucleotide) abort();
|
||||
|
||||
// Start reading and encoding the third polynucleotide.
|
||||
while(fgets(buffer, sizeof(buffer), stdin) && buffer[0]!='>'){
|
||||
for(int i=0; buffer[i]!='\0'; i++)
|
||||
if(buffer[i]!='\n')
|
||||
polynucleotide[polynucleotide_Length++]
|
||||
=CODE_FOR_NUCLEOTIDE(buffer[i]);
|
||||
|
||||
// Make sure we still have enough memory allocated for any potential
|
||||
// nucleotides in the next line.
|
||||
if(polynucleotide_Capacity-polynucleotide_Length<sizeof(buffer)) {
|
||||
polynucleotide=realloc(polynucleotide, polynucleotide_Capacity*=2);
|
||||
if (!polynucleotide) abort();
|
||||
}
|
||||
}
|
||||
|
||||
char output_Buffer[7][MAXIMUM_OUTPUT_LENGTH];
|
||||
char (*output)[MAXIMUM_OUTPUT_LENGTH] = output_Buffer;
|
||||
// Prepare outputs
|
||||
char outputBuffer[MAXMUM_NUMBER_OUTPUT][MAXIMUM_OUTPUT_LENGTH];
|
||||
char (*output)[MAXIMUM_OUTPUT_LENGTH] = outputBuffer;
|
||||
|
||||
// Do the following functions in parallel.
|
||||
// Define a synchronization block
|
||||
worker_sync_t block;
|
||||
worker_start(block);
|
||||
worker_sync_t block; // Define a synchronization block
|
||||
worker_start(block, worker);
|
||||
|
||||
WORKER_SPAWN(worker, block, (polynucleotide, polynucleotide_Length, output), {
|
||||
generate_Count_For_Oligonucleotide(polynucleotide,
|
||||
polynucleotide_Length,
|
||||
"GGTATTTTAATTTATAGT", output[6]);
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATTTTAATTTATAGT", output[6]);
|
||||
}, (/*no output*/));
|
||||
|
||||
WORKER_SPAWN(worker, block, (polynucleotide, polynucleotide_Length, output), {
|
||||
generate_Count_For_Oligonucleotide(polynucleotide,
|
||||
polynucleotide_Length,
|
||||
"GGTATTTTAATT", output[5]);
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATTTTAATT", output[5]);
|
||||
}, (/*no output*/));
|
||||
|
||||
WORKER_SPAWN(worker, block, (polynucleotide, polynucleotide_Length, output), {
|
||||
generate_Count_For_Oligonucleotide(polynucleotide,
|
||||
polynucleotide_Length,
|
||||
"GGTATT", output[4]);
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTATT", output[4]);
|
||||
}, (/*no output*/));
|
||||
|
||||
WORKER_SPAWN(worker, block, (polynucleotide, polynucleotide_Length, output), {
|
||||
generate_Count_For_Oligonucleotide(polynucleotide,
|
||||
polynucleotide_Length,
|
||||
"GGTA", output[3]);
|
||||
WORKER_SPAWN(block, (ptr, output), {
|
||||
compute_count(*ptr, "GGTA", output[3]);
|
||||
}, (/*no output*/));
|
||||
|
||||
WORKER_SPAWN(worker, block, (polynucleotide, polynucleotide_Length, output), {
|
||||
generate_Count_For_Oligonucleotide(polynucleotide,
|
||||
polynucleotide_Length,
|
||||
"GGT", output[2]);
|
||||
}, (/*no output*/));
|
||||
|
||||
WORKER_SPAWN(worker, block, (polynucleotide, polynucleotide_Length, output), {
|
||||
generate_Frequencies_For_Desired_Length_Oligonucleotides(polynucleotide,
|
||||
polynucleotide_Length,
|
||||
2,
|
||||
output[1]);
|
||||
}, (/*no output*/));
|
||||
|
||||
generate_Frequencies_For_Desired_Length_Oligonucleotides(polynucleotide,
|
||||
polynucleotide_Length,
|
||||
1,
|
||||
output[0]);
|
||||
compute_count(*ptr, "GGT", output[2]);
|
||||
compute_freq(*ptr, 2, output[1]);
|
||||
compute_freq(p, 1, output[0]);
|
||||
worker_sync(block);
|
||||
|
||||
// Output the results to stdout.
|
||||
for(int i=0; i<7; i++)
|
||||
printf("%s\n", output_Buffer[i]);
|
||||
for(int i=0; i<MAXMUM_NUMBER_OUTPUT; i++)
|
||||
printf("%s\n", outputBuffer[i]);
|
||||
|
||||
free(polynucleotide);
|
||||
polynucleotide_clear(p);
|
||||
worker_clear(worker);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
CC= gcc
|
||||
CXX= g++
|
||||
CFLAGS= -g -Wall -O2 -fomit-frame-pointer -march=native -lm -include stddef.h
|
||||
CXXFLAGS= $(CFLAGS)
|
||||
LDFLAGS=
|
||||
OBJS=
|
||||
LIBS= -lm
|
||||
|
||||
all: run
|
||||
|
||||
benchmarks:
|
||||
git clone https://github.com/attractivechaos/benchmarks.git
|
||||
|
||||
build: benchmarks
|
||||
mkdir -p benchmarks/udb/mlib
|
||||
cp test-mlib.c benchmarks/udb/mlib/test.c
|
||||
cp Makefile-mlib benchmarks/udb/mlib/Makefile
|
||||
mkdir -p benchmarks/udb/mlib-oa
|
||||
cp test-mlib-oa.c benchmarks/udb/mlib-oa/test.c
|
||||
cp Makefile-mlib benchmarks/udb/mlib-oa/Makefile
|
||||
touch build
|
||||
|
||||
all-recur: build
|
||||
cd benchmarks/udb; \
|
||||
wdir=`pwd`; \
|
||||
list_c=`find . -name 'test.c'`; \
|
||||
list_cc=`find . -name 'test.cc'`; \
|
||||
for t in $$list_c $$list_cc ; do \
|
||||
subdir=`dirname $$t`; \
|
||||
cd $$subdir; \
|
||||
$(MAKE) CC="$(CC)" CXX="$(CXX)" DFLAGS="$(DFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" || exit 1; \
|
||||
cd $$wdir; \
|
||||
done;
|
||||
|
||||
run: all-recur
|
||||
@cd benchmarks/udb; \
|
||||
echo ; echo "LIBRARY | INT TIME | STR TIME" ; \
|
||||
for i in `find . -name 'test'` ; do \
|
||||
printf "%s|" `dirname $$i|sed -e 's+./++g'`; \
|
||||
$$i -is | awk ' \
|
||||
/\[int data\] # element/ { nbInt= $$5 } \
|
||||
/\[int data\] CPU time/ {timeInt= $$5 } \
|
||||
/\[str data\] # element/ { nbStr= $$5 } \
|
||||
/\[str data\] CPU time/ {timeStr= $$5 } \
|
||||
END { \
|
||||
if (nbInt != 624444) timeInt="BRK" ; \
|
||||
if (nbStr != 624444) timeStr="BRK" ; \
|
||||
print timeInt,"|" ,timeStr \
|
||||
}' ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -rf benchmarks build
|
||||
@@ -0,0 +1,22 @@
|
||||
CC= gcc
|
||||
CFLAGS= -g -Wall -O2 -march=native
|
||||
OBJS= ../benchmark/benchmark.o ../runit/runlib.o test.o
|
||||
DFLAGS=
|
||||
INCLUDES= -I../benchmark -I../runit -I../../../../..
|
||||
LIBS=
|
||||
|
||||
.SUFFIXES:.c .o
|
||||
|
||||
.c.o:
|
||||
$(CC) -std=gnu11 -DNDEBUG -c $(INCLUDES) $(CFLAGS) $(DFLAGS) $< -o $@
|
||||
|
||||
all:test
|
||||
|
||||
test:$(OBJS)
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
cleanlocal:
|
||||
rm -f *.o a.out *~ test
|
||||
|
||||
clean:cleanlocal
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-mempool.h"
|
||||
|
||||
static inline bool oor_equal_p(int k, unsigned char n)
|
||||
{
|
||||
return k == (int)-n - 1;
|
||||
}
|
||||
|
||||
static inline void oor_set(int *k, unsigned char n)
|
||||
{
|
||||
*k = (int)-n - 1;
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_int,
|
||||
int, M_OPEXTEND(M_DEFAULT_OPLIST, OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
static const char *const oor[2] = { "EMPTY", "DELETED" };
|
||||
|
||||
static inline bool str_oor_equal_p(const char *k, unsigned char n)
|
||||
{
|
||||
return k == oor[n];
|
||||
}
|
||||
|
||||
static inline void str_oor_set(const char * *k, unsigned char n)
|
||||
{
|
||||
*k = oor[n];
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_str, const char *,
|
||||
M_OPEXTEND (M_CSTR_OPLIST, OOR_EQUAL(str_oor_equal_p), OOR_SET(str_oor_set M_IPTR)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
|
||||
int test_int(int N, const unsigned *data)
|
||||
{
|
||||
int ret;
|
||||
M_LET(h, DICT_OPLIST(dict_int)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_int_get (h, data[i]) == NULL) {
|
||||
dict_int_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_int_erase(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_int_size(h);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test_str(int N, char * const *data)
|
||||
{
|
||||
int ret;
|
||||
M_LET(h, DICT_OPLIST(dict_str)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_str_get (h, data[i]) == NULL) {
|
||||
dict_str_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_str_erase(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_str_size(h);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return udb_benchmark(argc, argv, test_int, test_str);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-mempool.h"
|
||||
|
||||
DICT_STOREHASH_DEF2(dict_int,
|
||||
int, M_OPEXTEND(M_DEFAULT_OPLIST, MEMPOOL(mpool_int), MEMPOOL_LINKAGE(static)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
DICT_STOREHASH_DEF2(dict_str,
|
||||
const char *, M_OPEXTEND (M_CSTR_OPLIST, MEMPOOL(mpool_str), MEMPOOL_LINKAGE(static)),
|
||||
int, M_DEFAULT_OPLIST)
|
||||
|
||||
int test_int(int N, const unsigned *data)
|
||||
{
|
||||
int ret;
|
||||
dict_int_list_pair_mempool_init(mpool_int);
|
||||
M_LET(h, DICT_OPLIST(dict_int)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_int_get (h, data[i]) == NULL) {
|
||||
dict_int_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_int_erase(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_int_size(h);
|
||||
}
|
||||
dict_int_list_pair_mempool_clear(mpool_int);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int test_str(int N, char * const *data)
|
||||
{
|
||||
int ret;
|
||||
dict_str_list_pair_mempool_init(mpool_str);
|
||||
M_LET(h, DICT_OPLIST(dict_str)) {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (dict_str_get (h, data[i]) == NULL) {
|
||||
dict_str_set_at (h, data[i], i);
|
||||
} else {
|
||||
dict_str_erase(h, data[i]);
|
||||
}
|
||||
}
|
||||
ret = (int)dict_str_size(h);
|
||||
}
|
||||
dict_str_list_pair_mempool_clear(mpool_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return udb_benchmark(argc, argv, test_int, test_str);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
CC= gcc
|
||||
CXX= g++
|
||||
CFLAGS= -g -Wall -O2 -fomit-frame-pointer -march=native -lm -include stddef.h
|
||||
CXXFLAGS= $(CFLAGS)
|
||||
LDFLAGS=
|
||||
OBJS=
|
||||
LIBS=
|
||||
|
||||
all: run
|
||||
|
||||
udb2:
|
||||
git clone https://github.com/attractivechaos/udb2.git
|
||||
|
||||
build: udb2
|
||||
mkdir -p udb2/mlib
|
||||
cp test-mlib.c udb2/mlib/test.c
|
||||
cp Makefile-mlib udb2/mlib/Makefile
|
||||
touch build
|
||||
|
||||
all-recur: build
|
||||
cd udb2/stb && make CC="gcc -lm"
|
||||
cd udb2 && for dir in `ls | egrep -v '^(Makefile|common.c|_)'`; do cd $$dir && make && cd -; done
|
||||
|
||||
run: all-recur
|
||||
@cd udb2; \
|
||||
echo ; echo RUNNING BENCH ; echo ; \
|
||||
for i in `find . -name 'run-test'` ; do \
|
||||
echo ; echo `dirname $$i|sed -e 's+./++g'`; \
|
||||
$$i ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -rf udb2 build *~
|
||||
@@ -0,0 +1,8 @@
|
||||
all:run-test
|
||||
|
||||
run-test:test.c ../common.c
|
||||
$(CC) -std=gnu99 -O2 -Wall -I../../../.. $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f run-test* *~
|
||||
rm -rf mlib
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "../common.c"
|
||||
#define M_USE_DICT_OA_PROBING(s) 1
|
||||
#include "m-dict.h"
|
||||
|
||||
static inline bool oor_equal_p(unsigned int k, unsigned char n)
|
||||
{
|
||||
return k == (unsigned int)n;
|
||||
}
|
||||
static inline void oor_set(unsigned int *k, unsigned char n)
|
||||
{
|
||||
*k = (unsigned int)n;
|
||||
}
|
||||
|
||||
DICT_OA_DEF2(dict_oa_uint,
|
||||
unsigned int, M_OPEXTEND(M_DEFAULT_OPLIST, HASH(hash_fn), OOR_EQUAL(oor_equal_p), OOR_SET(oor_set M_IPTR)),
|
||||
unsigned int, M_DEFAULT_OPLIST)
|
||||
|
||||
uint32_t test_int(uint32_t n, uint32_t x0)
|
||||
{
|
||||
uint32_t i, x, z = 0;
|
||||
dict_oa_uint_t h;
|
||||
dict_oa_uint_init(h);
|
||||
for (i = 0, x = x0; i < n; ++i) {
|
||||
x = hash32(x);
|
||||
unsigned int key = get_key(n, x);
|
||||
unsigned int *ptr = dict_oa_uint_get_at(h, key);
|
||||
(*ptr)++;
|
||||
z += *ptr;
|
||||
}
|
||||
fprintf(stderr, "# unique keys: %d; checksum: %u\n", (int) dict_oa_uint_size(h), z);
|
||||
z = dict_oa_uint_size(h);
|
||||
dict_oa_uint_clear(h);
|
||||
return z;
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 150 KiB |
+75
@@ -0,0 +1,75 @@
|
||||
API incompatibility
|
||||
-------------------
|
||||
|
||||
V0.1.*: * First formal version of API
|
||||
|
||||
V0.2.*: * worker_start takes a 'worker_t' as an additional parameter
|
||||
(This enables to perform a wait-on-event instead of a busy waiting).
|
||||
* worker_spawn doesn't take into a 'worker_t' anymore.
|
||||
* worker_init takes an int as numWorker instead of an unsigned int.
|
||||
* RBTREE pop method has been renamed into _pop_at (as it takes a key parameter).
|
||||
* SNAPSHOT_DEF has been removed. use SNAPSHOT_SPSC_DEF
|
||||
* array's _set_at2 has been replaced by array's _get_at.
|
||||
The interface is similar to dict _get_at, making the API cleaner and more generic.
|
||||
_set_at2(a,i,x) ==> set(*_get_at(a,i),x)
|
||||
* DICT_SET:
|
||||
- Rename method _set_at as _push
|
||||
Since there is no key, it is a push method, not a _set_at method.
|
||||
- Rename DICT_SET_DEF2 as DICT_SET_DEF
|
||||
since it only requires one argument.
|
||||
- The method _ref returns a pointer to the data,
|
||||
instead of a pointer to a single tuple like other DICT_SET interface.
|
||||
- The format of the output to character string as changed.
|
||||
* ALGO:
|
||||
- Rename _contains as _contain_p
|
||||
since it is a predicate '_p' and it shall be in infinitive form.
|
||||
- Rename _map as _for_each (Standard name).
|
||||
* M_PRINT_ARG & M_FPRINT_ARG don't put a final semicolon at the end
|
||||
of their expansion anymore (cleaner and no longer needed with the
|
||||
new available tools).
|
||||
* For the TUPLE and the VARIANT
|
||||
change name of _get_ ## field into _get_at_ ## fields
|
||||
Since there is a name collision with other methods
|
||||
if a field is named 'str'.
|
||||
* Rename method _remove into _erase for dictionary & B+TREE (adding a compatibility layer) for API uniformization.
|
||||
The provided _remove doesn't use an iterator but a 'key', incompatible with the one of array or list.
|
||||
But is like the one provided by the priority queue.
|
||||
|
||||
V0.3.*: * Remove B+TREE _remove method as it has been deprecated (use _erase instead)
|
||||
* Remove DICT _remove method as it has been deprecated (use _erase instead)
|
||||
* Make _it_to_p method of B+TREE / RB-TREE as deprecated (uses _it_until_p instead ).
|
||||
* Deprecated buffer options BUFFER_BLOCKING* and BUFFER_UNBLOCKING*
|
||||
as the _push_blocking and _pop_blocking functions can do the same and are more versatil.
|
||||
* Iteration over the items of a dictionnary/tree returns const iterators for access through
|
||||
OPLIST (impact: algorithm & M_EACH) as the IT_REF operator is no longer exported.
|
||||
The _it_ref method is still defined and can be used, so you can still modify the values
|
||||
but it won't be automatically used anymore (as the keys CANNOT be modified).
|
||||
(Previously it generates incorrect algorithm when used with m-dict).
|
||||
|
||||
V0.4.*: * The order of a list initialized using M_LET is reversed
|
||||
to match what is done for ARRAY (and the C++ forward_list).
|
||||
* Rename M_SKIP, M_KEEP & M_MID into M_SKIP_ARGS, M_KEEP_ARGS & M_MID_ARGS
|
||||
due to conflict with glibc malloc.h (that defines M_KEEP for compatibility).
|
||||
* Method write_string of m_serial expects a new argument (number of characters).
|
||||
* Method _init_set2 of m-i-shared is removed.
|
||||
* Operator _it_insert updates it_insertion iterator to reference the inserted element.
|
||||
* Dereference Type of an iterator of a dict / B+Tree is suffixed by _itref_t instead of _type_t.
|
||||
* Rename TUPLE _init_set2 in _init_emplace & _set2 in _emplace.
|
||||
* Rename Priority Queue method _update_further in _update.
|
||||
|
||||
V0.5.*: * M_INVERT is renamed into M_REVERSE to be more coherent with the REVERSE operator.
|
||||
M_INVERT is marked as obsolete.
|
||||
* Remove _it_to_p method of B+TREE / RB-TREE as it has been deprecated.
|
||||
* Remove ALGO_INIT_VA & M_ALG0_LET_INIT_VAI of m-algo as it has been deprecated (Use of M_LET instead).
|
||||
* Some Internal name of user modifiable macros have been renamed to be properly
|
||||
customizable by the user.
|
||||
* Rename M_HASH_SEED into M_USE_HASH_SEED (as it is a user macro).
|
||||
* M_LET: for initialization of variable with arguments, if there is only one argument,
|
||||
it will default to use INIT_SET instead of INIT_WITH.
|
||||
==> Encapsulate the argument between () to force use of INIT_WITH.
|
||||
* Remove of UPDATE operator. Use of SET for RBTREE or ADD for DICT instead.
|
||||
* Make the _clean suffix for reseting the container to empty deprecated. Use new _reset suffix instead.
|
||||
* Make the _get_at suffix for always getting a value deprecated. Use new _safe_get suffix instead.
|
||||
* Rename CLEAN operator in RESET operator.
|
||||
* Rename GET_SET_KEY operator in SAFE_GET_KEY operator.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><!--This file was converted to xhtml by LibreOffice - see http://cgit.freedesktop.org/libreoffice/core/tree/filter/source/xslt for the code.--><head profile="http://dublincore.org/documents/dcmi-terms/"><meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8"/><title xml:lang="en-US">- no title specified</title><meta name="DCTERMS.title" content="" xml:lang="en-US"/><meta name="DCTERMS.language" content="en-US" scheme="DCTERMS.RFC4646"/><meta name="DCTERMS.source" content="http://xml.openoffice.org/odf2xhtml"/><meta name="DCTERMS.issued" content="2018-07-01T11:06:00.978729880" scheme="DCTERMS.W3CDTF"/><meta name="DCTERMS.provenance" content="" xml:lang="en-US"/><meta name="DCTERMS.subject" content="," xml:lang="en-US"/><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" hreflang="en"/><link rel="schema.DCTERMS" href="http://purl.org/dc/terms/" hreflang="en"/><link rel="schema.DCTYPE" href="http://purl.org/dc/dcmitype/" hreflang="en"/><link rel="schema.DCAM" href="http://purl.org/dc/dcam/" hreflang="en"/><style type="text/css">
|
||||
@page { }
|
||||
table { border-collapse:collapse; border-spacing:0; empty-cells:show }
|
||||
td, th { vertical-align:top; font-size:10pt;}
|
||||
h1, h2, h3, h4, h5, h6 { clear:both }
|
||||
ol, ul { margin:0; padding:0;}
|
||||
li { list-style: none; margin:0; padding:0;}
|
||||
<!-- "li span.odfLiEnd" - IE 7 issue-->
|
||||
li span. { clear: both; line-height:0; width:0; height:0; margin:0; padding:0; }
|
||||
span.footnodeNumber { padding-right:1em; }
|
||||
span.annotation_style_by_filter { font-size:95%; font-family:Arial; background-color:#fff000; margin:0; border:0; padding:0; }
|
||||
* { margin:0;}
|
||||
.ta1 { writing-mode:lr-tb; }
|
||||
.Default { font-family:Liberation Sans; }
|
||||
.ce100 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce101 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce102 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce103 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce104 { font-family:Liberation Sans; background-color:#e6e6e6; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce57 { font-family:Liberation Sans; background-color:transparent; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce58 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce59 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce60 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce61 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce62 { font-family:Liberation Sans; background-color:#e6e6e6; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce63 { font-family:Liberation Sans; background-color:#e6e6e6; border-width:0,0621cm; border-style:solid; border-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; font-weight:bold; }
|
||||
.ce64 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-weight:bold; }
|
||||
.ce65 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
|
||||
.ce66 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
|
||||
.ce67 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
|
||||
.ce68 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; font-weight:bold; }
|
||||
.ce69 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6ff; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; font-weight:bold; }
|
||||
.ce70 { font-family:Liberation Sans; background-color:#e6e6e6; font-weight:bold; }
|
||||
.ce71 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce72 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
|
||||
.ce73 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce74 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce75 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce76 { font-family:Liberation Sans; border-bottom-style:none; background-color:transparent; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce77 { font-family:Liberation Sans; text-align:center ! important; margin-left:0cm; }
|
||||
.ce78 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce79 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
|
||||
.ce80 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce81 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce82 { font-family:Liberation Sans; background-color:transparent; text-align:center ! important; margin-left:0cm; }
|
||||
.ce83 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce84 { font-family:Liberation Sans; background-color:#ffff00; text-align:center ! important; margin-left:0cm; }
|
||||
.ce85 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#ffff00; border-left-style:none; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce86 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-style:none; border-right-style:none; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce87 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:underline; font-weight:bold; }
|
||||
.ce88 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
|
||||
.ce89 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce90 { font-family:Liberation Sans; border-bottom-style:none; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce91 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce92 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce93 { font-family:Liberation Sans; border-bottom-style:none; background-color:#ffff00; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-style:none; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce94 { font-family:Liberation Sans; border-bottom-style:none; background-color:transparent; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce95 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:transparent; border-left-style:none; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.ce96 { font-family:Liberation Sans; border-bottom-style:none; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; font-size:11pt; text-decoration:none ! important; font-weight:bold; }
|
||||
.ce97 { font-family:Liberation Sans; border-bottom-width:0,0621cm; border-bottom-style:solid; border-bottom-color:#000000; background-color:#e6e6e6; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; font-weight:bold; }
|
||||
.ce98 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-width:0,0621cm; border-top-style:solid; border-top-color:#000000; text-align:center ! important; margin-left:0cm; }
|
||||
.ce99 { font-family:Liberation Sans; border-bottom-style:none; border-left-width:0,0621cm; border-left-style:solid; border-left-color:#000000; border-right-width:0,0621cm; border-right-style:solid; border-right-color:#000000; border-top-style:none; text-align:center ! important; margin-left:0cm; }
|
||||
.co1 { width:3.399cm; }
|
||||
.co10 { width:1.304cm; }
|
||||
.co11 { width:1.942cm; }
|
||||
.co12 { width:1.815cm; }
|
||||
.co13 { width:1.348cm; }
|
||||
.co14 { width:1.432cm; }
|
||||
.co15 { width:2.258cm; }
|
||||
.co2 { width:6.281cm; }
|
||||
.co3 { width:1.475cm; }
|
||||
.co4 { width:1.277cm; }
|
||||
.co5 { width:1.602cm; }
|
||||
.co6 { width:1.39cm; }
|
||||
.co7 { width:1.686cm; }
|
||||
.co8 { width:1.644cm; }
|
||||
.co9 { width:2.452cm; }
|
||||
.ro1 { height:0.487cm; }
|
||||
.ro2 { height:0.885cm; }
|
||||
.ro3 { height:0.452cm; }
|
||||
<!-- ODF styles with no properties representable as CSS -->
|
||||
{ }
|
||||
</style></head><body dir="ltr" style="writing-mode:lr-tb; "><table border="0" cellspacing="0" cellpadding="0" class="ta1"><colgroup><col width="149"/><col width="274"/><col width="64"/><col width="56"/><col width="70"/><col width="61"/><col width="74"/><col width="72"/><col width="107"/><col width="57"/><col width="107"/><col width="107"/><col width="74"/><col width="85"/><col width="79"/><col width="79"/><col width="59"/><col width="61"/><col width="63"/><col width="99"/></colgroup><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce57"> </td><td style="text-align:left;width:6.281cm; " class="ce57"> </td><td colspan="6" style="text-align:left;width:1.475cm; " class="ce71"><p>Sequence Containers</p></td><td colspan="4" style="text-align:left;width:2.452cm; " class="ce71"><p>Associative Containers</p></td><td style="text-align:left;width:1.686cm; " class="ce96"> </td><td colspan="6" style="text-align:left;width:1.942cm; " class="ce71"><p>Synchronization Containers</p></td><td style="text-align:left;width:2.258cm; " class="ce104"> </td></tr><tr class="ro2"><td style="text-align:left;width:3.399cm; " class="ce58"> </td><td style="text-align:left;width:6.281cm; " class="ce63"><p>Method</p></td><td style="text-align:left;width:1.475cm; " class="ce72"><p>ARRAY</p></td><td style="text-align:left;width:1.277cm; " class="ce79"><p>LIST</p></td><td style="text-align:left;width:1.602cm; " class="ce79"><p>DUALP</p><p>LIST</p></td><td style="text-align:left;width:1.39cm; " class="ce79"><p>I-LIST</p></td><td style="text-align:left;width:1.686cm; " class="ce79"><p>DEQUE</p></td><td style="text-align:left;width:1.644cm; " class="ce88"><p>BITSET</p></td><td style="text-align:left;width:2.452cm; " class="ce72"><p>B+TREE</p><p>DICT/SET</p></td><td style="text-align:left;width:1.304cm; " class="ce79"><p>R/B</p><p>TREE</p></td><td style="text-align:left;width:2.452cm; " class="ce79"><p>[S]HASH</p><p>DICT / SET</p></td><td style="text-align:left;width:2.452cm; " class="ce88"><p>OA DICT</p></td><td style="text-align:left;width:1.686cm; " class="ce97"><p>PRIO </p><p>QUEUE</p></td><td style="text-align:left;width:1.942cm; " class="ce72"><p>BUFFER</p></td><td style="text-align:left;width:1.815cm; " class="ce79"><p>QUEUE </p><p>MPMC</p></td><td style="text-align:left;width:1.815cm; " class="ce79"><p>QUEUE </p><p>SPSC</p></td><td style="text-align:left;width:1.348cm; " class="ce79"><p>SNAP</p><p>SPSC</p></td><td style="text-align:left;width:1.39cm; " class="ce79"><p>SNAP</p><p>SPMC</p></td><td style="text-align:left;width:1.432cm; " class="ce88"><p>SNAP </p><p>MPMC</p></td><td style="text-align:left;width:2.258cm; " class="ce70"> </td></tr><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce59"><p>CONSTRUCTOR</p></td><td style="text-align:left;width:6.281cm; " class="ce64"><p>_init</p></td><td style="text-align:left;width:1.475cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.277cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.602cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.686cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.644cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.304cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:1.686cm; " class="ce98"><p>X</p></td><td style="text-align:left;width:1.942cm; " class="ce73"><p>X</p></td><td style="text-align:left;width:1.815cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.815cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.348cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce80"><p>X</p></td><td style="text-align:left;width:1.432cm; " class="ce89"><p>X</p></td><td style="text-align:left;width:2.258cm; " class="Default"> </td></tr><tr class="ro1"><td style="text-align:left;width:3.399cm; " class="ce60"> </td><td style="text-align:left;width:6.281cm; " class="ce65"><p>_set</p></td><td style="text-align:left;width:1.475cm; " class="ce74"><p>X</p></td><td style="text-align:left;width:1.277cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.602cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.39cm; " class="ce77"> </td><td style="text-align:left;width:1.686cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:1.644cm; " class="ce90"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce74"><p>X</p></td><td style="text-align:left;width:1.304cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce77"><p>X</p></td><td style="text-align:left;width:2.452cm; " class="ce90"><p>X</p></td><td style="text-align:left;width:1.686cmLine truncated
|
||||
Binary file not shown.
+139
-23
@@ -2,39 +2,155 @@ How to make a release?
|
||||
======================
|
||||
|
||||
1) Check that all test pass on all primary systems.
|
||||
Run 'make check' on all primary systems.
|
||||
Run 'make check' or 'make checkall' on all primary systems.
|
||||
No error shall be reported.
|
||||
|
||||
2) Check that no memory leak is reported by valgrind:
|
||||
Run 'make valgrind' in tests folder on a system.
|
||||
NOTE: valgrind seems completly lost in all the tests which spawn a lot of threads.
|
||||
|
||||
3) Check that no undefined behavior, nor data race, ... is reported by sanitizer:
|
||||
2) Check that no undefined behavior, nor data race, ... is reported by sanitizer:
|
||||
Run 'make sanitize' in tests folder on a system.
|
||||
This needs at least GCC 7
|
||||
|
||||
4) Check that no new warning is reported by static analyser:
|
||||
Run 'make scan-build' in tests folder on a system.
|
||||
This needs at least clang 4.
|
||||
Currently there is a false positive detected in m-shared:
|
||||
"Argument to free() is offset by 8 bytes from the start of memory allocated by malloc()"
|
||||
scan-build is not able to properly track that this case cannot open.
|
||||
|
||||
5) Check that coverage is reasonnable.
|
||||
3) Check that coverage is reasonable.
|
||||
Run 'make coverage' and analyze result.
|
||||
|
||||
6) Publish the release
|
||||
4) Publish the release
|
||||
|
||||
In case of problem, open a problem report.
|
||||
|
||||
List of primary systems
|
||||
=======================
|
||||
|
||||
* gcc 4.9 on linux/x86-64
|
||||
* gcc 7 on linux/x86-64
|
||||
* clang 3.5 on linux/x86-64
|
||||
* clang 4 on linux/x86-64
|
||||
* g++ 7 on linux/x86-64
|
||||
List of automatically tested systems
|
||||
====================================
|
||||
|
||||
* gcc on linux/x86-64 (32 bits and 64 bits)
|
||||
* g++ on linux/x86-64 (32 bits and 64 bits)
|
||||
* clang on macos/x86-64
|
||||
* clang++ on macos/x86-64
|
||||
* tcc on linux/x86-64
|
||||
* gcc 6 on windows/x86-64
|
||||
* gcc on windows/x86-64 (mingw)
|
||||
* msvc on windows/x86-64
|
||||
* clang on windows/x86-64
|
||||
|
||||
List of manually tested systems
|
||||
===============================
|
||||
|
||||
* clang on linux/x86-64
|
||||
* clang on linux/armv7l
|
||||
* gcc on linux/powerpc
|
||||
* gcc on linux/armv7l
|
||||
|
||||
Atomic library
|
||||
==============
|
||||
|
||||
For the targets armel, m68k, mips, mipsel, powerpc, powerpcspe or sh4,
|
||||
you may need to run the test suite by adding the atomic library:
|
||||
|
||||
make check LDFLAGS="-pthread -latomic"
|
||||
|
||||
since 'atomic' library is not automatically included for 32 bits systems.
|
||||
|
||||
|
||||
Cross compilation
|
||||
=================
|
||||
|
||||
There is no need to cross compile the library as it is a header only library.
|
||||
|
||||
However if you want to run the tests with a cross-compiler, do like the following
|
||||
command. It performs a cross-compilation of the test suite from linux to windows
|
||||
and run the test suite with wine:
|
||||
|
||||
make CC="i586-mingw32msvc-gcc -std=c99 -DWINVER=0x600 -D_WIN32_WINNT=0x600" LOG_COMPILER=wine check
|
||||
|
||||
|
||||
Handling issues
|
||||
===============
|
||||
|
||||
Issues are opened in the bug tracker of the project (typically https://github.com/P-p-H-d/mlib/issues )
|
||||
|
||||
If the issue cannot be taken into account quickly and are long term,
|
||||
it should be moved into the long term issues included in the repository:
|
||||
https://github.com/P-p-H-d/mlib/blob/master/doc/ISSUES.org
|
||||
This is a file in ORG mode: the issues are created with the proper tags.
|
||||
|
||||
The original issue in the tracker can then be closed with the issuer agreement.
|
||||
|
||||
This will have the following gains:
|
||||
|
||||
- no hard dependencies on any bug tracking infrastructure (can be migrated easily).
|
||||
- a release will automatically contain all its open problem reports.
|
||||
|
||||
Once one issue of ISSUES.org is taken into account,
|
||||
its state shall be changed to DONE (the issue shall not be removed).
|
||||
|
||||
|
||||
Debugging
|
||||
=========
|
||||
|
||||
Debugging the library can be quite difficult
|
||||
as it performs heavy preprocessing expansion
|
||||
and expands all code in one line.
|
||||
Different strategy for debugging the library itself are still possible:
|
||||
|
||||
1) Good old printf.
|
||||
Add printf of interresting values in the debugged function.
|
||||
|
||||
|
||||
2) Preprocess the library and then compile the preprocessed file like this:
|
||||
|
||||
cc -std=c99 -E test-file.c > test-file.i
|
||||
perl -pi -e 's/;/;\n/g' test-file.i
|
||||
cc -std=c99 -c -Wall test-file.i
|
||||
|
||||
The resulting executable can be debugged much more easily.
|
||||
|
||||
|
||||
3) To debug the preprocessing itself, the Boot Wave library can be used
|
||||
and in particular the [wave driver](https://www.boost.org/doc/libs/1_71_0/libs/wave/doc/wave_driver.html)
|
||||
|
||||
Add in the code the following pragmas around the code to debug:
|
||||
|
||||
#pragma wave trace(enable)
|
||||
ARRAY_DEF (array_charp, char *)
|
||||
#pragma wave trace(disable)
|
||||
|
||||
Run the wave driver:
|
||||
|
||||
./wave ex-array01.c -I .. --c99 -t test.trace
|
||||
|
||||
The different expansion performed by the library are described in test.trace
|
||||
|
||||
Coding rules
|
||||
============
|
||||
|
||||
* Use of non standard C shall be protected under a specific compiler test,
|
||||
so that the library remains compatible with a strict C99 compiler.
|
||||
|
||||
* C11 features shall be optional.
|
||||
|
||||
* Use 'make format' to format the header files properly.
|
||||
|
||||
* External types shall be suffixed by _t and in lower case
|
||||
but shall be optional if the user doesn't want then.
|
||||
|
||||
* Internal types shall be suffixed by _ct and in lower case
|
||||
(as confidential types)
|
||||
|
||||
* External / Internal functions shall be in lower case.
|
||||
|
||||
* External / Internal macros shall be in upper case.
|
||||
|
||||
* Internal macros / functions shall be prefixed by m_<container>
|
||||
with container is the name of the container with one letter transformed:
|
||||
o --> 0
|
||||
e --> 3
|
||||
i --> 1
|
||||
y --> 4
|
||||
This prevents the automatic completion to work when the user starts writing its function.
|
||||
|
||||
* Test functions shall be predicated and be suffixed by '_p'
|
||||
|
||||
|
||||
Misc
|
||||
====
|
||||
|
||||
To put a breakpoint for the thread sanitizer in case of error, in gdb add:
|
||||
|
||||
$ set env TSAN_OPTIONS=halt_on_error=1,abort_on_error=1
|
||||
+453
@@ -0,0 +1,453 @@
|
||||
*Long term issues*
|
||||
------------------
|
||||
|
||||
* TODO #26 : Hash-Map for huge objects :ENHANCEMENT:
|
||||
|
||||
Specialize Hashmap for huge objects on supported hardware:
|
||||
|
||||
Based on OA hash map with taggued pointer (needs space for tagging):
|
||||
low bits = high bits of hash
|
||||
pointer to object { key, hash, value }
|
||||
|
||||
Can solve collision without dereferencing object with 1/2^lowbits chances.
|
||||
NB: hash may be uneeded due to the small chances of collision.
|
||||
Reduce cache usage ==> Should be faster.
|
||||
Increase cache usage for very small object (integer).
|
||||
|
||||
Use of pointer to avoid moving object on GC.
|
||||
|
||||
On AMD64, only 48 bits of 64 bits are really used for addressing data.
|
||||
==> lowbits= 16
|
||||
Empty representation= 0 (hash can be 0. pointer cannot be it).
|
||||
Deleted representation = 1 (pointer cannot be zero)
|
||||
|
||||
<---- PTR ----><-HASH->
|
||||
|
||||
#+BEGIN_SRC C
|
||||
#define LOW_BITS 16
|
||||
#define ALIGNED_BITS 2
|
||||
void *get_ptr(int64_t x) {
|
||||
return (void*) ((x >> LOW_BITS) << ALIGNED_BITS);
|
||||
}
|
||||
unsigned get_small_hash(int64_t x) {
|
||||
return x & ((1<<LOW_BITS)-1);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
* TODO #25 : Support of error code model for error handling. :ENHANCEMENT:
|
||||
|
||||
Find a way to support error return code for the API in case of allocation
|
||||
failure.
|
||||
|
||||
Any service that returns void shall return a "int".
|
||||
In case of allocation failure, it shall return an error.
|
||||
M_CALL macro shall stop its execution if the service returns an error code
|
||||
and the error code represents an error (avoid rewritting everything)
|
||||
and throw back the error code (stopping the execution flow).
|
||||
|
||||
Services returning already something shall not be modified.
|
||||
|
||||
This model should be applied at the container level only and not globally.
|
||||
Different containers may need different levels of error handling.
|
||||
4 combinaison to take into account:
|
||||
|
||||
==> ABORT on container / ABORT on used type: The current model
|
||||
==> ABORT on container / RETCODE on used type: Needs to abort on reported error.
|
||||
==> RETCODE on container / ABORT on used type: Nothing particular to note.
|
||||
==> RETCODE on container / RETCODE on used type: Forward retcode to caller.
|
||||
|
||||
Find a way to make it while not making the code too complex
|
||||
(try to keep as simple as possible).
|
||||
|
||||
Using a specialized oplist for such containers (_OPLIST_RETCODE)
|
||||
using a specialized API for retcode:
|
||||
|
||||
API_RET_*: that expands to
|
||||
|
||||
if (func(args) == 0) goto handler_exit_failure;
|
||||
|
||||
Adding at the end of each service:
|
||||
M_END_FUNCTION(cleanup_code) that expands to different cases:
|
||||
M_END_FUNCTION_VALUE(cleanup_code, return_success, return_failure) that expands to different cases:
|
||||
|
||||
ABORT/ABORT:
|
||||
handler_exit_success:
|
||||
handler_exit_failure:
|
||||
return;
|
||||
|
||||
ABORT/RETCODE:
|
||||
handler_exit_success:
|
||||
return;
|
||||
handler_exit_failure:
|
||||
RAISE_FATAL_ERROR
|
||||
|
||||
RETCODE/ABORT
|
||||
handler_exit_success:
|
||||
return SUCESS;
|
||||
handler_exit_failure:
|
||||
cleanup_code
|
||||
return FAILURE;
|
||||
|
||||
RETCODE/RETCODE
|
||||
handler_exit_success:
|
||||
return SUCESS;
|
||||
handler_exit_failure:
|
||||
cleanup_code
|
||||
return FAILURE;
|
||||
|
||||
If really needed, the macro can be avoided and code can be hand written.
|
||||
|
||||
Open points:
|
||||
- How to handle warnings on unused labels?
|
||||
- What about M_LET/ M_EACH?
|
||||
|
||||
|
||||
* TODO #24 : New: Add MIN-MAX-HEAP container :ENHANCEMENT:
|
||||
|
||||
See https://en.wikipedia.org/wiki/Min-max_heap
|
||||
as DPRIORITY_QUEUE_DEF ?
|
||||
|
||||
NOTE: Needs for such container?
|
||||
|
||||
|
||||
* TODO #23: Strict MOVE semantic to clarify :ENHANCEMENT:
|
||||
|
||||
Some type may need to have a force MOVE semantic (for example, they can store
|
||||
pointer to themselves). Currently the INIT_MOVE & MOVE operators are more
|
||||
a help for performance than a strict semantic usage.
|
||||
|
||||
** ARRAY container
|
||||
|
||||
The ARRAY container doesn't support strict MOVE semantic for example.
|
||||
It is not a simple matter as it performs a realloc of the table, thus
|
||||
moving the data before they can be moved using MOVE. Two solutions:
|
||||
|
||||
- New operator MOVE_SELF to fix a type after it has been moved.
|
||||
- If MOVE defined, force another table and then copy by hand the type. This will be slower and consumme more memory.
|
||||
|
||||
** MOVE in OPLIST :ENHANCEMENT:
|
||||
|
||||
Proposal: do not export INIT_MOVE / MOVE operator in OPLIST if the MOVE operator is
|
||||
compatible with a pure COPY semantic. An exported MOVE operator will tell
|
||||
other containers than the type shall be carefully moved using the provided
|
||||
MOVE operator.
|
||||
|
||||
For example for tuple, it shall
|
||||
|
||||
- create an init_move operator if no one has disabled INIT_MOVE,
|
||||
- export the init_move operator if at least one has exported a INIT_MOVE and no one has disabled INIT_MOVE.
|
||||
|
||||
** DO_INIT_MOVE operator
|
||||
|
||||
DO_INIT_MOVE macro is not also fully working for structure
|
||||
defined with [1] tricks but without an explicit INIT_MOVE / MOVE
|
||||
operators as it uses MOVE_DEFAULT which is not (fully compatible).
|
||||
==> Analyse limitation and possible constraint usages.
|
||||
|
||||
|
||||
* TODO #20: New: Bucket priority queue :ENHANCEMENT:
|
||||
|
||||
Add a new kind of priority queue.
|
||||
See https://en.wikipedia.org/wiki/Bucket_queue
|
||||
|
||||
Check if it will be better as intrusive or non-intrusive container.
|
||||
|
||||
To test if a bucket is empty or not, a bitfield can be used to check if
|
||||
the bit associated to the bucket is set or not. To get the highest bucket
|
||||
non empty, we can perform a CLZ of the bitset, which shall be much faster than
|
||||
performing a linear search of the buckets (algorithm complexity is the same,
|
||||
except that we can scan 64 entries at a time).
|
||||
|
||||
Check if we can use BITSET, or introduce fixed size BITSET or use ad-hock
|
||||
implementation.
|
||||
|
||||
|
||||
* TODO #19: New: Intrusive Red Black Tree :ENHANCEMENT:
|
||||
|
||||
Add intrusive red black tree.
|
||||
Look also for AVL tree (NOTE: Is there a performance difference between the two?)
|
||||
|
||||
|
||||
* TODO #18: Missing methods :ENHANCEMENT:
|
||||
|
||||
Some containers don't have all the methods they should.
|
||||
See the cells in yellow here:
|
||||
http://htmlpreview.github.io/?https://github.com/P-p-H-d/mlib/blob/master/doc/Container.html
|
||||
|
||||
|
||||
* TODO #17: New: Ressource handler :ENHANCEMENT:
|
||||
|
||||
A global 'ressouce handler' which shall associated a unique handle to a ressource.
|
||||
The handle shall be unique for the ressource and shall not be reused.
|
||||
It is typically a 64 bits integers always incremented (even if the program
|
||||
creates one billion ressources per second, the counter won't overflow
|
||||
until 585 years).
|
||||
|
||||
The ressource handler shall make an association between a HANDLE 64 bits and:
|
||||
|
||||
- how much real owners claim to own the ressource
|
||||
(the ressource is only owned by the ressource handler, however
|
||||
it acts as a delegate of the real owner),
|
||||
- how much users keep a pointer to the ressource.
|
||||
- pointer to the resource itself.
|
||||
|
||||
This may be a better alternative than shared_ptr & weak_ptr:
|
||||
|
||||
- reduce fragmentation,
|
||||
- no cycle dependencies,
|
||||
- shared_ref & weak_ref becomes only HANDLE,
|
||||
- all ressources can be freed in one pass.
|
||||
|
||||
Needs lock free dictionnary or at least concurrent dictionnary.
|
||||
|
||||
How to handle multiple resource ?
|
||||
|
||||
* variant: works fine. Memory usage can be (much) higher than needed if there is a lot of dissimilarity between the size of the objects.
|
||||
* embedded the type in the ressource handler: more work, API more complex. Memory usage seems better.
|
||||
|
||||
|
||||
* TODO #16: New: Lock Free List :ENHANCEMENT:
|
||||
|
||||
Implement a lock free list. Most of the difficulty is the memory reclamation part.
|
||||
Typically this lock free list shall be compatible with RCU method.
|
||||
|
||||
** First step: backoff methods :DONE:
|
||||
** Second step: lock free node pool : :DONE:
|
||||
Done as m-c-mempool header.
|
||||
|
||||
** Third step: Implement generic lock free list on top of it.
|
||||
|
||||
The ABA problem is already taken into account by the memory alloctor
|
||||
provided that the lock free list doesn't try to be smart.
|
||||
|
||||
backoff has be used when using CAS.
|
||||
|
||||
Concurrent insertion / insertion and insertion / deletion and deletion / deletion shall be crefully analyzed when taken into account.
|
||||
|
||||
Questions:
|
||||
- singly or doubly or dual push?
|
||||
- needs to be logically deleted : needs a previous field
|
||||
(NULL if not logically deleted) ? TBC
|
||||
|
||||
|
||||
* TODO #15: Prologue / Epilogue for Constructor / Destructor for error handling :ENHANCEMENT:
|
||||
|
||||
Constructor (and destructor) need to use user-defined prologue / epilogue.
|
||||
This is in order to register the constructed object into a proper Exception
|
||||
Handling Stack so that throwing exceptions may work reliably.
|
||||
|
||||
Proposal:
|
||||
|
||||
- M_CONSTRUCTOR_PROLOGUE(object, oplist);
|
||||
- M_CONSTRUCTOR_EPILOGUE(object, oplist);
|
||||
- M_DESTRUCTOR_PROLOGUE(object, oplist);
|
||||
- M_DESTRUCTOR_EPILOGUE(object, oplist);
|
||||
|
||||
Object creation will need to add all sub-objects into the stack,
|
||||
then unstack all to push instead the root object (which recursively remove them).
|
||||
|
||||
See also http://freetype.sourceforge.net/david/reliable-c.html#cseh
|
||||
|
||||
- How to handle like allocation of the object?
|
||||
- How to avoid calling the destructor multiple times? (It is needed?)
|
||||
- How the code can be factorized with RETCODE needs?
|
||||
|
||||
|
||||
* TODO #14: Memory allocation enhancement :ENHANCEMENT:
|
||||
|
||||
Enhancement of the memory allocation scheme to find way to deal properly with advanced allocators:
|
||||
|
||||
- non-default alignment requirements for types,
|
||||
- instance-based allocator (may need instance based variable access),
|
||||
- expected life of created type (temporary or permanent),
|
||||
- stack based allocator,
|
||||
- global variable access for allocator,
|
||||
- maximum allocation before failure.
|
||||
|
||||
Most of theses are already more or less supported. Examples shall be created to show how to deal with this:
|
||||
|
||||
- alignement shall be implemented with the attributes of <stdalign.h>
|
||||
|
||||
However I sill don't know how to implement "instance-based allocator" which is what is missing.
|
||||
The problem is how to give to methods context local information store within the container itself.
|
||||
|
||||
Update:
|
||||
|
||||
API transformation support enables "instance-based allocator" to be made easily.
|
||||
Needs some formal operator in the oplist to support it fully and an example.
|
||||
|
||||
Can be supported using another API extension, some more operators and forcing some names:
|
||||
|
||||
* API_N: call like FUNC(obj->extra_data, type)
|
||||
|
||||
'obj' is a forced named corresponding to an alias to an object in the function.
|
||||
Operator needed:
|
||||
|
||||
- EXTRA_DATA: Add an extra-data field wihtin the container. Defines the type of data.
|
||||
|
||||
It is a kind of object inheritance where the container inherits some extra data from its base.
|
||||
|
||||
|
||||
* TODO #12: New: Atomic shared pointer :ENHANCEMENT:
|
||||
|
||||
Add an extension to the SHARED_PTR API:
|
||||
|
||||
- New type atomic_shared_ptr
|
||||
- name_init_atomic_set (&atomic_shared_ptr, shared_ptr);
|
||||
- name_init_set_atomic (shared_ptr, &atomic_shared_ptr);
|
||||
- name_init_atomic_set_atomic (&atomic_shared_ptr, &atomic_shared_ptr);
|
||||
- name_atomic_set (&atomic_shared_ptr, shared_ptr);
|
||||
- name_set_atomic (shared_ptr, &atomic_shared_ptr);
|
||||
- name_atomic_set_atomic (&atomic_shared_ptr, &atomic_shared_ptr);
|
||||
- name_atomic_clear
|
||||
|
||||
No _ref or direct _init: we need to init first a normal shared_ptr then the atomic (TBC)
|
||||
|
||||
** _atomic_set method:
|
||||
|
||||
It can be implemented by incrementing the non atomic shared pointer reference,
|
||||
then performs a compare_and_swap to the data of the atomic shared pointer,
|
||||
finally decrement and dec/free the swapped previous data of the atomic like a normal shared pointer.
|
||||
All 3 steps are safe.
|
||||
|
||||
** _set_atomic method:
|
||||
|
||||
It needs to perform the following atomic operation : <read the pointer, deref pointer and increment the pointed value> I don't known how to do it properly.
|
||||
|
||||
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4162.pdf
|
||||
|
||||
Proposition for _set_atomic we store temporary NULL to the atomic_ptr struct to request an exclusive access to the data (this looks like a lock and other operations need to handle NULL) :
|
||||
|
||||
#+BEGIN_SRC C
|
||||
void shared_ptr_set_atomic(ptr a, atomic_ptr *ptr)
|
||||
{
|
||||
// Get exclusive access to the data
|
||||
p = atomic_load(ptr);
|
||||
do {
|
||||
if (p == NULL) {
|
||||
// TODO: exponential backoff
|
||||
p = atomic_load(ptr);
|
||||
continue;
|
||||
}
|
||||
} while (!atomic_compare_exchange_weak(ptr, &p, NULL));
|
||||
// p has exclusive access to the pointer
|
||||
p->data->cpt ++;
|
||||
a->data = p->data;
|
||||
atomic_exchange (ptr, p);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
This prevents using NULL which obliges atomic shared pointer to point to a created object...
|
||||
|
||||
Other alternative solution is to use the bit 0 to mark the pointer as being updated, preventing other from using it (TBC only clear):
|
||||
|
||||
#+BEGIN_SRC C
|
||||
void shared_ptr_set_atomic(ptr a, atomic_ptr *ptr)
|
||||
{
|
||||
// Get exclusive access to the data
|
||||
p = atomic_load(ptr);
|
||||
do {
|
||||
if ( (p&1) != 0) {
|
||||
// TODO: exponential backoff
|
||||
p = atomic_load(ptr);
|
||||
continue;
|
||||
}
|
||||
} while (!atomic_compare_exchange_weak(ptr, &p, p|1));
|
||||
// Exclusive access (kind of lock).
|
||||
p->data->cpt ++;
|
||||
a->data = p->data;
|
||||
atomic_set (ptr, p);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
Other implementation seems to have it hard to be lock-free: cf. https://github.com/llvm-mirror/libcxx/commit/5fec82dc0db3623546038e4a86baa44f749e554f
|
||||
|
||||
|
||||
|
||||
* TODO #5: New: Concurrent dictionary Container :ENHANCEMENT:
|
||||
|
||||
Implement a more efficient dictionary than lock + std dictionary for all operations when dealing with threads.
|
||||
See https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx
|
||||
|
||||
The best parallel algorithm is still when there is as few synchronization as possible. A concurrent dictionary will fail at this and will result in average performance at best.
|
||||
The typical best case will be in RCU context (a lot of readers, few writers), so the interface shall be compatible with such structure.
|
||||
|
||||
** Multiple locks within the dictionnary
|
||||
|
||||
A potential implementation may be to request at initialization time the number of concurrent thread N.
|
||||
Create a static array of N dictionnary with N mutex. Then to access the data will perform :
|
||||
|
||||
- compute hash of object,
|
||||
- access high bits of hash and select which dictionnary shall have the data,
|
||||
- lock it,
|
||||
- perform classic access to the data (check if the compiler can properly optimize the hash computation),
|
||||
- unlock it.
|
||||
|
||||
The property of the hash shall allow a good dispersion of the data across multiple locks, reducing the constraints on the lock. This implementation could be build easily upon the already existent dictionary.
|
||||
|
||||
To test.
|
||||
|
||||
See also https://github.com/simonhf/sharedhashfile
|
||||
|
||||
** Lock Free dictionnary
|
||||
|
||||
Evaluate also lock-free dictionary (easier with open addressing).
|
||||
It needs a complete rewrite of the inner loop through. The hard part is the dynamic resizing of the internal array.
|
||||
Ssee http://preshing.com/20160222/a-resizable-concurrent-map/ for a potential solution
|
||||
and http://www.cs.toronto.edu/~tomhart/papers/tomhart_thesis.pdf for memory reclamation techniques).
|
||||
and https://www.research.ibm.com/people/m/michael/spaa-2002.pdf
|
||||
https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.2017.11.22a.pdf presents different techniques used by linux kernel.
|
||||
It needs before lock-free list: http://www.cse.yorku.ca/~ruppert/papers/lfll.pdf http://www.cse.yorku.ca/~ruppert/Mikhail.pdf
|
||||
|
||||
A good way maybe Open Addessing table used only for indirection and a freelist memory reclamation container for handle the entries
|
||||
(like a transaction).
|
||||
|
||||
Solution?
|
||||
--------
|
||||
|
||||
Use of the concurrent pool (m-c-mempool) to allocate items.
|
||||
The big table will only store pointers (+ part of hash in unused bits?) to such allocate items.
|
||||
Atomic update is done by allocating a new node, update it, and atomicaly store it in the dict, putting the old one as logically deleted and to reclaim later.
|
||||
(Other threads may still read its data so we cannot free immediatly).
|
||||
|
||||
|
||||
* DONE #22: Enhanced services for SPSC Queue :ENHANCEMENT:
|
||||
|
||||
Add services:
|
||||
|
||||
** DONE _push_bulk
|
||||
Test the capacity of the queue and push as much as possible in the queue
|
||||
with one check of the atomic structure.
|
||||
|
||||
** DONE _pop_bulk
|
||||
Test the capacity of the queue and push as much as possible in the queue
|
||||
with one check of the atomic structure.
|
||||
|
||||
** DONE _push_force:
|
||||
|
||||
If the capacity of the queue is full, pop one element and push it:
|
||||
push always succeed and the queue always keep the youngest element.
|
||||
|
||||
* DONE #21: Generic Binary serialization :ENHANCEMENT:
|
||||
|
||||
Based on issue #26 of https://github.com/P-p-H-d/mlib/issues/26
|
||||
|
||||
Some kind of "binary serialization" on the model of get_str/parse_str
|
||||
could be possible. It would be a great feature from the application
|
||||
point-of-view: binary representation is more bandwidth-efficient if
|
||||
used on network communications.
|
||||
|
||||
It will be good to have import/export methods to the
|
||||
XML/JSON/MSGPACK/PROTOBUF/BINARY format.
|
||||
However, adding all of them on by one in the M*LIB containers
|
||||
doesn't seem satisfactory.
|
||||
|
||||
Instead, adding a generic interface for the serialization of data
|
||||
that may be customized by the user to perform the import/export of
|
||||
objects in whatever format they want into what they want (FILE/memory/...).
|
||||
To simplify it, this interface could only support one kind of import/export
|
||||
per compilation unit.
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
+34
-5
@@ -1,18 +1,47 @@
|
||||
# Copyright (c) 2017-2021, Patrick Pelissier
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# + Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# + Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
CC=cc -std=c99
|
||||
CFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function
|
||||
CFLAGS=-Wall -W -pedantic -Wshadow -Wpointer-arith -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes -Wswitch-default -Wswitch-enum -Wcast-align -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 -Wstrict-prototypes -Winline -Wundef -Wnested-externs -Wcast-qual -Wshadow -Wunreachable-code -Wlogical-op -Wstrict-aliasing=2 -Wredundant-decls -Wold-style-definition -Wno-unused-function -g
|
||||
GMP_DIR=
|
||||
CPPFLAGS=-I..
|
||||
LDFLAGS=-pthread -lgmp
|
||||
LDFLAGS=-pthread
|
||||
RM=rm -f
|
||||
|
||||
.SUFFIXES: .c .exe
|
||||
|
||||
.c.exe:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< -o $@
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) `cat config` $< -o $@
|
||||
|
||||
all:
|
||||
all: config
|
||||
$(MAKE) `ls ex-*.c|sed -e 's/\.c/\.exe/g'`
|
||||
if test "$(CC)" = "cc -std=c99" ; then $(MAKE) `ls ex11-*.c|sed -e 's/\.c/\.exe/g'` CC="cc -std=c11" ; fi
|
||||
|
||||
config:
|
||||
printf "#include <gmp.h>\nint main(void){mpz_t z; mpz_init(z); return 0;}\n" > config-test.c
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp config-test.c -o config-test.exe 2> /dev/null && echo "-DHAVE_GMP=1 -I$(GMP_DIR)/include -L$(GMP_DIR)/lib -lgmp" > config || echo "-DHAVE_GMP=0" > config
|
||||
$(RM) config-test.c config-test.exe
|
||||
|
||||
clean:
|
||||
$(RM) *.exe *.s *~ *.o ./a.dat
|
||||
$(RM) *.exe *.s *~ *.o ./a.dat config
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#include <stdio.h>
|
||||
#include "m-array.h"
|
||||
#include "m-algo.h"
|
||||
|
||||
/* Define a dynamic array of int */
|
||||
ARRAY_DEF(vector_int, int)
|
||||
/* Register the oplist of this dynamic array globally */
|
||||
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
|
||||
/* Define some standards algorithms on this dynamic array */
|
||||
ALGO_DEF(vector_int, vector_int_t)
|
||||
|
||||
/* Define a dynamic array of bool */
|
||||
ARRAY_DEF(vector_bool, bool, M_BOOL_OPLIST)
|
||||
/* Register the oplist of this dynamic array globally */
|
||||
#define M_OPL_vector_bool_t() ARRAY_OPLIST(vector_bool, M_BOOL_OPLIST)
|
||||
/* Define some standards algorithms on this dynamic array */
|
||||
ALGO_DEF(vector_bool, vector_bool_t)
|
||||
|
||||
/* ID function */
|
||||
static bool id(bool x) { return x ; }
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Define, init (& later clear) myvector & tmp as vector_int_t
|
||||
M_LET(myvector, tmp, vector_int_t) {
|
||||
// Fill myvector with 3 int equal to 20
|
||||
vector_int_fill_n(myvector, 3, 20);
|
||||
// Fill tmp with 3 int equal to 33
|
||||
vector_int_fill_n(tmp, 3, 33);
|
||||
// Append all tmp in myvector
|
||||
vector_int_splice(myvector, tmp);
|
||||
// Fill tmp with 2 int equal to 10
|
||||
vector_int_fill_n(tmp, 2, 10);
|
||||
// Append all tmp in myvector
|
||||
vector_int_splice(myvector, tmp);
|
||||
// Display the result
|
||||
printf("myvector contains:");
|
||||
// In C11, the for loop can be simplified as :
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
|
||||
ALGO_FOR_EACH(myvector, vector_int_t, M_PRINT, " ");
|
||||
#else
|
||||
for M_EACH(it, myvector, vector_int_t) {
|
||||
printf(" %d", *it);
|
||||
}
|
||||
#endif
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Same thing but with booleans
|
||||
M_LET(myvector, tmp, vector_bool_t) {
|
||||
vector_bool_fill_n(myvector, 3, false);
|
||||
vector_bool_fill_n(tmp, 3, true);
|
||||
vector_bool_splice(myvector, tmp);
|
||||
vector_bool_fill_n(tmp, 2, false);
|
||||
vector_bool_splice(myvector, tmp);
|
||||
printf("myvector contains:");
|
||||
for M_EACH(it, myvector, vector_bool_t) {
|
||||
printf(" %d", (int)*it);
|
||||
}
|
||||
printf("\n");
|
||||
printf ("ALL: %d ANY: %d NONE: %d\n",
|
||||
vector_bool_all_of_p(myvector, id),
|
||||
vector_bool_any_of_p(myvector, id),
|
||||
vector_bool_none_of_p(myvector, id) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "m-dict.h"
|
||||
#include "m-array.h"
|
||||
#include "m-algo.h"
|
||||
#include "m-string.h"
|
||||
|
||||
/* Define a dictionnary (or hash_map or undordered_map)
|
||||
* from a string_t to int */
|
||||
DICT_DEF2(dict, string_t, int)
|
||||
/* Register the oplist of this dictionnary globally */
|
||||
#define M_OPL_dict_t() DICT_OPLIST(dict, STRING_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
/* Define an array of integer */
|
||||
ARRAY_DEF(vector_int, int)
|
||||
/* Register the oplist of this array of integer globally */
|
||||
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
|
||||
|
||||
/* Micro macro to detect if a string starts with the given pattern */
|
||||
#define start_with(pattern, item) \
|
||||
string_start_with_str_p((item).key, (pattern))
|
||||
|
||||
/* Micro macro to get the value of a pair of (key, value) */
|
||||
#define get_value(out, item) ((out) = (item).value)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int s;
|
||||
// Init keys as a vector_int_t, initialize it (and prepare its destruction)
|
||||
M_LET(keys, vector_int_t)
|
||||
// Initialize m as a dictionnary and initialize it with the given database
|
||||
// Initialize tmp as a dictionanry
|
||||
M_LET( (m, (STRING_CTE("foo"), 1), (STRING_CTE("bar"), 42), (STRING_CTE("bluez"), 7), (STRING_CTE("stop"), 789) ), tmp, dict_t) {
|
||||
|
||||
/* Extract all elements of 'm' that starts with 'b' */
|
||||
ALGO_EXTRACT(tmp, dict_t, m, dict_t, start_with, "b");
|
||||
/* Extract the values of theses elements */
|
||||
ALGO_TRANSFORM(keys, vector_int_t, tmp, dict_t, get_value);
|
||||
/* Sum theses values */
|
||||
ALGO_REDUCE(s, keys, vector_int_t, sum);
|
||||
printf("Sum of elements starting with 'b' is: %d\n", s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "m-algo.h"
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
|
||||
/* Define an array of int */
|
||||
ARRAY_DEF(vector_int, int)
|
||||
/* Register the oplist of this array of int globally */
|
||||
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
|
||||
|
||||
/* Set s (string_t) to the integer i */
|
||||
#define str_ui(s, i) string_printf(s, "%d", i)
|
||||
|
||||
/* Concat two strings by insertion a '-' within them */
|
||||
#define str_cat(s1, s2) (string_cat_str(s1, "-"), string_cat(s1, s2))
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Let s be a string_t and initilize it.
|
||||
M_LET(s, string_t)
|
||||
// Let v be a vector of integer and initialize it.
|
||||
M_LET( (v, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), vector_int_t) {
|
||||
int sum, product;
|
||||
|
||||
// Compute the sum of theses integers.
|
||||
ALGO_REDUCE(sum, v, vector_int_t, add);
|
||||
|
||||
// Compute the product of theses integers.
|
||||
ALGO_REDUCE(product, v, vector_int_t, mul);
|
||||
|
||||
/* Example of reduction with a transformation of the type from int to string_t */
|
||||
ALGO_REDUCE( (s, string_t), v, vector_int_t, str_cat, str_ui);
|
||||
|
||||
printf ("sum: %d\n"
|
||||
"product: %d\n"
|
||||
"dash str: %s\n", sum, product, string_get_cstr(s));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,619 @@
|
||||
/* Include M*LIB header */
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
#include "m-tuple.h"
|
||||
|
||||
/* Provide several examples of different types usage with array,
|
||||
with the oplist to use for each case.
|
||||
|
||||
Each time it creates and initializes an array,
|
||||
fills it with some number,
|
||||
computes its sum,
|
||||
and returns it.
|
||||
|
||||
This is done for several basic C types:
|
||||
- integers,
|
||||
- floats,
|
||||
- pointers,
|
||||
- structures,
|
||||
- tuples
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Generate prototypes and inline functions
|
||||
for basic types.
|
||||
For integer and floats, there is no need to specify
|
||||
an oplist. M_DEFAULT_OPLIST will be used.
|
||||
*/
|
||||
ARRAY_DEF(r_bool, bool)
|
||||
|
||||
/* Basic usage of the type */
|
||||
static int
|
||||
test_bool(int n)
|
||||
{
|
||||
r_bool_t array;
|
||||
|
||||
r_bool_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_bool_push_back(array, i & 1);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_bool_get(array, i);
|
||||
}
|
||||
|
||||
r_bool_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type char */
|
||||
ARRAY_DEF(r_char, char)
|
||||
|
||||
static int
|
||||
test_char(int n)
|
||||
{
|
||||
r_char_t array;
|
||||
|
||||
r_char_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_char_push_back(array, i & 255);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_char_get(array, i);
|
||||
}
|
||||
|
||||
r_char_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type signed int. */
|
||||
ARRAY_DEF(r_int, signed int)
|
||||
|
||||
static int
|
||||
test_int(int n)
|
||||
{
|
||||
r_int_t array;
|
||||
|
||||
r_int_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_int_push_back(array, i);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_int_get(array, i);
|
||||
}
|
||||
|
||||
r_int_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type signed int.
|
||||
But with an explicit oplist.
|
||||
This generates exactly the same code than r_int
|
||||
*/
|
||||
ARRAY_DEF(r_int2, signed int, M_DEFAULT_OPLIST)
|
||||
|
||||
static int
|
||||
test_int2(int n)
|
||||
{
|
||||
r_int2_t array;
|
||||
|
||||
r_int2_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_int2_push_back(array, i);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_int2_get(array, i);
|
||||
}
|
||||
|
||||
r_int2_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type unsigned long long */
|
||||
ARRAY_DEF(r_ullong, unsigned long long)
|
||||
|
||||
static int
|
||||
test_ullong(int n)
|
||||
{
|
||||
r_ullong_t array;
|
||||
|
||||
r_ullong_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_ullong_push_back(array, i);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_ullong_get(array, i);
|
||||
}
|
||||
|
||||
r_ullong_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type float */
|
||||
ARRAY_DEF(r_float, float)
|
||||
|
||||
static int
|
||||
test_float(int n)
|
||||
{
|
||||
r_float_t array;
|
||||
|
||||
r_float_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_float_push_back(array, i/4.0);
|
||||
}
|
||||
|
||||
float s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_float_get(array, i);
|
||||
}
|
||||
|
||||
r_float_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type double and an explicit oplist*/
|
||||
ARRAY_DEF(r_double, double, M_DEFAULT_OPLIST)
|
||||
|
||||
static int
|
||||
test_double(int n)
|
||||
{
|
||||
r_double_t array;
|
||||
|
||||
r_double_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
r_double_push_back(array, i/4.0);
|
||||
}
|
||||
|
||||
float s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
s += *r_double_get(array, i);
|
||||
}
|
||||
|
||||
r_double_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Helper function strdup */
|
||||
static char *my_strdup(const char *p)
|
||||
{
|
||||
size_t s = strlen(p);
|
||||
char *d= malloc(s+1);
|
||||
if(!d) abort();
|
||||
strcpy(d, p);
|
||||
return d;
|
||||
}
|
||||
|
||||
/* Same with type const char *, representing a contant string.
|
||||
We *** need *** an explicit oplist to tell M*LIB
|
||||
to consider the type as a constant string.
|
||||
The oplist to use is M_CSTR_OPLIST.
|
||||
We could have used M_PTR_OPLIST too,
|
||||
but in this case, we won't get proper string order or
|
||||
string equality.
|
||||
*/
|
||||
ARRAY_DEF(r_cstring, const char *, M_CSTR_OPLIST)
|
||||
|
||||
static int
|
||||
test_cstring(int n)
|
||||
{
|
||||
r_cstring_t array;
|
||||
|
||||
r_cstring_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
char buffer[10];
|
||||
// Create a string from the integer
|
||||
sprintf(buffer, "%d", i);
|
||||
// We need to make an explicit copy of the string
|
||||
// since it is not supported by the basic type.
|
||||
char *p = my_strdup(buffer);
|
||||
// Push the copy in the container.
|
||||
r_cstring_push_back(array, p);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get back the string
|
||||
const char *p = *r_cstring_get(array, i);
|
||||
// Convert it to integer
|
||||
int j = atoi(p);
|
||||
s += j;
|
||||
}
|
||||
|
||||
// We also need a special unalloc pass
|
||||
// to free all constant strings we have allocated.
|
||||
for(int i = 0; i < n; i++) {
|
||||
const char *p = *r_cstring_get(array, i);
|
||||
free((void*)(uintptr_t)p);
|
||||
}
|
||||
|
||||
r_cstring_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type string_t of M*LIB, representing a variable string.
|
||||
We don't need explicit oplist to tell M*LIB!
|
||||
The oplist to use is STRING_OPLIST,
|
||||
but it has been registered globaly, so we don't need to explicit
|
||||
give it to array_def! */
|
||||
ARRAY_DEF(r_string, string_t)
|
||||
|
||||
static int
|
||||
test_string(int n)
|
||||
{
|
||||
r_string_t array;
|
||||
string_t str;
|
||||
|
||||
r_string_init(array);
|
||||
string_init (str);
|
||||
|
||||
for(int i = 0; i < n; i++) {
|
||||
// create a string from an integer
|
||||
string_printf(str, "%d", i);
|
||||
// No need to make a copy in the container!
|
||||
r_string_push_back(array, str);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get back the string and convert it back to an integer
|
||||
int j = atoi(string_get_cstr(*r_string_get(array, i)));
|
||||
s += j;
|
||||
}
|
||||
|
||||
// No need to explicit dealloc !
|
||||
string_clear(str);
|
||||
r_string_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with type string_t of M*LIB, representing a variable string
|
||||
and its explicit oplist. And said above, it generates exactly the
|
||||
same code. */
|
||||
ARRAY_DEF(r_string2, string_t, STRING_OPLIST)
|
||||
|
||||
|
||||
|
||||
/* Same with type 'volatile unsigned int *'
|
||||
The array stores pointers to another integer,
|
||||
allocated somewhere else.
|
||||
We need to tell M*LIB to handle the basic type
|
||||
as pointers without caring for what the value of
|
||||
the pointer is.
|
||||
So here, we **need** to specify the oplist of the type
|
||||
as M_PTR_OPLIST.
|
||||
*/
|
||||
ARRAY_DEF(r_vintptr, volatile unsigned int *, M_PTR_OPLIST)
|
||||
|
||||
static int
|
||||
test_vintptr(int n)
|
||||
{
|
||||
r_vintptr_t array;
|
||||
|
||||
// Allocate a secondary table to store the value.
|
||||
unsigned *tab = calloc(n, sizeof(volatile unsigned int));
|
||||
if (!tab) abort();
|
||||
|
||||
r_vintptr_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
tab[i] = i*i - i;
|
||||
// Push the pointer to this integer in the array
|
||||
r_vintptr_push_back(array, &tab[i]);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the integer.
|
||||
volatile unsigned *p = *r_vintptr_get(array, i);
|
||||
s += *p;
|
||||
}
|
||||
|
||||
r_vintptr_clear(array);
|
||||
free(tab);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type.
|
||||
We need to tell M*LIB to handle the basic type
|
||||
as POD;
|
||||
So here, we **need** to specify the oplist of the type
|
||||
as M_POD_OPLIST.
|
||||
*/
|
||||
struct rock_me_out {
|
||||
int n;
|
||||
float other;
|
||||
};
|
||||
|
||||
ARRAY_DEF(r_rockme, struct rock_me_out, M_POD_OPLIST)
|
||||
|
||||
static int
|
||||
test_rockme(int n)
|
||||
{
|
||||
r_rockme_t array;
|
||||
|
||||
r_rockme_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
struct rock_me_out rock;
|
||||
rock.n = i*i - i;
|
||||
// Push the struct in the array
|
||||
r_rockme_push_back(array, rock);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the struct
|
||||
struct rock_me_out *p = r_rockme_get(array, i);
|
||||
s += p->n;
|
||||
}
|
||||
|
||||
r_rockme_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type defined with [1]:
|
||||
We need to tell M*LIB to handle the basic type
|
||||
as a special array;
|
||||
So here, we **need** to specify the oplist of the type
|
||||
as M_A1_OPLIST.
|
||||
*/
|
||||
typedef struct rock_me_in {
|
||||
int n;
|
||||
float other;
|
||||
} rock_me_in[1];
|
||||
|
||||
ARRAY_DEF(r_rockme2, rock_me_in, M_A1_OPLIST)
|
||||
|
||||
static int
|
||||
test_rockme2(int n)
|
||||
{
|
||||
r_rockme2_t array;
|
||||
|
||||
r_rockme2_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
rock_me_in rock;
|
||||
rock->n = i*i - i;
|
||||
r_rockme2_push_back(array, rock);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the struct.
|
||||
rock_me_in *p = r_rockme2_get(array, i);
|
||||
s += (*p)->n;
|
||||
}
|
||||
|
||||
r_rockme2_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type defined with [1] and a global registeration.
|
||||
We need to tell M*LIB to handle the basic type
|
||||
as a special array.
|
||||
|
||||
To do so, we register the oplist of rock_us_t by defining a macro based
|
||||
on M_OPL_ + type name + () which expands with the oplist of the type.
|
||||
|
||||
Then we define the array, giving it only the type.
|
||||
M*LIB will look at the global registered oplist of the type, we don't
|
||||
need to give it explictly.
|
||||
|
||||
Global registeration makes easier code.
|
||||
Registeration should be done when the type is defined.
|
||||
*/
|
||||
#define M_OPL_rock_me_in() M_A1_OPLIST
|
||||
|
||||
ARRAY_DEF(r_rockme2b, rock_me_in)
|
||||
|
||||
static int
|
||||
test_rockme2b(int n)
|
||||
{
|
||||
r_rockme2b_t array;
|
||||
|
||||
r_rockme2b_init(array);
|
||||
for(int i = 0; i < n; i++) {
|
||||
rock_me_in rock;
|
||||
rock->n = i*i - i;
|
||||
r_rockme2b_push_back(array, rock);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the struct.
|
||||
rock_me_in *p = r_rockme2b_get(array, i);
|
||||
s += (*p)->n;
|
||||
}
|
||||
|
||||
r_rockme2b_clear(array);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type defined as a tuple.
|
||||
|
||||
We first define a tuple. No need to specify the oplist as it uses basic C types
|
||||
so M_DEFAULT_OPLIST is used.
|
||||
|
||||
Then we define the array, giving it the oplist definition of the tuple:
|
||||
the macro TUPLE_OPLIST is used to build it based on the tuple name,
|
||||
and the oplists of the tuple.
|
||||
*/
|
||||
TUPLE_DEF2(rock_you, (n, int), (other, float))
|
||||
|
||||
ARRAY_DEF(r_rockme3, rock_you_t, TUPLE_OPLIST(rock_you, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST))
|
||||
|
||||
static int
|
||||
test_rockme3(int n)
|
||||
{
|
||||
r_rockme3_t array;
|
||||
rock_you_t x;
|
||||
|
||||
r_rockme3_init(array);
|
||||
rock_you_init(x);
|
||||
|
||||
for(int i = 0; i < n; i++) {
|
||||
rock_you_set_n(x, i*i - i);
|
||||
r_rockme3_push_back(array, x);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the structure containing the integer.
|
||||
rock_you_t *p = r_rockme3_get(array, i);
|
||||
s += (*p)->n;
|
||||
}
|
||||
|
||||
r_rockme3_clear(array);
|
||||
rock_you_clear(x);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Same with a structure type defined as a tuple using global registeration.
|
||||
|
||||
We first define a tuple. No need to specify the oplist as it uses basic C types
|
||||
so M_DEFAULT_OPLIST is used.
|
||||
|
||||
Then we register the oplist of rock_us_t by defining a macro based
|
||||
on M_OPL_ + type name + () which expands with the oplist of the type.
|
||||
|
||||
Then we define the array, giving it only the type.
|
||||
M*LIB will look at the global registered oplist of the type, we don't
|
||||
need to give it explictly.
|
||||
|
||||
Global registeration makes easier code.
|
||||
*/
|
||||
TUPLE_DEF2(rock_us, (n, int), (other, float))
|
||||
|
||||
#define M_OPL_rock_us_t() TUPLE_OPLIST(rock_us, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
ARRAY_DEF(r_rockme4, rock_us_t)
|
||||
|
||||
static int
|
||||
test_rockme4(int n)
|
||||
{
|
||||
r_rockme4_t array;
|
||||
rock_us_t x;
|
||||
|
||||
r_rockme4_init(array);
|
||||
rock_us_init(x);
|
||||
|
||||
for(int i = 0; i < n; i++) {
|
||||
rock_us_set_n(x, i*i - i);
|
||||
r_rockme4_push_back(array, x);
|
||||
}
|
||||
|
||||
int s = 0;
|
||||
for(int i = 0; i < n; i++) {
|
||||
// Get the pointer to the structure containing the integer.
|
||||
rock_us_t *p = r_rockme4_get(array, i);
|
||||
s += (*p)->n;
|
||||
}
|
||||
|
||||
r_rockme4_clear(array);
|
||||
rock_us_clear(x);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, const char *argv[])
|
||||
{
|
||||
int n = argc == 1 ? 10 : atoi(argv[1]);
|
||||
int s;
|
||||
|
||||
s = test_bool(n);
|
||||
printf ("S[bool] = %d\n", s);
|
||||
|
||||
s = test_char(n);
|
||||
printf ("S[char] = %d\n", s);
|
||||
|
||||
s = test_int(n);
|
||||
printf ("S[int] = %d\n", s);
|
||||
|
||||
s = test_int2(n);
|
||||
printf ("S[int2] = %d\n", s);
|
||||
|
||||
s = test_ullong(n);
|
||||
printf ("S[ullong] = %d\n", s);
|
||||
|
||||
s = test_float(n);
|
||||
printf ("S[float] = %d\n", s);
|
||||
|
||||
s = test_double(n);
|
||||
printf ("S[double] = %d\n", s);
|
||||
|
||||
s = test_cstring(n);
|
||||
printf ("S[cstring] = %d\n", s);
|
||||
|
||||
s = test_string(n);
|
||||
printf ("S[string] = %d\n", s);
|
||||
|
||||
s = test_vintptr(n);
|
||||
printf ("S[vintptr] = %d\n", s);
|
||||
|
||||
s = test_rockme(n);
|
||||
printf ("S[rockme] = %d\n", s);
|
||||
|
||||
s = test_rockme2(n);
|
||||
printf ("S[rockme2] = %d\n", s);
|
||||
|
||||
s = test_rockme2b(n);
|
||||
printf ("S[rockme2b] = %d\n", s);
|
||||
|
||||
s = test_rockme3(n);
|
||||
printf ("S[rockme3] = %d\n", s);
|
||||
|
||||
s = test_rockme3(n);
|
||||
printf ("S[rockme4] = %d\n", s);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
#include <stdlib.h>
|
||||
#include "m-array.h"
|
||||
|
||||
// Let's define an array of 'int' named 'int'
|
||||
// Let's define an array of 'int' named 'array_int'
|
||||
ARRAY_DEF(array_int, int)
|
||||
|
||||
const int n = 100000;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Declare and initilize the array
|
||||
// Declare and initialize the array
|
||||
array_int_t x;
|
||||
array_int_init(x);
|
||||
|
||||
|
||||
+13
-1
@@ -1,9 +1,13 @@
|
||||
#if HAVE_GMP
|
||||
|
||||
#include <gmp.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-algo.h"
|
||||
|
||||
/* Define an array of mpz_t */
|
||||
ARRAY_DEF(array_mpz, mpz_t, M_CLASSIC_OPLIST(mpz))
|
||||
/* Define some algorithm functions on array_mpz_t */
|
||||
ALGO_DEF(array_mpz, ARRAY_OPLIST(array_mpz, M_CLASSIC_OPLIST(mpz)))
|
||||
|
||||
static inline void my_mpz_inc(mpz_t *d, const mpz_t a){
|
||||
@@ -19,14 +23,22 @@ int main(void)
|
||||
mpz_init_set_ui(z, 1);
|
||||
array_mpz_t a;
|
||||
array_mpz_init(a);
|
||||
|
||||
array_mpz_push_back(a, z);
|
||||
for(size_t i = 2 ; i < 1000; i++) {
|
||||
mpz_mul_ui(z, z, i);
|
||||
array_mpz_push_back(a, z);
|
||||
}
|
||||
/* z = sum (a[i]^2) */
|
||||
|
||||
/* Compute z = sum (a[i]^2) */
|
||||
array_mpz_map_reduce (&z, a, my_mpz_inc, my_mpz_sqr);
|
||||
gmp_printf ("Z=%Zd\n", z);
|
||||
|
||||
array_mpz_clear(a);
|
||||
mpz_clear(z);
|
||||
}
|
||||
|
||||
#else
|
||||
// Nothing to do
|
||||
int main(void) { return 0; }
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
|
||||
/* This example show how to use complex structure with array
|
||||
embedding another library */
|
||||
|
||||
/* This is a typical trivial library with its interface */
|
||||
typedef struct lib_ext_struct {
|
||||
int id;
|
||||
// Other data may be added
|
||||
} lib_ext_struct;
|
||||
|
||||
/* Method to create a duplicate of the object.
|
||||
The new object is heap allocated */
|
||||
static lib_ext_struct *lib_ext_struct_Duplicate(const lib_ext_struct *obj)
|
||||
{
|
||||
lib_ext_struct *p = malloc(sizeof(lib_ext_struct));
|
||||
if (!p) abort();
|
||||
p->id = obj->id;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Method to delete any object */
|
||||
static void lib_ext_struct_Delete(lib_ext_struct *obj)
|
||||
{
|
||||
free(obj);
|
||||
}
|
||||
|
||||
/* This is the complex structure */
|
||||
typedef struct {
|
||||
uint32_t id;
|
||||
string_t type;
|
||||
lib_ext_struct* properties;
|
||||
} data_node;
|
||||
|
||||
/* This method shall initialize the object data_node to
|
||||
the init state.
|
||||
It takes as first argument the object to initialize,
|
||||
and since it takes by reference through a pointer,
|
||||
it is compatible with 'API_2' convention for oplist */
|
||||
static void data_node_init(data_node *obj)
|
||||
{
|
||||
obj->id = 0;
|
||||
string_init(obj->type);
|
||||
obj->properties = NULL;
|
||||
}
|
||||
|
||||
/* This method shall initialize the object data_node to
|
||||
the same state as the 'src' object (creating a copy, a duplicate) .
|
||||
It takes as first argument the object to initialize,
|
||||
and as second argument the source object.
|
||||
It takes both object by reference through pointers,
|
||||
as such it is compatible with 'API_6' convention for oplist */
|
||||
static void data_node_init_set(data_node *obj, const data_node *src)
|
||||
{
|
||||
obj->id = src->id;
|
||||
string_init_set(obj->type, src->type);
|
||||
if (src->properties)
|
||||
obj->properties = lib_ext_struct_Duplicate(src->properties);
|
||||
else
|
||||
obj->properties = NULL;
|
||||
}
|
||||
|
||||
/* This method shall set the object data_node to
|
||||
the same state as the 'src' object (creating a copy, a duplicate) .
|
||||
THe object was already initialized.
|
||||
It takes as first argument the object to initialize,
|
||||
and as second argument the source object.
|
||||
It takes both object by reference through pointers,
|
||||
as such it is compatible with 'API_6' convention for oplist */
|
||||
static void data_node_set(data_node *obj, const data_node *src)
|
||||
{
|
||||
obj->id = src->id;
|
||||
string_set(obj->type, src->type);
|
||||
if (obj->properties)
|
||||
lib_ext_struct_Delete(obj->properties);
|
||||
if (src->properties)
|
||||
obj->properties = lib_ext_struct_Duplicate(src->properties);
|
||||
else
|
||||
obj->properties = NULL;
|
||||
}
|
||||
|
||||
/* This method shall clear the object data_node to
|
||||
the cleared state (aka it shall free any resource).
|
||||
It takes as first argument the object to initialize,
|
||||
and since it takes by reference through a pointer,
|
||||
it is compatible with 'API_2' convention for oplist */
|
||||
static void data_node_clear(data_node *obj)
|
||||
{
|
||||
string_clear(obj->type);
|
||||
if (obj->properties)
|
||||
lib_ext_struct_Delete(obj->properties);
|
||||
}
|
||||
|
||||
/* Create a dynamic array of 'data_node' named array_data_node_t
|
||||
and gives the minimum oplist allowing M*LIB to handle
|
||||
properly the object 'data_node' with the API convention
|
||||
to respect for calling the methods.
|
||||
*/
|
||||
ARRAY_DEF(/* the array prefix */ array_data_node,
|
||||
/* the object type */ data_node,
|
||||
/* the object oplist */ (INIT(API_2(data_node_init)),
|
||||
SET(API_6(data_node_set)),
|
||||
INIT_SET(API_6(data_node_init_set)),
|
||||
CLEAR(API_2(data_node_clear))))
|
||||
|
||||
/* Define a global array with the created type */
|
||||
array_data_node_t global_array;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Call the created method */
|
||||
array_data_node_init(global_array);
|
||||
/* Do stuff... */
|
||||
array_data_node_clear(global_array);
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// -- An A* pathfinder inspired by the tutorial at Red Blob Games --
|
||||
// https://www.redblobgames.com/pathfinding/a-star/introduction.html
|
||||
// Code based on:
|
||||
// https://github.com/glouw/ctl/blob/master/examples/astar.c
|
||||
|
||||
#include <stdio.h>
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-prioqueue.h"
|
||||
#include "m-dict.h"
|
||||
|
||||
// Create a tuple of point, but don't use its priority field for comparison.
|
||||
TUPLE_DEF2( point, (x, int), (y, int), (priority, int, M_OPEXTEND( M_OPL_int(), CMP(0), HASH(0), EQUAL(0) ) ) )
|
||||
#define M_OPL_point_t() TUPLE_OPLIST(point, M_OPL_int(), M_OPL_int(), M_OPL_int())
|
||||
static inline int point_cmp_prio(const point_t a, const point_t b)
|
||||
{
|
||||
return a->priority < b->priority ? -1 : a->priority > b->priority;
|
||||
}
|
||||
PRIOQUEUE_DEF(prio_point, point_t, M_OPEXTEND(M_OPL_point_t(), CMP(point_cmp_prio) ) )
|
||||
#define M_OPL_prio_point_t() PRIOQUEUE_OPLIST(prio_point, M_OPL_point_t() )
|
||||
ARRAY_DEF(array_point, point_t)
|
||||
#define M_OPL_array_point_t() ARRAY_OPLIST(array_point, M_OPL_point_t() )
|
||||
DICT_DEF2(cost_point, point_t, int)
|
||||
#define M_OPL_cost_point_t() DICT_OPLIST(cost_point, M_OPL_point_t(), M_OPL_int())
|
||||
DICT_DEF2(step_point, point_t, point_t)
|
||||
#define M_OPL_step_point_t() DICT_OPLIST(step_point, M_OPL_point_t(), M_OPL_point_t())
|
||||
|
||||
static inline int point_index(const point_t p, int width)
|
||||
{
|
||||
return p->x + width * p->y;
|
||||
}
|
||||
|
||||
static inline void point_set_from(point_t p, const string_t maze, int width, const char pattern[])
|
||||
{
|
||||
size_t s = string_search_str(maze, pattern);
|
||||
point_emplace(p, s % width, s / width , 0);
|
||||
}
|
||||
|
||||
static void astar(array_point_t path, const string_t maze, int width)
|
||||
{
|
||||
M_LET(front, prio_point_t)
|
||||
M_LET(from, step_point_t)
|
||||
M_LET(costs, cost_point_t)
|
||||
M_LET(start, goal, current, next, point_t)
|
||||
{
|
||||
point_set_from(start, maze, width, "@");
|
||||
point_set_from(goal, maze, width, "!");
|
||||
cost_point_set_at(costs, start, 0);
|
||||
prio_point_push(front, start);
|
||||
while (!prio_point_empty_p(front)) {
|
||||
prio_point_pop(¤t, front);
|
||||
if (point_equal_p(current, goal))
|
||||
break;
|
||||
const struct { int x; int y; } deltas[] = {
|
||||
{ -1, +1 }, { 0, +1 }, { 1, +1 },
|
||||
{ -1, 0 }, /* ~ ~ */ { 1, 0 },
|
||||
{ -1, -1 }, { 0, -1 }, { 1, -1 },
|
||||
};
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
point_emplace(next, current->x + deltas[i].x, current->y + deltas[i].y, 0);
|
||||
if (string_get_char(maze, point_index(next, width)) != '#') {
|
||||
int *new_cost = cost_point_get(costs, current);
|
||||
int *cost = cost_point_get(costs, next);
|
||||
if (cost == NULL || *new_cost < *cost) {
|
||||
cost_point_set_at(costs, next, *new_cost);
|
||||
next->priority = *new_cost + abs(goal->x - next->x) + abs(goal->y - next->y);
|
||||
prio_point_push(front, next);
|
||||
step_point_set_at(from, next, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
point_set(current, goal);
|
||||
while (!point_equal_p(current, start)) {
|
||||
array_point_push_back(path, current);
|
||||
point_set(current, *step_point_get(from, current));
|
||||
}
|
||||
array_point_push_back(path, start);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
M_LET( (maze,
|
||||
("#########################################################################\n"
|
||||
"# # # # # # #\n"
|
||||
"# # ######### # ##### ######### ##### ##### ##### # ! #\n"
|
||||
"# # # # # # # # # #\n"
|
||||
"######### # ######### ######### ##### # # # ######### #\n"
|
||||
"# # # # # # # # # # #\n"
|
||||
"# # ############# # # ######### ##### # ######### # #\n"
|
||||
"# # # # # # # # # #\n"
|
||||
"# ############# ##### ##### # ##### ######### # ##### #\n"
|
||||
"# # # # # # # # # #\n"
|
||||
"# ##### ##### # ##### # ######### # # # #############\n"
|
||||
"# # # # # # # # # # # #\n"
|
||||
"############# # # # ######### # ##### # ##### ##### #\n"
|
||||
"# # # # # # # # # #\n"
|
||||
"# ##### # ######### ##### # ##### ##### ############# #\n"
|
||||
"# # # # # # # # # #\n"
|
||||
"# # ######### # ##### ######### # # ############# # #\n"
|
||||
"# # # # # # # # # # #\n"
|
||||
"# ######### # # # ##### ######### ######### # #########\n"
|
||||
"# # # # # # # # # #\n"
|
||||
"# @ # ##### ##### ##### ######### ##### # ######### # #\n"
|
||||
"# # # # # # #\n"
|
||||
"#########################################################################\n")), string_t)
|
||||
M_LET(path, array_point_t)
|
||||
{
|
||||
int width = string_search_str(maze, "\n") + 1;
|
||||
astar(path, maze, width);
|
||||
for M_EACH(it, path, array_point_t) {
|
||||
string_set_char(maze, point_index(*it, width), 'x');
|
||||
}
|
||||
printf("%s", string_get_cstr(maze));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "m-bptree.h"
|
||||
#include "m-string.h"
|
||||
|
||||
// Define a map int -> string named map_employee_id
|
||||
// The number 5 is the number of elements in a node of the B+TREE
|
||||
BPTREE_DEF2(map_employee_id, 5, int, string_t)
|
||||
// Register the oplist of this map globally.
|
||||
#define M_OPL_map_employee_id_t() BPTREE_OPLIST2(map_employee_id, M_DEFAULT_OPLIST, STRING_OPLIST)
|
||||
|
||||
// Define a map string -> int named map_employee_name
|
||||
// The number 5 is the number of elements in a node of the B+TREE
|
||||
BPTREE_DEF2(map_employee_name, 5, string_t, int)
|
||||
// Register the oplist of this map globally.
|
||||
#define M_OPL_map_employee_name_t() BPTREE_OPLIST2(map_employee_name, STRING_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
map_employee_id_t Employees;
|
||||
map_employee_id_init(Employees);
|
||||
|
||||
// Assign the map with some data
|
||||
map_employee_id_set_at(Employees, 17425, STRING_CTE("Mike"));
|
||||
map_employee_id_set_at(Employees, 7147, STRING_CTE("Tango"));
|
||||
map_employee_id_set_at(Employees, 841, STRING_CTE("Charlie"));
|
||||
map_employee_id_set_at(Employees, 11632, STRING_CTE("John Do"));
|
||||
map_employee_id_set_at(Employees, 852, STRING_CTE("Peter"));
|
||||
map_employee_id_set_at(Employees, 530, STRING_CTE("Amanda"));
|
||||
map_employee_id_set_at(Employees, 4258, STRING_CTE("Elisabeth"));
|
||||
|
||||
printf("Number of employees: %zu\n", map_employee_id_size(Employees));
|
||||
|
||||
// Iterate over the employee list
|
||||
printf("\nList of employees (sorted by id):\n");
|
||||
map_employee_id_it_t it;
|
||||
for(map_employee_id_it(it, Employees); !map_employee_id_end_p(it); map_employee_id_next(it)) {
|
||||
// Get a pointer to this employee from the iterator
|
||||
const map_employee_id_itref_t *e = map_employee_id_cref(it);
|
||||
// Print it
|
||||
printf ("%d: %s\n", *(e->key_ptr), string_get_cstr(*(e->value_ptr)));
|
||||
}
|
||||
|
||||
// Now, let's go using some macros!
|
||||
|
||||
// Let's create a reverse map
|
||||
M_LET(Employees_r, map_employee_name_t) {
|
||||
|
||||
// Let's fill in the reverse map with each employee
|
||||
// (using macro M_EACH)
|
||||
for M_EACH(e, Employees, map_employee_id_t) {
|
||||
map_employee_name_set_at(Employees_r, *(e->value_ptr), *(e->key_ptr));
|
||||
}
|
||||
|
||||
// Iterate over the employee list
|
||||
printf("\nList of employees (sorted by name):\n");
|
||||
for M_EACH(e, Employees_r, map_employee_name_t) {
|
||||
printf ("%d: %s\n", *(e->value_ptr), string_get_cstr(*(e->key_ptr)));
|
||||
}
|
||||
|
||||
// Print
|
||||
printf("\nElisabeth has id = %d\n", *map_employee_name_get(Employees_r, STRING_CTE("Elisabeth")));
|
||||
} /* Employees_r is cleared beyond this point */
|
||||
|
||||
map_employee_id_clear(Employees);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "m-core.h"
|
||||
|
||||
// Stubs defined for the example. Real code will use soundio library.
|
||||
struct SoundIo { int x; };
|
||||
struct SoundIoDevice { int x; };
|
||||
struct SoundIoOutStream { int x; };
|
||||
|
||||
static struct SoundIo *soundio_create(void) { return (struct SoundIo *) malloc(sizeof(struct SoundIo)); }
|
||||
static void soundio_destroy(struct SoundIo *s) { free(s); }
|
||||
|
||||
static struct SoundIoDevice *soundio_get_device(struct SoundIo *io, int def) { (void) io; (void) def; return (struct SoundIoDevice *) malloc(sizeof(struct SoundIoDevice)); }
|
||||
static void soundio_device_unref(struct SoundIoDevice *s) { free(s); }
|
||||
|
||||
static struct SoundIoOutStream *soundio_outstream_create(struct SoundIoDevice *device) { (void) device ; return (struct SoundIoOutStream *) malloc(sizeof(struct SoundIoOutStream)); }
|
||||
static void soundio_outstream_destroy(struct SoundIoOutStream *s) { free(s); }
|
||||
|
||||
static bool soundio_wait_events(struct SoundIo *s) { (void) s; return false; }
|
||||
// End of stubs
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int err = 1;
|
||||
|
||||
/* Example of using M_LET_IF macro to simplify writing error handling code.
|
||||
The following code creates some object, test if the object creation succeeds, register the destructor, and print an error if something went wrong
|
||||
*/
|
||||
M_LET_IF( struct SoundIo *soundio = soundio_create(), soundio != 0 , soundio_destroy(soundio) , fprintf(stderr, "out of memory for soundio\n") )
|
||||
M_LET_IF(struct SoundIoDevice *device = soundio_get_device(soundio, -1), device != 0, soundio_device_unref(device), fprintf(stderr, "out of memory for device\n"))
|
||||
M_LET_IF(struct SoundIoOutStream *outstream = soundio_outstream_create(device), outstream != 0, soundio_outstream_destroy(outstream), fprintf(stderr, "out of memory for stream\n")) {
|
||||
err = 0;
|
||||
bool cont = true;
|
||||
while (cont)
|
||||
cont = soundio_wait_events(soundio);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
#include "m-string.h"
|
||||
|
||||
// Define a dictionnary of string --> int
|
||||
// named 'str'
|
||||
// named 'dict_str'
|
||||
DICT_DEF2(dict_str, string_t, STRING_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
|
||||
int main(void)
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ int main(void) {
|
||||
char *k = dict_m32_get(h, 10); // No 10 in h, so k == NULL
|
||||
int is_missing = (k != NULL); // true
|
||||
assert (is_missing);
|
||||
dict_m32_remove(h, 5); // h is now empty
|
||||
dict_m32_erase(h, 5); // h is now empty
|
||||
dict_m32_it_t it; // iterate over all dictionnary
|
||||
for (dict_m32_it (it, h) ; !dict_m32_end_p (it); dict_m32_next(it)) {
|
||||
struct dict_m32_pair_s *item = dict_m32_ref(it);
|
||||
@@ -33,7 +33,7 @@ int main(void) {
|
||||
char *k = dict_m32_get(h, 10); // k == NULL
|
||||
int is_missing = (k != NULL); // true
|
||||
assert (is_missing);
|
||||
dict_m32_remove(h, 5); // h is now empty
|
||||
dict_m32_erase(h, 5); // h is now empty
|
||||
for M_EACH(item, h, M32_OPLIST) { // traverse each item
|
||||
item->value = 1; // Set its value to 1
|
||||
}
|
||||
|
||||
+7
-6
@@ -5,8 +5,9 @@
|
||||
#include "m-dict.h"
|
||||
#include "m-string.h"
|
||||
|
||||
DICT_DEF2(dict_map, char, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
|
||||
// Define a dictionary (hashmap) from 'char' to 'int'
|
||||
DICT_DEF2(dict_map, char, int)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
dict_map_t mp;
|
||||
@@ -50,11 +51,11 @@ int main(void)
|
||||
printf("Enter the mapped string to be deleted: ");
|
||||
string_fgets (str, stdin, STRING_READ_PURE_LINE);
|
||||
s = atoi(string_get_cstr(str));
|
||||
dict_map_remove(mp, s);
|
||||
dict_map_erase(mp, s);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
printf("Size of Map: %lu\n", (unsigned long) dict_map_size(mp) );
|
||||
printf("Size of Map: %zu\n", dict_map_size(mp) );
|
||||
break;
|
||||
|
||||
case 4:
|
||||
@@ -81,7 +82,7 @@ int main(void)
|
||||
}
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 6:
|
||||
dict_map_clear(mp);
|
||||
string_clear (str);
|
||||
exit(0);
|
||||
@@ -89,7 +90,7 @@ int main(void)
|
||||
|
||||
default:
|
||||
printf("Wrong Choice\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-5
@@ -5,14 +5,23 @@
|
||||
/* program that reads text from standard-input,
|
||||
and prints the total number of distinct words found in the text.
|
||||
*/
|
||||
DICT_SET_DEF2(dict_str, string_t, STRING_OPLIST)
|
||||
|
||||
/* Definition of a set (hashset) of unique string_t */
|
||||
DICT_SET_DEF(dict_str, string_t, STRING_OPLIST)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Define word as a string_t
|
||||
M_LET(word, STRING_OPLIST)
|
||||
M_LET(wordcount, DICT_SET_OPLIST(dict_str, STRING_OPLIST)) {
|
||||
while (string_fget_word(word, " \t\n,.!;:?", stdin))
|
||||
dict_str_set_at(wordcount, word);
|
||||
printf ("Words: %lu\n", dict_str_size(wordcount));
|
||||
// Define wordcount as a set of string_t
|
||||
M_LET(wordcount, DICT_SET_OPLIST(dict_str, STRING_OPLIST)) {
|
||||
// Read a word from the standard input
|
||||
while (string_fget_word(word, " \t\n,.!;:?", stdin)) {
|
||||
// Push this word in the set.
|
||||
// Do nothing if it already exists.
|
||||
dict_str_push(wordcount, word);
|
||||
}
|
||||
// Print the number of words of the standard input.
|
||||
printf ("Words: %zu\n", dict_str_size(wordcount));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <stdio.h>
|
||||
#include "m-string.h"
|
||||
#include "m-array.h"
|
||||
#include "m-dict.h"
|
||||
|
||||
// Define an array of size_t
|
||||
ARRAY_DEF(array_size, size_t)
|
||||
// Register the oplist of this array globaly
|
||||
#define M_OPL_array_size_t() ARRAY_OPLIST(array_size)
|
||||
|
||||
// Define a dictionary (hashmap) of string_t to array of size_t
|
||||
DICT_DEF2(map_pos, string_t, array_size_t)
|
||||
// Register the oplist of this dictionary (hashmap)
|
||||
#define M_OPL_map_pos_t() DICT_OPLIST(map_pos, STRING_OPLIST, ARRAY_OPLIST(array_size))
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf("USAGE: %s filename\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Define word as a string_t
|
||||
M_LET(word, string_t)
|
||||
// Define positions as a map of string_t-->array of size_t
|
||||
M_LET(positions, map_pos_t) {
|
||||
// Open the file
|
||||
FILE *f = fopen(argv[1], "rt");
|
||||
if (!f) {
|
||||
fprintf(stderr, "ERROR: Cannot open %s.\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// While it reads some word from the file
|
||||
while (string_fget_word(word, " \t\n\r\"(),=", f)) {
|
||||
// Get the reference to the array of this word in the dictionnary (or create it if needed)
|
||||
array_size_t *array = map_pos_safe_get(positions, word);
|
||||
// Push the new offset to this word in the associated array
|
||||
array_size_push_back(*array, (size_t)ftell(f) - string_size(word));
|
||||
}
|
||||
|
||||
// Close the file
|
||||
fclose(f);
|
||||
|
||||
// Print the words and where they are:
|
||||
for M_EACH(pair, positions, map_pos_t) {
|
||||
string_fputs(stdout, pair->key);
|
||||
for M_EACH(pos, pair->value, array_size_t) {
|
||||
printf(" %zu", *pos);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
} // All created objects are destroyed beyond this point
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-string.h"
|
||||
#include "m-algo.h"
|
||||
|
||||
/* Based on:
|
||||
* Easy Perfect Minimal Hashing
|
||||
* By Steve Hanov. Released to the public domain.
|
||||
* http://stevehanov.ca/blog/index.php?id=119
|
||||
*
|
||||
* Based on:
|
||||
* Edward A. Fox, Lenwood S. Heath, Qi Fan Chen and Amjad M. Daoud,
|
||||
* "Practical minimal perfect hash functions for large databases", CACM, 35(1):105-121
|
||||
*
|
||||
* also a good reference:
|
||||
* Compress, Hash, and Displace algorithm by Djamal Belazzougui,
|
||||
* Fabiano C. Botelho, and Martin Dietzfelbinger
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Define the needed data structure : */
|
||||
|
||||
// Define a dictionnary of constant string --> integer and register it.
|
||||
DICT_DEF2(dict_mph, const char *, M_CSTR_OPLIST, uint32_t, M_DEFAULT_OPLIST)
|
||||
#define M_OPL_dict_mph_t() DICT_OPLIST(dict_mph, M_CSTR_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
// Define an array of integer to store the value and register it.
|
||||
ARRAY_DEF(array_value, uint32_t) // Start from 1. 0 is 'None'
|
||||
#define M_OPL_array_value_t() ARRAY_OPLIST(array_value)
|
||||
|
||||
// Define an array of integer to store the seed and register it.
|
||||
ARRAY_DEF(array_seed, int32_t)
|
||||
#define M_OPL_array_seed_t() ARRAY_OPLIST(array_seed)
|
||||
// Define algorithms on array_seed
|
||||
ALGO_DEF(array_seed, array_seed_t)
|
||||
|
||||
// Define an array of constant string that are ordered by their size.
|
||||
ARRAY_DEF(array_cstr, const char *, M_CSTR_OPLIST)
|
||||
// Register it globally but with a custom CMP operator
|
||||
static inline int array_cstr_cmp(const array_cstr_t a, const array_cstr_t b) {
|
||||
// We compare only the size
|
||||
size_t sa = array_cstr_size(a);
|
||||
size_t sb = array_cstr_size(b);
|
||||
return (sa > sb) ? -1 : (sa < sb);
|
||||
}
|
||||
#define M_OPL_array_cstr_t() M_OPEXTEND(ARRAY_OPLIST(array_cstr, M_CSTR_OPLIST), CMP(array_cstr_cmp))
|
||||
|
||||
// Define an array of array of constant string, and some algo over this container
|
||||
ARRAY_DEF(array_bucket, array_cstr_t)
|
||||
#define M_OPL_array_bucket_t() ARRAY_OPLIST(array_bucket, M_OPL_array_cstr_t() )
|
||||
ALGO_DEF(array_bucket, array_bucket_t)
|
||||
|
||||
// Define an array of string to save the input.
|
||||
ARRAY_DEF(array_string, string_t)
|
||||
#define M_OPL_array_string_t() ARRAY_OPLIST(array_string, M_OPL_string_t())
|
||||
|
||||
|
||||
|
||||
|
||||
/* The algorithm may not converge on all inputs if the hash function
|
||||
* doesn't randomly distribute the inputs:
|
||||
* str1 != str2 ==> \exist seed \such_as hash(seed,str1) != hash(seed, str2)
|
||||
* The first hash function below doesn't provide the needed property for
|
||||
* inputs with two words 'a' and 'c'
|
||||
* The second one is more robust but may still have this issue.
|
||||
*/
|
||||
static uint32_t hash(int32_t seed, const char str[])
|
||||
{
|
||||
#if 0
|
||||
if (seed == 0) seed = 0x01000193;
|
||||
while (*str) {
|
||||
seed = ((seed * 0x01000193) ^ *str);
|
||||
str++;
|
||||
}
|
||||
return seed;
|
||||
#else
|
||||
if (seed == 0) seed = 0x811C9DC5;
|
||||
while (*str) {
|
||||
seed = (seed ^ *str) * 16777619;
|
||||
str++;
|
||||
}
|
||||
return seed ^ (seed >> 16);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Step 1: place all key keys into buckets
|
||||
static void
|
||||
CreateMinimalPerfectHash1(array_seed_t seed, array_value_t value,
|
||||
array_bucket_t bucket,
|
||||
const dict_mph_t dict)
|
||||
{
|
||||
size_t size = dict_mph_size (dict);
|
||||
|
||||
array_value_resize(value, size);
|
||||
array_seed_resize(seed, size);
|
||||
array_bucket_resize(bucket, size);
|
||||
|
||||
for M_EACH(item, dict, dict_mph_t) {
|
||||
size_t i = hash(0, item->key) % size;
|
||||
array_cstr_push_back (*array_bucket_get (bucket, i), item->key);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Sort the buckets and process the ones with the most items first.
|
||||
static void
|
||||
CreateMinimalPerfectHash2(array_seed_t seed, array_value_t value,
|
||||
array_bucket_t bucket,
|
||||
const dict_mph_t dict)
|
||||
{
|
||||
size_t size = dict_mph_size (dict);
|
||||
array_bucket_sort(bucket);
|
||||
M_LET(slots, array_seed_t) {
|
||||
for M_EACH(b, bucket, array_bucket_t) {
|
||||
if (array_cstr_size(*b) <= 1)
|
||||
break;
|
||||
size_t d = 1;
|
||||
/* Repeatedly try different values of d until we find a hash function
|
||||
that places all items in the bucket into free slots.
|
||||
This may loop forever on some inputs if the hash function is not good
|
||||
enought. */
|
||||
for(bool cont = true; cont ; ) {
|
||||
cont = false;
|
||||
array_seed_reset(slots);
|
||||
for M_EACH(it, *b, array_cstr_t) {
|
||||
size_t s = hash(d, *it) % size;
|
||||
if (*array_value_get(value, s) != 0 || array_seed_contain_p(slots, s)){
|
||||
d++;
|
||||
cont = true;
|
||||
break;
|
||||
}
|
||||
array_seed_push_back (slots, s);
|
||||
}
|
||||
}
|
||||
// Fill in the seed & value with the computed value
|
||||
array_seed_set_at(seed, hash(0, *array_cstr_get(*b, 0)) % size, d);
|
||||
d = 0;
|
||||
for M_EACH(it, *b, array_cstr_t) {
|
||||
array_value_set_at(value,
|
||||
*array_seed_get(slots, d),
|
||||
*dict_mph_get(dict, *it));
|
||||
d++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 3:
|
||||
* Only buckets with 1 item remain. Process them more quickly by directly
|
||||
* placing them into a free slot. Use a negative value of d to indicate
|
||||
* this.
|
||||
*/
|
||||
static void
|
||||
CreateMinimalPerfectHash3(array_seed_t seed, array_value_t value,
|
||||
array_bucket_t bucket,
|
||||
const dict_mph_t dict)
|
||||
{
|
||||
size_t size = dict_mph_size (dict);
|
||||
|
||||
M_LET(freelist, array_seed_t) {
|
||||
// Get a list of available slot
|
||||
for(size_t i = 0; i < size; i++)
|
||||
if (*array_value_get(value, i) == 0)
|
||||
array_seed_push_back(freelist, i);
|
||||
for M_EACH(b, bucket, array_bucket_t) {
|
||||
if (array_cstr_size(*b) != 1)
|
||||
continue;
|
||||
int32_t slot;
|
||||
array_seed_pop_back(&slot, freelist);
|
||||
/*We subtract one to ensure it's negative even if the zeroeth slot was
|
||||
used. */
|
||||
array_seed_set_at (seed, hash(0, *array_cstr_get(*b, 0)) % size,
|
||||
-slot-1);
|
||||
array_value_set_at(value, slot, *dict_mph_get(dict, *array_cstr_get(*b, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
CreateMinimalPerfectHash(array_seed_t seed, array_value_t value,
|
||||
const dict_mph_t dict)
|
||||
{
|
||||
M_LET(bucket, array_bucket_t) {
|
||||
CreateMinimalPerfectHash1(seed, value, bucket, dict);
|
||||
CreateMinimalPerfectHash2(seed, value, bucket, dict);
|
||||
CreateMinimalPerfectHash3(seed, value, bucket, dict);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dict_read_from_file(dict_mph_t dict, array_string_t arr, const char filename[])
|
||||
{
|
||||
FILE *f = fopen(filename, "rt");
|
||||
if (!f) {
|
||||
fprintf(stderr, "ERROR: Cannot open %s\n", filename);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
uint32_t line = 1;
|
||||
M_LET(str, string_t) {
|
||||
while (!feof(f) && !ferror(f)) {
|
||||
string_fgets(str, f, STRING_READ_PURE_LINE);
|
||||
string_strim(str);
|
||||
if (!string_empty_p(str)) {
|
||||
// Not clean as items of 'dict' points are linked to items of 'arr'
|
||||
// and the containers are not aware of this link.
|
||||
array_string_push_back (arr, str);
|
||||
dict_mph_set_at(dict, string_get_cstr(*array_string_back(arr)), line);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
perfect_hash_lockup(array_seed_t seed, array_value_t value,
|
||||
const char key[])
|
||||
{
|
||||
int32_t s = *array_seed_get(seed, hash(0, key) % array_seed_size(seed));
|
||||
if (s < 0)
|
||||
return *array_value_get(value, -s-1);
|
||||
else
|
||||
return *array_value_get(value, hash(s, key) % array_value_size(value));
|
||||
}
|
||||
|
||||
static void
|
||||
test(array_seed_t seed, array_value_t value, dict_mph_t dict)
|
||||
{
|
||||
for M_EACH(item, dict, dict_mph_t) {
|
||||
const char *key = item->key;
|
||||
uint32_t v = perfect_hash_lockup(seed, value, key);
|
||||
if (v != item->value) {
|
||||
fprintf(stderr, "ERROR for %s: %u VS %u\n", key, v, item->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
// Create arr as array_string_t, dict as dist_mph_t and seed as array_seed_t
|
||||
M_LET(arr, array_string_t) M_LET(dict, dict_mph_t) M_LET(seed, array_seed_t)
|
||||
// and value as array_value_t
|
||||
M_LET(value, array_value_t) {
|
||||
const char *filename = (argc > 1) ? argv[1] :"/usr/share/dict/words";
|
||||
dict_read_from_file(dict, arr, filename);
|
||||
CreateMinimalPerfectHash(seed, value, dict);
|
||||
test(seed, value, dict);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
|
||||
// Define an array of integer
|
||||
ARRAY_DEF(array_int, int)
|
||||
|
||||
typedef struct {
|
||||
@@ -25,7 +26,7 @@ static void tape_devance(tape_t tape) { if (tape->pos > 0) tape->pos--; }
|
||||
|
||||
typedef struct {
|
||||
string_t code;
|
||||
array_int_t bracket_map; // Should use map when possible.
|
||||
array_int_t bracket_map;
|
||||
} program_t[1];
|
||||
|
||||
static void program_init(program_t p, string_t text) {
|
||||
@@ -47,8 +48,8 @@ static void program_init(program_t p, string_t text) {
|
||||
int left;
|
||||
array_int_pop_back(&left, leftstack);
|
||||
int right = pc;
|
||||
array_int_set_at2 (p->bracket_map, left, right);
|
||||
array_int_set_at2 (p->bracket_map, right, left);
|
||||
*array_int_safe_get (p->bracket_map, left) = right;
|
||||
*array_int_safe_get (p->bracket_map, right) = left;
|
||||
}
|
||||
|
||||
pc++;
|
||||
@@ -98,7 +99,11 @@ static void program_run(program_t p){
|
||||
|
||||
static void read_file(string_t str, const string_t filename) {
|
||||
FILE *f = fopen(string_get_cstr(filename), "rt");
|
||||
if (!f) abort();
|
||||
if (!f) {
|
||||
fprintf(stderr, "ERROR: Cannot open %s\n", string_get_cstr(filename));
|
||||
exit(2);
|
||||
}
|
||||
// Read the full FILE in the string
|
||||
string_fgets (str, f, STRING_READ_FILE);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#if HAVE_GMP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gmp.h>
|
||||
|
||||
@@ -50,14 +52,19 @@ int main(void)
|
||||
// Dictionary iterator are pair (key,value)
|
||||
if (my_integer_n_p(p->value)) {
|
||||
printf ("It is a long long, value = %lld\n",
|
||||
*my_integer_get_n(p->value));
|
||||
*my_integer_cget_n(p->value));
|
||||
}
|
||||
if (my_integer_z_p(p->value)) {
|
||||
printf ("It is a mpz_t, value =");
|
||||
mpz_out_str(stdout, 10, *my_integer_get_z(p->value));
|
||||
mpz_out_str(stdout, 10, *my_integer_cget_z(p->value));
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* All variables are automatically cleared beyond this point */
|
||||
}
|
||||
|
||||
#else
|
||||
// Nothing to do
|
||||
int main(void) { return 0; }
|
||||
#endif
|
||||
@@ -4,7 +4,7 @@
|
||||
/* Register a dynamic seed to M*LIB hash function.
|
||||
This shall be done before any M*LIB header inclusion. */
|
||||
unsigned long long rand_seed;
|
||||
#define M_HASH_SEED rand_seed
|
||||
#define M_USE_HASH_SEED rand_seed
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-dict.h"
|
||||
@@ -12,7 +12,9 @@ unsigned long long rand_seed;
|
||||
#include "m-variant.h"
|
||||
|
||||
/* This example will show how to create JSON-like files.
|
||||
This is a JSON **like** syntax, not exactly JSON format */
|
||||
This is a JSON **like** syntax, not exactly JSON format.
|
||||
For an example with JSON format, see https://github.com/P-p-H-d/mjsonlib
|
||||
*/
|
||||
|
||||
/* First let's handle the OUT/IN functions of boolean */
|
||||
static inline void boolean_out_str(FILE *f, bool b)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
#include "m-list.h"
|
||||
#include "m-buffer.h"
|
||||
|
||||
/* This example chains the structure in different ways in
|
||||
order to show/test the good chaining of the structures */
|
||||
|
||||
TUPLE_DEF2(symbol, (type, int), (name, string_t))
|
||||
#define M_OPL_symbol_t() TUPLE_OPLIST(symbol, M_DEFAULT_OPLIST, STRING_OPLIST)
|
||||
|
||||
ARRAY_DEF(symbol_list, symbol_t)
|
||||
#define M_OPL_symbol_list_t() ARRAY_OPLIST(symbol_list, M_OPL_symbol_t())
|
||||
|
||||
TUPLE_DEF2(record, (fields, symbol_list_t))
|
||||
#define M_OPL_record_t() TUPLE_OPLIST(record, M_OPL_symbol_list_t())
|
||||
|
||||
TUPLE_DEF2(my_tuple, (name, string_t), (value, int))
|
||||
#define M_OPL_my_tuple_t() TUPLE_OPLIST(my_tuple)
|
||||
|
||||
ARRAY_DEF(my_list_of_tuple, my_tuple_t)
|
||||
#define M_OPL_my_list_of_tuple_t() ARRAY_OPLIST(my_list_of_tuple)
|
||||
|
||||
TUPLE_DEF2(my_tuple_of_tuple, (name, string_t), (inner_tuple, my_tuple_t))
|
||||
#define M_OPL_my_tuple_of_tuple_t() TUPLE_OPLIST(my_tuple_of_tuple)
|
||||
|
||||
LIST_DEF(list_of_tuple_of_tuple, my_tuple_of_tuple_t)
|
||||
#define M_OPL_list_of_tuple_of_tuple_t() LIST_OPLIST(list_of_tuple_of_tuple, M_OPL_my_tuple_of_tuple_t() )
|
||||
|
||||
TUPLE_DEF2(my_triple_tuple, (name, string_t), (inner_tuple, my_tuple_t), (list, list_of_tuple_of_tuple_t) )
|
||||
#define M_OPL_my_triple_tuple_t() TUPLE_OPLIST(my_triple_tuple, STRING_OPLIST, M_OPL_my_tuple_t(), M_OPL_list_of_tuple_of_tuple_t())
|
||||
|
||||
BUFFER_DEF(trbuff, my_triple_tuple_t, 100, BUFFER_QUEUE)
|
||||
#define M_OPL_trbuff_t() BUFFER_OPLIST(trbuff, M_OPL_my_triple_tuple_t())
|
||||
|
||||
TUPLE_DEF2(my_junction, (active, bool), (buffer, trbuff_t))
|
||||
#define M_OPL_my_junction_t() TUPLE_OPLIST(my_junction, M_DEFAULT_OPLIST, M_OPL_trbuff_t())
|
||||
|
||||
TUPLE_DEF2(mlib_point, (x, int), (y, int))
|
||||
#define M_OPL_mlib_point_t() \
|
||||
TUPLE_OPLIST(mlib_point, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
TUPLE_DEF2(mlib_points, (first_ref, mlib_point_ptr, M_PTR_OPLIST),
|
||||
(second_ref, mlib_point_ptr, M_PTR_OPLIST),
|
||||
(third, mlib_point_t))
|
||||
#define M_OPL_mlib_points_t() \
|
||||
TUPLE_OPLIST(mlib_points, M_PTR_OPLIST, M_PTR_OPLIST, M_OPL_mlib_point_t())
|
||||
|
||||
static void let2(void)
|
||||
{
|
||||
// Initialize p1 as mlib_point_t with (4,4)
|
||||
// and p2 as mlib_point_t with (5,5)
|
||||
// and pair as mlib_point_t with default value
|
||||
M_LET((p1, 4, 4), (p2, 5, 5), mlib_point_t) {
|
||||
M_LET(pair, mlib_points_t) {
|
||||
pair->first_ref = p1;
|
||||
pair->second_ref = p2;
|
||||
pair->third->x = pair->third->y = 6;
|
||||
|
||||
assert(pair->first_ref->x == 4);
|
||||
p1->x = 10;
|
||||
assert(pair->first_ref->x == 10);
|
||||
|
||||
assert(pair->third->x == 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void let(void)
|
||||
{
|
||||
// Let's initialize all the declared types
|
||||
M_LET(s, symbol_t)
|
||||
M_LET(l, symbol_list_t)
|
||||
M_LET(r,record_t)
|
||||
M_LET(tup, my_tuple_t)
|
||||
M_LET(l2, my_list_of_tuple_t)
|
||||
M_LET(tuptup, my_tuple_of_tuple_t)
|
||||
M_LET(ltuptup, list_of_tuple_of_tuple_t)
|
||||
M_LET(tuptuptup, my_triple_tuple_t)
|
||||
M_LET(junc, my_junction_t)
|
||||
{
|
||||
printf("Init ok\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
let();
|
||||
let2();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
|
||||
// Let's create an array of string_t and register it globaly
|
||||
ARRAY_DEF(vector_string, string_t)
|
||||
#define M_OPL_vector_string_t() ARRAY_OPLIST(vector_string, STRING_OPLIST)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Let's define a new string named str
|
||||
M_LET(str, string_t)
|
||||
// Let's define msg as a vector_array_t
|
||||
M_LET( (msg, STRING_CTE("Hello"), STRING_CTE("C"), STRING_CTE("World!"), STRING_CTE("Let's"), STRING_CTE("discover"), STRING_CTE("M*LIB!")), vector_string_t) {
|
||||
// Iterate over all words of the array msg
|
||||
for M_EACH(word, msg, vector_string_t) {
|
||||
// Cat the current word into the target string
|
||||
string_cat(str, *word);
|
||||
// Also add a space character
|
||||
string_push_back(str, ' ');
|
||||
}
|
||||
// Let's define another string and set it to a formatted value.
|
||||
M_LET( (count, "\nTo display this it uses \"vector\" of %zu strings!", vector_string_size(msg)), string_t) {
|
||||
string_cat(str, count);
|
||||
}
|
||||
// Print the result
|
||||
printf("%s\n", string_get_cstr(str) );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+10
-8
@@ -4,6 +4,15 @@
|
||||
|
||||
#include "m-rbtree.h"
|
||||
|
||||
// Let's define one utility function.
|
||||
void *xmalloc(size_t n);
|
||||
void *xmalloc(size_t n)
|
||||
{
|
||||
void *p = malloc (n);
|
||||
if (!p) abort();
|
||||
return p;
|
||||
}
|
||||
|
||||
// Let's define a new type.
|
||||
typedef struct {
|
||||
int key;
|
||||
@@ -12,7 +21,7 @@ typedef struct {
|
||||
|
||||
#define SIZE 256
|
||||
|
||||
void *xmalloc(size_t n);
|
||||
// Let's define the prototypes of the methods of this new type.
|
||||
void my_type_init(my_type_t x);
|
||||
void my_type_clear(my_type_t x);
|
||||
void my_type_init_set(my_type_t x, const my_type_t y);
|
||||
@@ -24,13 +33,6 @@ void my_type_out_str(FILE *f, const my_type_t x);
|
||||
bool my_type_in_str(my_type_t x, FILE *f);
|
||||
void my_type_rand(my_type_t x);
|
||||
|
||||
void *xmalloc(size_t n)
|
||||
{
|
||||
void *p = malloc (n);
|
||||
if (!p) abort();
|
||||
return p;
|
||||
}
|
||||
|
||||
// Let's define its constructor
|
||||
void my_type_init(my_type_t x)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "m-string.h"
|
||||
|
||||
#define MAX_COLUMN 80
|
||||
|
||||
static void format(FILE *f)
|
||||
{
|
||||
string_t s;
|
||||
string_init(s);
|
||||
while (string_fgets(s, f, STRING_READ_PURE_LINE) == true)
|
||||
{
|
||||
size_t n = string_size(s);
|
||||
if (n > 0 && string_get_char(s, n-1) == '\\' && n < MAX_COLUMN) {
|
||||
string_left(s, n-1);
|
||||
while (n < MAX_COLUMN-1) {
|
||||
string_push_back(s, ' ');
|
||||
n++;
|
||||
}
|
||||
string_push_back(s, '\\');
|
||||
}
|
||||
if (n >= MAX_COLUMN && string_get_char(s, n-1) == '\\') {
|
||||
n--;
|
||||
string_left(s, n);
|
||||
while (n >= MAX_COLUMN-2 && string_get_char(s, n-1) == ' ') {
|
||||
n--;
|
||||
string_left(s, n);
|
||||
}
|
||||
string_push_back(s, ' ');
|
||||
string_push_back(s, '\\');
|
||||
}
|
||||
printf("%s\n", string_get_cstr(s));
|
||||
}
|
||||
string_clear(s);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Missing argument filename.\nUSAGE: %s filename.h\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
FILE *f = fopen(argv[1], "rt");
|
||||
if (!f) {
|
||||
fprintf(stderr, "ERROR: Cannot open filename '%s' .h\n", argv[1]);
|
||||
exit(2);
|
||||
}
|
||||
format(f);
|
||||
fclose(f);
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#include "m-array.h"
|
||||
#include "m-string.h"
|
||||
|
||||
// Define an array of string_t. Since the oplist of string_t is registered globaly
|
||||
// there is no need to give it to this definition.
|
||||
ARRAY_DEF(str_array, string_t)
|
||||
|
||||
// First the verbose way and (nearly) macro free:
|
||||
static void main_macrofree(void)
|
||||
{
|
||||
str_array_t tab_name;
|
||||
str_array_init(tab_name);
|
||||
|
||||
// Push some elements in the array (long way)
|
||||
str_array_push_back(tab_name, STRING_CTE("My"));
|
||||
str_array_push_back(tab_name, STRING_CTE("CD"));
|
||||
str_array_push_back(tab_name, STRING_CTE("IS"));
|
||||
str_array_push_back(tab_name, STRING_CTE("OUT"));
|
||||
|
||||
// Overwrite element 1 of the array.
|
||||
str_array_set_at(tab_name, 1, STRING_CTE("DVD"));
|
||||
|
||||
// Finaly rewrite 1 element of the array
|
||||
string_set_str(*str_array_get(tab_name, 1), "BLU-RAY");
|
||||
|
||||
// Format some strings and push them back in the array
|
||||
string_t format;
|
||||
string_init_printf(format, "There are %d elements", str_array_size(tab_name));
|
||||
str_array_push_back(tab_name, format);
|
||||
string_printf(format, "There is a capacty of %d", str_array_capacity(tab_name));
|
||||
string_replace_all_str(format, "capacty", "capacity");
|
||||
str_array_push_back(tab_name, format);
|
||||
string_printf(format, "The third element is '%s'", string_get_cstr(*str_array_get(tab_name, 2)));
|
||||
str_array_push_back(tab_name, format);
|
||||
string_clear(format);
|
||||
|
||||
// Display the content of the array.
|
||||
str_array_it_t it;
|
||||
int i = 0;
|
||||
for (str_array_it(it, tab_name) ; !str_array_end_p(it); str_array_next(it)) {
|
||||
printf("item[%d] = '%s'\n", i, string_get_cstr(*str_array_cref(it)) );
|
||||
i++;
|
||||
}
|
||||
|
||||
// Clear the array
|
||||
str_array_clear(tab_name);
|
||||
}
|
||||
|
||||
// Then the short version with macros.
|
||||
// For this we need to register the oplist of the array of string globally.
|
||||
#define M_OPL_str_array_t() ARRAY_OPLIST(str_array, STRING_OPLIST)
|
||||
|
||||
static void main_macro(void)
|
||||
{
|
||||
// Define and fill an array of string with some elements in it:
|
||||
M_LET( (tab_name, STRING_CTE("My"), STRING_CTE("CD"), STRING_CTE("IS"), STRING_CTE("OUT")), str_array_t) {
|
||||
// Overwrite element 1 of the array.
|
||||
str_array_set_at(tab_name, 1, STRING_CTE("DVD"));
|
||||
|
||||
// Finaly rewrite 1 element of the array
|
||||
string_set_str(*str_array_get(tab_name, 1), "BLU-RAY");
|
||||
|
||||
// Format some strings and push them back in the array
|
||||
M_LET(format, string_t) {
|
||||
string_printf(format, "There are %d elements", str_array_size(tab_name));
|
||||
str_array_push_back(tab_name, format);
|
||||
string_printf(format, "There is a capacty of %d", str_array_capacity(tab_name));
|
||||
string_replace_all_str(format, "capacty", "capacity");
|
||||
str_array_push_back(tab_name, format);
|
||||
string_printf(format, "The third element is '%s'", string_get_cstr(*str_array_get(tab_name, 2)));
|
||||
str_array_push_back(tab_name, format);
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
// Set format to a formated string
|
||||
// In C11, we can mix string_t and char *.
|
||||
// We can also use M_CSTR to create a printf formated string.
|
||||
string_sets(format, "FILE=", getenv("PWD"), "/", *str_array_get(tab_name, 2), M_CSTR("-%zu.txt", str_array_size(tab_name) ));
|
||||
str_array_push_back(tab_name, format);
|
||||
#endif
|
||||
} // beyond this point format is cleared
|
||||
|
||||
// Display the content of the array.
|
||||
int i = 0;
|
||||
for M_EACH(item, tab_name, str_array_t) {
|
||||
printf("item[%d] = '%s'\n", i, string_get_cstr(*item) );
|
||||
i++;
|
||||
}
|
||||
} // beyond this point tab_name is cleared
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("Version macrofree:\n");
|
||||
main_macrofree();
|
||||
printf("Version macro:\n");
|
||||
main_macro();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <stdio.h>
|
||||
#include "m-string.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-array.h"
|
||||
|
||||
/* Definition of an associative map string_t --> size_t */
|
||||
DICT_DEF2(dict_str, string_t, size_t)
|
||||
#define M_OPL_dict_str_t() DICT_OPLIST(dict_str, STRING_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
/* Definition of an array of string_t */
|
||||
ARRAY_DEF(vector_str, string_t)
|
||||
#define M_OPL_vector_str_t() ARRAY_OPLIST(vector_str, STRING_OPLIST)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Construct an array of string, performing a convertion of the C const char *
|
||||
// into a proper string_t at real time and push then into a dynamic array
|
||||
// that is declared, initialized and cleared.
|
||||
M_LET( (words, ("This"), ("is"), ("a"), ("useless"), ("sentence"), ("."),
|
||||
("It"), ("is"), ("used"), ("a"), ("bit"), ("to"), ("count"), ("words"), (".") ),
|
||||
vector_str_t) {
|
||||
// Print the arrays.
|
||||
printf("The words are: ");
|
||||
vector_str_out_str(stdout, words);
|
||||
printf("\n");
|
||||
|
||||
// Count the words.
|
||||
M_LET(map, dict_str_t) {
|
||||
// Count the words:
|
||||
for M_EACH(w, words, vector_str_t) {
|
||||
(*dict_str_safe_get(map, *w)) ++;
|
||||
}
|
||||
|
||||
// Print the count:
|
||||
for M_EACH(p, map, dict_str_t) {
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
|
||||
M_PRINT(p->value, " occurences of ", p->key, "\n");
|
||||
#else
|
||||
printf ("%zu occurences of %s\n", p->value, string_get_cstr(p->key));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Based on https://en.cppreference.com/w/cpp/algorithm/for_each
|
||||
Need to be built in C11 mode */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "m-array.h"
|
||||
#include "m-algo.h"
|
||||
|
||||
/* Define a dynamic array of int */
|
||||
ARRAY_DEF(vector_int, int)
|
||||
#define M_OPL_vector_int_t() ARRAY_OPLIST(vector_int)
|
||||
|
||||
/* Define operator increment on int */
|
||||
#define INC(n) (n)++
|
||||
|
||||
int main(void)
|
||||
{
|
||||
M_LET( (nums, 3, 4, 2, 8, 15, 267), vector_int_t) {
|
||||
// Print the array before
|
||||
printf ("before:");
|
||||
ALGO_FOR_EACH(nums, vector_int_t, M_PRINT, " ");
|
||||
printf ("\n");
|
||||
|
||||
// Increment each element of the array
|
||||
ALGO_FOR_EACH(nums, vector_int_t, INC);
|
||||
|
||||
// Print the array after
|
||||
printf ("after: ");
|
||||
ALGO_FOR_EACH(nums, vector_int_t, M_PRINT, " ");
|
||||
printf ("\n");
|
||||
|
||||
// Sum the elements of the array
|
||||
int sum = 0;
|
||||
ALGO_REDUCE(sum, nums, vector_int_t, add);
|
||||
M_PRINT("sum = ", sum, "\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
#include "m-algo.h"
|
||||
#include "m-funcobj.h"
|
||||
#include "m-serial-json.h"
|
||||
#include "m-string.h"
|
||||
|
||||
/* Define the employee class */
|
||||
TUPLE_DEF2(employee,
|
||||
(name, string_t),
|
||||
(age, int),
|
||||
(idnum, int))
|
||||
/* Register globaly the employee_t oplist */
|
||||
#define M_OPL_employee_t() TUPLE_OPLIST(employee, STRING_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
/* Define an array of employee, register the oplist and define further algorithms on it */
|
||||
ARRAY_DEF(vector_employee, employee_t)
|
||||
#define M_OPL_vector_employee_t() ARRAY_OPLIST(vector_employee, M_OPL_employee_t())
|
||||
ALGO_DEF(vector_employee, vector_employee_t)
|
||||
|
||||
/* Define the function object to select the sort order.
|
||||
* It is named 'compare_by' and is an instance of the interface
|
||||
* 'vector_employee_cmp_obj' created by ALGO_DEF.
|
||||
*/
|
||||
FUNC_OBJ_INS_DEF(compare_by /* name of the instance */,
|
||||
vector_employee_cmp_obj /* name of the interface */,
|
||||
(a, b) /* name of the input parameters of the function like object. The type are inherited from the interface. */, {
|
||||
/* code of the function object */
|
||||
if (string_equal_str_p(self->sort_field, "name"))
|
||||
return string_cmp(a->name, b->name);
|
||||
else if (string_equal_str_p(self->sort_field, "age"))
|
||||
return a->age < b->age ? -1 : a->age > b->age;
|
||||
else
|
||||
return a->idnum < b->idnum ? -1 : a->idnum > b->idnum;
|
||||
},
|
||||
/* Additional fields stored in the function object */
|
||||
(sort_field, string_t) )
|
||||
#define M_OPL_compare_by_t() FUNC_OBJ_INS_OPLIST(compare_by, STRING_OPLIST)
|
||||
|
||||
// Let's read an array of person from the given JSON file
|
||||
static void read(vector_employee_t base, const char filename[])
|
||||
{
|
||||
m_serial_return_code_t ret;
|
||||
|
||||
// Open the file
|
||||
FILE *f = fopen(filename, "rt");
|
||||
if (!f) {
|
||||
// Handle errors
|
||||
fprintf(stderr, "ERROR: Cannot open file '%s'.\n", filename);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
// Initialize the JSON serializer with this file
|
||||
M_LET( (in,f), m_serial_json_read_t)
|
||||
// Read the JSON file and fill in 'base' with it.
|
||||
ret = vector_employee_in_serial(base, in);
|
||||
|
||||
// Let's handle serialization error
|
||||
if (ret != M_SERIAL_OK_DONE) {
|
||||
fprintf(stderr, "ERROR: Cannot read JSON data from file '%s'.\nParsing stops at:\n", filename);
|
||||
while (!feof(f)) { fputc(fgetc(f), stdout); }
|
||||
fclose(f);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
// Close the FILE
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
M_LET(emps, vector_employee_t) {
|
||||
/* Code to populate database from JSON */
|
||||
read(emps, "ex11-algo02.json");
|
||||
|
||||
// Sort the database by employee ID number
|
||||
M_LET( (cmp, STRING_CTE("idnum")), compare_by_t )
|
||||
vector_employee_sort_fo(emps, compare_by_as_interface(cmp));
|
||||
M_PRINT("Employees sorted by idnum are: ", (emps, vector_employee_t), "\n" );
|
||||
|
||||
// Sort the database by employee name
|
||||
M_LET( (cmp, STRING_CTE("name")), compare_by_t )
|
||||
vector_employee_sort_fo(emps, compare_by_as_interface(cmp));
|
||||
M_PRINT("Employees sorted by name are: ", (emps, vector_employee_t), "\n" );
|
||||
|
||||
// End
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{ "name": "John Doe", "age": 32, "idnum": 14528},
|
||||
{ "name": "Clint Smith", "age": 31, "idnum": 14529},
|
||||
{ "name": "Linda Cameron", "age": 20, "idnum": 14527},
|
||||
{ "name": "Cameron Carrey", "age": 25, "idnum": 14526},
|
||||
{ "name": "Marty Brown", "age": 26, "idnum": 14525},
|
||||
{ "name": "Donald Cow", "age": 45, "idnum": 14520}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "m-serial-json.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-array.h"
|
||||
|
||||
// Let's define a 'person' composed of several fields.
|
||||
TUPLE_DEF2(person,
|
||||
(surname, string_t),
|
||||
(age, int),
|
||||
(present, bool)
|
||||
)
|
||||
// Register the oplist of this tuple globaly
|
||||
#define M_OPL_person_t() TUPLE_OPLIST(person, M_DEFAULT_OPLIST, STRING_OPLIST, M_DEFAULT_OPLIST )
|
||||
|
||||
// Let's define an array of person
|
||||
ARRAY_DEF(base, person_t)
|
||||
// Register the oplist of this array globaly
|
||||
#define M_OPL_base_t() ARRAY_OPLIST(base, M_OPL_person_t())
|
||||
|
||||
// Let's read an array of person from the given JSON file
|
||||
static void read(base_t base, const char filename[])
|
||||
{
|
||||
m_serial_read_t in;
|
||||
m_serial_return_code_t ret;
|
||||
|
||||
// Open the file
|
||||
FILE *f = fopen(filename, "rt");
|
||||
if (!f) {
|
||||
fprintf(stderr, "ERROR: Cannot open file '%s'.\n", filename);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
// Initialize the serializer with the file
|
||||
m_serial_json_read_init(in, f);
|
||||
// Read the array
|
||||
ret = base_in_serial(base, in);
|
||||
if (ret != M_SERIAL_OK_DONE) {
|
||||
fprintf(stderr, "ERROR: Cannot read JSON data from file '%s'.\n", filename);
|
||||
exit(2);
|
||||
}
|
||||
// Let's close & clear all
|
||||
m_serial_json_read_clear(in);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Let's have base as a base_t
|
||||
M_LET(base, base_t) {
|
||||
// Read the JSON file and fill-in base
|
||||
read(base, "ex11-json01.json");
|
||||
// Print the contents of base:
|
||||
printf ("List of presents:\n");
|
||||
for M_EACH(el, base, base_t) {
|
||||
if ((*el)->present) {
|
||||
printf("%s\n", string_get_cstr((*el)->surname));
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{ "surname": "Angel", "age": 1, "present": false},
|
||||
{ "surname": "Snake", "age": 2, "present": true},
|
||||
{ "surname": "BallOfBase", "age": 5, "present": true}
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "m-array.h"
|
||||
#include "m-tuple.h"
|
||||
#include "m-dict.h"
|
||||
#include "m-string.h"
|
||||
|
||||
// Let's define a tuple composed of (offset, value) and register it
|
||||
TUPLE_DEF2(symbol, (offset, long), (value, long))
|
||||
#define M_OPL_symbol_t() TUPLE_OPLIST(symbol, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
// Let's define an array of such tuple and register it
|
||||
ARRAY_DEF(array_symbol, symbol_t)
|
||||
#define M_OPL_array_symbol_t() ARRAY_OPLIST(array_symbol, M_OPL_symbol_t())
|
||||
|
||||
// Let's define a dictionnary associating a string_t to such an array and register it
|
||||
DICT_DEF2(sections, string_t, array_symbol_t)
|
||||
#define M_OPL_sections_t() DICT_OPLIST(sections, STRING_OPLIST, M_OPL_array_symbol_t())
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
if (argc < 2) abort();
|
||||
FILE *f = fopen(argv[1], "rt");
|
||||
if (!f) {
|
||||
fprintf(stderr, "ERROR: Cannot open %s\n", argv[1]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Create & initialize sc as sections_t
|
||||
M_LET(sc, sections_t) {
|
||||
// Read the section from the FILE
|
||||
sections_in_str(sc, f);
|
||||
// Get the section named .text
|
||||
array_symbol_t *a = sections_get(sc, STRING_CTE(".text"));
|
||||
if (a == NULL) {
|
||||
printf("There is no .text section.");
|
||||
} else {
|
||||
// Print its content
|
||||
printf("Section .text is :");
|
||||
array_symbol_out_str(stdout, *a);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/* Based on the example of STC https://github.com/tylov/STC */
|
||||
#include "m-dict.h"
|
||||
#include "m-array.h"
|
||||
#include "m-deque.h"
|
||||
#include "m-list.h"
|
||||
#include "m-rbtree.h"
|
||||
#include "m-algo.h"
|
||||
#include "m-tuple.h"
|
||||
|
||||
// NOTE: After each definition of a container, we register its oplist
|
||||
// It is not mandatory, but it makes the code easier to write and read.
|
||||
|
||||
// Define a basic structure (POD structure) using a tuple to get the default functions
|
||||
TUPLE_DEF2(Point, (x, float), (y, float))
|
||||
#define M_OPL_Point_t() TUPLE_OPLIST(Point, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
// NOTE: array, list, deque don't have a native find function.
|
||||
// We'll use the algorithm header to create a find function for such containers.
|
||||
|
||||
// Define a set of int & register its oplist
|
||||
DICT_SET_DEF(cset_i, int)
|
||||
#define M_OPL_cset_i_t() DICT_SET_OPLIST(cset_i, M_DEFAULT_OPLIST)
|
||||
|
||||
// Define an array of Point, register its oplist & include some basic algorithms
|
||||
ARRAY_DEF(cvec_p, Point_t)
|
||||
#define M_OPL_cvec_p_t() ARRAY_OPLIST(cvec_p, M_OPL_Point_t())
|
||||
ALGO_DEF(cvec_p, cvec_p_t)
|
||||
|
||||
// Define a deque of int, including some basic algorithms
|
||||
DEQUE_DEF(cdeq_i, int)
|
||||
#define M_OPL_cdeq_i_t() DEQUE_OPLIST(cdeq_i, M_DEFAULT_OPLIST)
|
||||
ALGO_DEF(cdeq_i, cdeq_i_t)
|
||||
|
||||
// Define a backward list, including some basic algorithms
|
||||
LIST_DEF(clist_i, int)
|
||||
#define M_OPL_clist_i_t() LIST_OPLIST(clist_i, M_DEFAULT_OPLIST)
|
||||
ALGO_DEF(clist_i, clist_i_t)
|
||||
|
||||
// Define an unordered map of int to int
|
||||
DICT_DEF2(csmap_i, int, int)
|
||||
#define M_OPL_csmap_i_t() DICT_OPLIST(csmap_i, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Define, init & set the different containers.
|
||||
M_LET( (set, 10, 20, 30), cset_i_t)
|
||||
M_LET( (vec, (10, 1), (20, 2), (30, 3) ), cvec_p_t)
|
||||
M_LET( (deq, 10, 20, 30), cdeq_i_t)
|
||||
M_LET( (lst, 10, 20, 30), clist_i_t)
|
||||
M_LET( (map, (20, 2), (10, 1), (30,3) ), csmap_i_t) {
|
||||
|
||||
// add one more element to each container
|
||||
cset_i_push(set, 40);
|
||||
cvec_p_push_back(vec, (Point_t) {{40, 4}} );
|
||||
cdeq_i_push_front(deq, 5);
|
||||
clist_i_push_back(lst, 5);
|
||||
csmap_i_set_at(map, 40, 4);
|
||||
|
||||
M_PRINT("vec= ", (vec, cvec_p_t));
|
||||
|
||||
// Find an element (either native or through the algo header)
|
||||
int *i1 = cset_i_get(set, 20);
|
||||
cvec_p_it_t i2; cvec_p_find(i2, vec, (Point_t) {{20, 2}});
|
||||
cdeq_i_it_t i3; cdeq_i_find(i3, deq, 20);
|
||||
clist_i_it_t i4; clist_i_find(i4, lst, 20);
|
||||
int *i5 = csmap_i_get(map, 20);
|
||||
printf("\nFound: %d, (%g, %g), %d, %d, [%d: %d]\n",
|
||||
*i1,
|
||||
(*cvec_p_ref(i2))->x, (*cvec_p_ref(i2))->y,
|
||||
*cdeq_i_ref(i3),
|
||||
*clist_i_ref(i4),
|
||||
20, *i5);
|
||||
|
||||
// Erase the elements found (through key or through iterators)
|
||||
cset_i_erase(set, *i1);
|
||||
cvec_p_remove(vec, i2);
|
||||
cdeq_i_remove(deq, i3);
|
||||
clist_i_remove(lst, i4);
|
||||
csmap_i_erase(map, *i5);
|
||||
|
||||
// Display everything in one call by using the registered print functions of the different types.
|
||||
M_PRINT("After erasing elements found:\n",
|
||||
"set: ", (set, cset_i_t), "\n",
|
||||
"vec: ", (vec, cvec_p_t), "\n",
|
||||
"deq: ", (deq, cdeq_i_t), "\n",
|
||||
"map: ", (map, csmap_i_t), "\n");
|
||||
}
|
||||
}
|
||||
+148
-74
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* M*LIB - Thin stdatomic wrapper for C++ compatibility
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* Copyright (c) 2017-2021, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
@@ -22,18 +22,21 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_ATOMIC_H
|
||||
#define __M_ATOMIC_H
|
||||
#ifndef MSTARLIB_ATOMIC_H
|
||||
#define MSTARLIB_ATOMIC_H
|
||||
|
||||
/* NOTE: Due to the C++ not having recognized stdatomic.h officialy,
|
||||
it is hard to use this header directly with a C++ compiler like
|
||||
g++ or MSVC.
|
||||
(See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932 ).
|
||||
clang has no issue with this header.
|
||||
GCC 4.9 doesn't have a working implementation of 'atomic'
|
||||
it is hard to use this header directly with a C++ compiler.
|
||||
See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60932
|
||||
clang++ has no issue with this header but if someone includes
|
||||
atomic from C++, there is incompatibility between atomic & stdatomic.
|
||||
Moreover some compilers lack a working stdatomic header.
|
||||
GCC 4.9 doesn't have a working implementation of 'atomic'.
|
||||
APPLE Clang defines __GNUC__ to be only 4 despite having full support
|
||||
for atomic.
|
||||
*/
|
||||
#if defined(__cplusplus) && __cplusplus >= 201103L \
|
||||
&& !defined(__clang__) && !(defined(__GNUC__) && __GNUC__ < 5)
|
||||
#if defined(__cplusplus) && __cplusplus >= 201103L \
|
||||
&& !(defined(__GNUC__) && __GNUC__ < 5 && !defined(__APPLE__))
|
||||
|
||||
/* NOTE: This is what the stdatomic.h header shall do in C++ mode. */
|
||||
#include <atomic>
|
||||
@@ -89,42 +92,91 @@ using std::atomic_flag_test_and_set_explicit;
|
||||
using std::atomic_flag_clear;
|
||||
using std::atomic_flag_clear_explicit;
|
||||
|
||||
using std::memory_order_relaxed;
|
||||
using std::memory_order_consume;
|
||||
using std::memory_order_acquire;
|
||||
using std::memory_order_release;
|
||||
using std::memory_order_acq_rel;
|
||||
using std::memory_order_seq_cst;
|
||||
|
||||
/* CLANG provides a warning on defining _Atomic as it sees it
|
||||
* as a reserved system macro. It is true. However, the goal of this
|
||||
* header is to provide stdatomic semantic, so it needs to define
|
||||
* _Atomic macro.
|
||||
*
|
||||
* So, this warning has to be ignored.
|
||||
*
|
||||
* It cannot use M_BEGIN_PROTECTED_CODE as this header is normally
|
||||
* independent of m-core.h
|
||||
*/
|
||||
#if defined(__clang__) && __clang_major__ >= 4
|
||||
_Pragma("clang diagnostic push")
|
||||
_Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"")
|
||||
#endif
|
||||
|
||||
#define _Atomic(T) std::atomic< T >
|
||||
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \
|
||||
|| ( defined(__GNUC__) && !defined(__clang__) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \
|
||||
|| (defined(__clang__) && __clang_major__ >= 4)
|
||||
#if defined(__clang__) && __clang_major__ >= 4
|
||||
_Pragma("clang diagnostic pop")
|
||||
#endif
|
||||
|
||||
/* C11 with working stdatomic
|
||||
STDATOMIC doesn't work with C++ except for clang but is incompatible with atomic.
|
||||
GCC < 4.9 doesn't provide a compliant stdatomic.h
|
||||
CLANG 3.5 has issues with GCC's stdatomic.h and doesn't provide its own
|
||||
ICC < 18 doesn't provide a compliant stdatomic.h
|
||||
*/
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) ) \
|
||||
|| (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__cplusplus) && (__GNUC__*100 + __GNUC_MINOR__) >= 409) \
|
||||
|| (defined(__clang__) && (__clang_major__ * 100 + __clang_minor__) >= 308) \
|
||||
|| (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1800)
|
||||
|
||||
/* CLANG 3.5 has issues with GCC's stdatomic.h
|
||||
and doesn't provide its own stdatomic.h */
|
||||
#include <stdatomic.h>
|
||||
|
||||
#else
|
||||
/* MSYS2 has a conflict between cdefs.h which defines a _Atomic macro (if not C11)
|
||||
not compatible with the used stdatomic.h (from GCC).
|
||||
Provide a configurable mechanism to undef it with auto-detection of msys2 / gcc */
|
||||
#ifndef M_USE_UNDEF_ATOMIC
|
||||
# if defined(__MSYS__) && defined(__GNUC__) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L)
|
||||
# define M_USE_UNDEF_ATOMIC 1
|
||||
# endif
|
||||
#endif
|
||||
#if defined(M_USE_UNDEF_ATOMIC) && M_USE_UNDEF_ATOMIC == 1
|
||||
# undef _Atomic
|
||||
#endif
|
||||
|
||||
/* Non working atomic.h, nor working stdatomic.h found.
|
||||
/* Non working C++ atomic header, nor working stdatomic.h found.
|
||||
Write a compatible layer using mutex as slin as possible.
|
||||
Supports only up to 64-bits atomic (sizeof long long to be more precise).
|
||||
The locks are never properly cleared and remain active until
|
||||
the end of the program.
|
||||
We also assume that the call to the atomic_* interface is "clean".
|
||||
We also assume that the call to the atomic_* interface is "macro clean".
|
||||
*/
|
||||
#include "m-mutex.h"
|
||||
#else
|
||||
|
||||
/* The structure is quite large:
|
||||
__val : value of the atomic type,
|
||||
__zero : zero value of the atomic type (constant),
|
||||
__previous: temporary value used within the mutex lock,
|
||||
__lock : the mutex lock.
|
||||
#include "m-mutex.h"
|
||||
#include "m-core.h"
|
||||
|
||||
M_BEGIN_PROTECTED_CODE
|
||||
|
||||
/* _Atomic qualifier for a type (emulation).
|
||||
The structure is quite large:
|
||||
_val : value of the atomic type,
|
||||
_zero : zero value of the atomic type (constant),
|
||||
_previous: temporary value used within the mutex lock,
|
||||
_lock : the mutex lock.
|
||||
Support up to sizeof (long long) type.
|
||||
*/
|
||||
#define _Atomic(T) \
|
||||
struct { \
|
||||
volatile T __val; \
|
||||
T __zero; \
|
||||
T __previous; \
|
||||
m_mutex_t __lock; \
|
||||
#define _Atomic(T) \
|
||||
struct { \
|
||||
T volatile _val; \
|
||||
T _zero; \
|
||||
T _previous; \
|
||||
m_mutex_t _lock; \
|
||||
}
|
||||
|
||||
/* Even memory order is defined, only the strongest constraint is used */
|
||||
/* Define the supported memory order.
|
||||
Even if memory order is defined, only the strongest constraint is used */
|
||||
typedef enum {
|
||||
memory_order_relaxed,
|
||||
memory_order_consume,
|
||||
@@ -152,7 +204,7 @@ typedef _Atomic(size_t) atomic_size_t;
|
||||
typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
|
||||
|
||||
/* Define the minimum size supported by the architecture
|
||||
for atomic read or write.
|
||||
for an atomic read or write.
|
||||
This can help a lot since it avoids locking for atomic_load and
|
||||
atomic_store.
|
||||
*/
|
||||
@@ -165,77 +217,97 @@ typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
|
||||
#endif
|
||||
|
||||
/* Detect if stdint.h was included */
|
||||
#if (defined (INTMAX_C) && defined (UINTMAX_C) && !defined(__cplusplus)) || \
|
||||
defined (_STDINT_H) || defined (_STDINT_H_) || defined (_STDINT) || \
|
||||
#if (defined (INTMAX_C) && defined (UINTMAX_C) && !defined(__cplusplus)) || \
|
||||
defined (_STDINT_H) || defined (_STDINT_H_) || defined (_STDINT) || \
|
||||
defined (_SYS_STDINT_H_)
|
||||
/* Define additional atomic types */
|
||||
typedef _Atomic(intmax_t) atomic_intmax_t;
|
||||
typedef _Atomic(uintmax_t) atomic_uintmax_t;
|
||||
#endif
|
||||
|
||||
/* Unlock the mutex and return the given value */
|
||||
/* (INTERNAL) Unlock the mutex and return the given value */
|
||||
static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
|
||||
{
|
||||
m_mutex_unlock (*lock);
|
||||
return val;
|
||||
}
|
||||
|
||||
/* This is the heart of the wrapper:
|
||||
/* (INTERNAL) This is the heart of the wrapper:
|
||||
lock the atomic value, read it and returns the value.
|
||||
In order to avoid any compiler extension, we need to transform the
|
||||
atomic type into 'long long' then convert it back to its value.
|
||||
This is because __previous can't be read after the lock, and we can't
|
||||
This is because _previous can't be read after the lock, and we can't
|
||||
generate temporary variable within a macro.
|
||||
The trick is computing _val - _zero within the lock, then
|
||||
returns retvalue + _zero after the lock.
|
||||
returns retvalue + _zero after the release of the lock.
|
||||
*/
|
||||
#define atomic_fetch_op(ptr, val, op) \
|
||||
(m_mutex_lock((ptr)->__lock), \
|
||||
(ptr)->__previous = (ptr)->__val, \
|
||||
(ptr)->__val op (val), \
|
||||
atomic_fetch_unlock(&(ptr)->__lock, (ptr)->__previous-(ptr)->__zero)+(ptr)->__zero)
|
||||
#define atomic_fetch_op(ptr, val, op) \
|
||||
(m_mutex_lock((ptr)->_lock), \
|
||||
(ptr)->_previous = (ptr)->_val, \
|
||||
(ptr)->_val op (val), \
|
||||
atomic_fetch_unlock(&(ptr)->_lock, (long long)((ptr)->_previous-(ptr)->_zero))+(ptr)->_zero)
|
||||
|
||||
/* Perform an atomic add (EMULATION) */
|
||||
#define atomic_fetch_add(ptr, val) atomic_fetch_op(ptr, val, +=)
|
||||
/* Perform an atomic sub (EMULATION) */
|
||||
#define atomic_fetch_sub(ptr, val) atomic_fetch_op(ptr, val, -=)
|
||||
/* Perform an atomic or (EMULATION) */
|
||||
#define atomic_fetch_or(ptr, val) atomic_fetch_op(ptr, val, |=)
|
||||
/* Perform an atomic xor (EMULATION) */
|
||||
#define atomic_fetch_xor(ptr, val) atomic_fetch_op(ptr, val, ^=)
|
||||
/* Perform an atomic and (EMULATION) */
|
||||
#define atomic_fetch_and(ptr, val) atomic_fetch_op(ptr, val, &=)
|
||||
/* Perform an atomic exchange (EMULATION) */
|
||||
#define atomic_exchange(ptr, val) atomic_fetch_op(ptr, val, =)
|
||||
|
||||
/* Initialize an atomic GLOBAL variable */
|
||||
#define ATOMIC_VAR_INIT(val) { val, 0, 0, M_MUTEXI_INIT_VALUE }
|
||||
|
||||
#define atomic_init(ptr, val) \
|
||||
(m_mutex_init((ptr)->__lock), (ptr)->__val = val, (ptr)->__zero = 0)
|
||||
/* Initialize an atomic variable */
|
||||
#define atomic_init(ptr, val) \
|
||||
(m_mutex_init((ptr)->_lock), (ptr)->_val = val, (ptr)->_zero = 0)
|
||||
|
||||
#define atomic_load_lock(ptr) \
|
||||
(m_mutex_lock((ptr)->__lock), \
|
||||
(ptr)->__previous = (ptr)->__val, \
|
||||
atomic_fetch_unlock(&(ptr)->__lock, (ptr)->__previous-(ptr)->__zero)+(ptr)->__zero)
|
||||
/* (INTERNAL) Load an atomic variable within a lock
|
||||
(needed for variable greater than CPU atomic size) */
|
||||
#define atomic_load_lock(ptr) \
|
||||
(m_mutex_lock((ptr)->_lock), \
|
||||
(ptr)->_previous = (ptr)->_val, \
|
||||
atomic_fetch_unlock(&(ptr)->_lock, (long long) ((ptr)->_previous-(ptr)->_zero))+(ptr)->_zero)
|
||||
|
||||
#define atomic_store_lock(ptr, val) \
|
||||
(m_mutex_lock((ptr)->__lock), \
|
||||
(ptr)->__val = (val), \
|
||||
m_mutex_unlock((ptr)->__lock))
|
||||
/* (INTERNAL) Store an atomic variable within a lock
|
||||
(needed for variable greater than CPU atomic size) */
|
||||
#define atomic_store_lock(ptr, val) \
|
||||
(m_mutex_lock((ptr)->_lock), \
|
||||
(ptr)->_val = (val), \
|
||||
m_mutex_unlock((ptr)->_lock))
|
||||
|
||||
#define atomic_load(ptr) \
|
||||
( sizeof ((ptr)->__val) <= ATOMICI_MIN_RW_SIZE \
|
||||
? (ptr)->__val \
|
||||
/* Atomic load of a variable (EMULATION)
|
||||
If the atomic type size is not greater than the CPU atomic size,
|
||||
we can perform a direct read of the variable (much faster) */
|
||||
#define atomic_load(ptr) \
|
||||
( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE \
|
||||
? (ptr)->_val \
|
||||
: atomic_load_lock(ptr))
|
||||
|
||||
#define atomic_store(ptr, val) do { \
|
||||
if ( sizeof ((ptr)->__val) <= ATOMICI_MIN_RW_SIZE) { \
|
||||
(ptr)->__val = (val); \
|
||||
} else { \
|
||||
long long _offset = (val) - (ptr)->__zero; \
|
||||
atomic_store_lock(ptr, (ptr)->__zero + _offset); \
|
||||
} \
|
||||
|
||||
/* Atomic store of a variable (EMULATION)
|
||||
If the atomic type size is not greater than the CPU atomic size,
|
||||
we can perform a direct write of the variable (much faster) */
|
||||
#define atomic_store(ptr, val) do { \
|
||||
if ( sizeof ((ptr)->_val) <= ATOMICI_MIN_RW_SIZE) { \
|
||||
(ptr)->_val = (val); \
|
||||
} else { \
|
||||
long long _offset = (long long) ((val) - (ptr)->_zero); \
|
||||
atomic_store_lock(ptr, (ptr)->_zero + _offset); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define atomic_compare_exchange_strong(ptr, exp, val) \
|
||||
(m_mutex_lock((ptr)->__lock), \
|
||||
atomic_fetch_unlock(&(ptr)->__lock, \
|
||||
(ptr)->__val == *(exp) \
|
||||
? ((ptr)->__val = (val), true) \
|
||||
: (*(exp) = (ptr)->__val, false)))
|
||||
/* Perform a CAS (Compare and swap) operation (EMULATION) */
|
||||
#define atomic_compare_exchange_strong(ptr, exp, val) \
|
||||
(m_mutex_lock((ptr)->_lock), \
|
||||
atomic_fetch_unlock(&(ptr)->_lock, \
|
||||
(ptr)->_val == *(exp) \
|
||||
? ((ptr)->_val = (val), true) \
|
||||
: (*(exp) = (ptr)->_val, false)))
|
||||
|
||||
|
||||
#define atomic_fetch_add_explicit(ptr, val, mem) atomic_fetch_op(ptr, val, +=)
|
||||
@@ -250,11 +322,13 @@ static inline long long atomic_fetch_unlock (m_mutex_t *lock, long long val)
|
||||
#define atomic_thread_fence(mem) (void) 0
|
||||
#define atomic_signal_fence(mem) (void) 0
|
||||
#define atomic_is_lock_free(ptr) false
|
||||
#define atomic_compare_exchange_strong_explicit(ptr, exp, val, mem) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_weak_explicit(ptr, exp, val, mem) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_weak(ptr, exp, val) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_strong_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_weak_explicit(ptr, exp, val, mem1, mem2) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
#define atomic_compare_exchange_weak(ptr, exp, val) atomic_compare_exchange_strong(ptr, exp, val)
|
||||
|
||||
/* TODO: Missing atomic_flag. Problem: it needs to be lock free! */
|
||||
/* TODO: Missing atomic_flag. Problem: it is supposed to be lock free! */
|
||||
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+464
-259
File diff suppressed because it is too large.
Load diff
+1482
File diff suppressed because it is too large.
Load diff
+1191
-656
File diff suppressed because it is too large.
Load diff
+831
@@ -0,0 +1,831 @@
|
||||
/*
|
||||
* M*LIB - Concurrent memory pool allocator
|
||||
*
|
||||
* Copyright (c) 2017-2021, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_CONCURRENT_MEMPOOL_H
|
||||
#define MSTARLIB_CONCURRENT_MEMPOOL_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-atomic.h"
|
||||
#include "m-genint.h"
|
||||
|
||||
M_BEGIN_PROTECTED_CODE
|
||||
|
||||
/* Minimum number of nodes per group of nodes */
|
||||
#define M_CMEMP00L_MIN_NODE_PER_GROUP 16
|
||||
|
||||
#define C_MEMPOOL_DEF(name, type_t) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \
|
||||
M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \
|
||||
M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
|
||||
M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \
|
||||
M_CMEMP00L_DEF_LF_MEMPOOL(name, type_t) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
/* Classic internal Singly List without allocation */
|
||||
#define M_CMEMP00L_DEF_SINGLY_LIST(name, type_t) \
|
||||
\
|
||||
typedef struct M_C(name, _slist_node_s) { \
|
||||
struct M_C(name, _slist_node_s) *next; \
|
||||
type_t data; \
|
||||
} M_C(name, _slist_node_ct); \
|
||||
\
|
||||
typedef struct M_C(name, _slist_node_s) *M_C(name, _slist_ct)[1]; \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _slist_init)(M_C(name, _slist_ct) list) \
|
||||
{ \
|
||||
*list = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _slist_push)(M_C(name, _slist_ct) list, \
|
||||
M_C(name, _slist_node_ct) *node) \
|
||||
{ \
|
||||
node->next = *list; \
|
||||
*list = node; \
|
||||
} \
|
||||
\
|
||||
static inline M_C(name, _slist_node_ct) * \
|
||||
M_C(name, _slist_pop)(M_C(name, _slist_ct) list) \
|
||||
{ \
|
||||
M_ASSERT (*list != NULL); \
|
||||
M_C(name, _slist_node_ct) *node = *list; \
|
||||
*list = node->next; \
|
||||
M_IF_DEBUG(node->next = NULL;) \
|
||||
return node; \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _slist_empty_p)(M_C(name, _slist_ct) list) \
|
||||
{ \
|
||||
return *list == NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _slist_move)(M_C(name, _slist_ct) list, \
|
||||
M_C(name, _slist_ct) src) \
|
||||
{ \
|
||||
*list = *src; \
|
||||
*src = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _slist_clear)(M_C(name, _slist_ct) list) \
|
||||
{ \
|
||||
M_C(name, _slist_node_ct) *it = *list, *next; \
|
||||
while (it) { \
|
||||
next = it->next; \
|
||||
M_MEMORY_DEL(it); \
|
||||
it = next; \
|
||||
} \
|
||||
*list = NULL; \
|
||||
} \
|
||||
|
||||
|
||||
/* Lock Free free queue list (not generic one) of lists without allocation
|
||||
Based on Michael & Scott Lock Free Queue List algorithm.
|
||||
Each list is considered empty if there is only one node within.
|
||||
This LF Queue List doesn't try to prevent the ABA problem. It is up to the
|
||||
caller to avoid recycling the nodes too fast.
|
||||
Each list has its own unique NIL ptr in order to avoid issues when
|
||||
migrating a node from a Q to another: in the following scenario,
|
||||
- Thread 1 performs a PUSH of N in Q1 with Q1 empty (only node is NA)
|
||||
NA.next is NIL.
|
||||
- Thread 1 is interrupted just before the CAS on NA.next
|
||||
- Thread 2 performs a sucessfull push of NB in Q1. NA.next is set to NB.
|
||||
- Thread 2 performs a sucessfull pop of NA in Q1
|
||||
- Thread 2 performs a sucessfull push of NA in Q2. NA.next is set to NIL.
|
||||
- Thread 1 is restored and will succeed as NA.next is once again NIL.
|
||||
In order to prevent the last CAS to succeed, each queue uses its own NIL pointer.
|
||||
It is a derived problem of the ABA problem.
|
||||
*/
|
||||
/* TODO: Optimize alignement to reduce memory consumption. NIL object can use []
|
||||
to reduce memory consumption too (non compatible with C++ ...) */
|
||||
#define M_CMEMP00L_DEF_LF_QUEUE(name, type_t) \
|
||||
\
|
||||
typedef struct M_C(name, _lf_node_s) { \
|
||||
M_ATTR_EXTENSION _Atomic(struct M_C(name, _lf_node_s) *) next; \
|
||||
m_gc_atomic_ticket_ct cpt; \
|
||||
M_C(name, _slist_ct) list; \
|
||||
} M_C(name, _lf_node_t); \
|
||||
\
|
||||
typedef struct M_C(name, _lflist_s) { \
|
||||
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) head; \
|
||||
char align1[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||
M_ATTR_EXTENSION _Atomic(M_C(name, _lf_node_t) *) tail; \
|
||||
char align2[M_ALIGN_FOR_CACHELINE_EXCLUSION]; \
|
||||
M_C(name, _lf_node_t) nil; \
|
||||
} M_C(name, _lflist_ct)[1]; \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lflist_init)(M_C(name, _lflist_ct) list, \
|
||||
M_C(name, _lf_node_t) *node) \
|
||||
{ \
|
||||
atomic_init(&list->head, node); \
|
||||
atomic_init(&list->tail, node); \
|
||||
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
|
||||
} \
|
||||
\
|
||||
static inline bool \
|
||||
M_C(name, _lflist_empty_p)(M_C(name, _lflist_ct) list) \
|
||||
{ \
|
||||
return atomic_load(&list->tail) == atomic_load(&list->head); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lflist_push)(M_C(name, _lflist_ct) list, \
|
||||
M_C(name, _lf_node_t) *node, m_core_backoff_ct bkoff) \
|
||||
{ \
|
||||
M_C(name, _lf_node_t) *tail; \
|
||||
M_C(name, _lf_node_t) *next; \
|
||||
\
|
||||
atomic_store_explicit(&node->next, &list->nil, memory_order_relaxed); \
|
||||
m_core_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load_explicit(&tail->next, memory_order_acquire); \
|
||||
if (M_UNLIKELY(next != &list->nil)) { \
|
||||
/* Tail was not pointing to the last node \
|
||||
Try to swing Tail to the next node */ \
|
||||
atomic_compare_exchange_weak_explicit(&list->tail, \
|
||||
&tail, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Try to link node at the end of the linked list */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&tail->next, \
|
||||
&next, node, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) \
|
||||
break; \
|
||||
m_core_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
/* Enqueue is done. Try to swing Tail to the inserted node \
|
||||
If it fails, someone else will do it or has already did it. */ \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, node, \
|
||||
memory_order_acq_rel, \
|
||||
memory_order_relaxed); \
|
||||
} \
|
||||
\
|
||||
static inline M_C(name, _lf_node_t) * \
|
||||
M_C(name, _lflist_pop)(M_C(name, _lflist_ct) list, m_core_backoff_ct bkoff) \
|
||||
{ \
|
||||
M_C(name, _lf_node_t) *head; \
|
||||
M_C(name, _lf_node_t) *tail; \
|
||||
M_C(name, _lf_node_t) *next; \
|
||||
\
|
||||
/* Reinitialize backoff */ \
|
||||
m_core_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
head = atomic_load(&list->head); \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load(&head->next); \
|
||||
/* Are head, tail, and next consistent?*/ \
|
||||
if (M_LIKELY(head == \
|
||||
atomic_load_explicit(&list->head, memory_order_relaxed))) \
|
||||
{ \
|
||||
/* Is queue empty or Tail falling behind? */ \
|
||||
if (head == tail) { \
|
||||
/* Is queue empty? */ \
|
||||
if (next == &list->nil) \
|
||||
return NULL; \
|
||||
/* Tail is falling behind. Try to advance it */ \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, \
|
||||
next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Try to swing Head to the next node */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&list->head, \
|
||||
&head, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) { \
|
||||
break; \
|
||||
} \
|
||||
/* Failure: perform a random exponential backoff */ \
|
||||
m_core_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
/* dequeue returns an element that becomes the new dummy element (the new head), \
|
||||
and the former dummy element (the former head) is removed: \
|
||||
Since we want a link of free list, and we don't care about the content itsef, \
|
||||
provided that the node we return is older than the one we should return, \
|
||||
Therefore, we return the previous dummy head. \
|
||||
As such, it is not the original MSqueue algorithm. */ \
|
||||
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
|
||||
return head; \
|
||||
} \
|
||||
\
|
||||
/* Dequeue a node if the node is old enough */ \
|
||||
static inline M_C(name, _lf_node_t) * \
|
||||
M_C(name, _lflist_pop_if)(M_C(name, _lflist_ct) list, \
|
||||
m_gc_ticket_ct age, m_core_backoff_ct bkoff) \
|
||||
{ \
|
||||
M_C(name, _lf_node_t) *head; \
|
||||
M_C(name, _lf_node_t) *tail; \
|
||||
M_C(name, _lf_node_t) *next; \
|
||||
\
|
||||
m_core_backoff_reset(bkoff); \
|
||||
while (true) { \
|
||||
head = atomic_load(&list->head); \
|
||||
tail = atomic_load(&list->tail); \
|
||||
next = atomic_load(&head->next); \
|
||||
if (M_LIKELY(head == atomic_load_explicit(&list->head, memory_order_relaxed))) \
|
||||
{ \
|
||||
if (head == tail) { \
|
||||
if (next == &list->nil) \
|
||||
return NULL; \
|
||||
atomic_compare_exchange_strong_explicit(&list->tail, &tail, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed); \
|
||||
} else { \
|
||||
/* Test if the node is old enought to be popped */ \
|
||||
if (atomic_load_explicit(&next->cpt, memory_order_relaxed) >= age) \
|
||||
return NULL; \
|
||||
/* Try to swing Head to the next node */ \
|
||||
if (atomic_compare_exchange_strong_explicit(&list->head, \
|
||||
&head, next, \
|
||||
memory_order_release, \
|
||||
memory_order_relaxed)) { \
|
||||
break; \
|
||||
} \
|
||||
m_core_backoff_wait(bkoff); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
M_IF_DEBUG(atomic_store(&head->next, (M_C(name, _lf_node_t) *) 0);) \
|
||||
return head; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lflist_clear)(M_C(name, _lflist_ct) list) \
|
||||
{ \
|
||||
m_core_backoff_ct bkoff; \
|
||||
m_core_backoff_init(bkoff); \
|
||||
while (true) { \
|
||||
M_C(name, _lf_node_t) *node = M_C(name, _lflist_pop)(list, bkoff); \
|
||||
if (node == NULL) break; \
|
||||
M_C(name, _lf_node_t) *next = atomic_load_explicit(&node->next, \
|
||||
memory_order_relaxed); \
|
||||
M_C(name, _slist_clear)(node->list); \
|
||||
M_MEMORY_DEL(node); \
|
||||
node = next; \
|
||||
} \
|
||||
/* Dummy node to free too */ \
|
||||
M_C(name, _lf_node_t) *dummy; \
|
||||
dummy = atomic_load_explicit(&list->head, memory_order_relaxed); \
|
||||
M_C(name, _slist_clear)(dummy->list); \
|
||||
M_MEMORY_DEL(dummy); \
|
||||
} \
|
||||
|
||||
/* System node allocator: request memory to the system.
|
||||
As such it is a non Lock-Free path. */
|
||||
#define M_CMEMP00L_DEF_SYSTEM_ALLOC(name, type_t) \
|
||||
\
|
||||
static inline M_C(name, _lf_node_t) * \
|
||||
M_C(name, _alloc_node)(unsigned int initial) \
|
||||
{ \
|
||||
M_C(name, _lf_node_t) * node; \
|
||||
node = M_MEMORY_ALLOC(M_C(name, _lf_node_t)); \
|
||||
if (M_UNLIKELY(node == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
|
||||
return NULL; \
|
||||
} \
|
||||
atomic_init(&node->next, (M_C(name, _lf_node_t) *) 0); \
|
||||
atomic_init(&node->cpt, 0UL); \
|
||||
M_C(name, _slist_init)(node->list); \
|
||||
for(unsigned i = 0; i < initial; i++) { \
|
||||
M_C(name, _slist_node_ct) *n; \
|
||||
n = M_MEMORY_ALLOC(M_C(name, _slist_node_ct)); \
|
||||
if (M_UNLIKELY(n == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof(M_C(name, _lf_node_t))); \
|
||||
return NULL; \
|
||||
} \
|
||||
M_C(name, _slist_push)(node->list, n); \
|
||||
} \
|
||||
return node; \
|
||||
} \
|
||||
|
||||
/* Concurrent Memory pool
|
||||
The data structure is the following.
|
||||
Each thread has its own pool of nodes (local) that only it can
|
||||
access (it is a singly list). If there is no longer any node in this
|
||||
pool, it requests a new pool to the lock free queue of pool (group of
|
||||
nodes). If it fails, it requests a new pool to the system allocator
|
||||
(and from there it is no longer lock free).
|
||||
This memory pool can only be lock free if the initial state is
|
||||
sufficiently dimensionned to avoid calling the system allocator during
|
||||
the normal processing.
|
||||
Then each thread pushs its deleted node into another pool of nodes,
|
||||
where the node is logically deleted (no contain of the node is destroyed
|
||||
at this point and the node can be freely accessed by other threads).
|
||||
Once the thread mempool is put to sleep, the age of the pool of logical
|
||||
deleted nodes is computed and this pool is move to the Lock Free Queue
|
||||
List of pools to be reclaimed. Then A Garbage Collector is performed
|
||||
on this Lock Free Queue list to reclaim all pools thare are sufficiently
|
||||
aged (taking into account the grace period of the pool) to be moved back
|
||||
to the Lock Free Queue of the free pools.
|
||||
|
||||
Each pool of nodes can be in the following state:
|
||||
* FREE state if it is present in the Lock Free Queue of free pools.
|
||||
* EMPTY state if it is present in the Lock Free Queue of empty pools
|
||||
which means that the nodes present in it has been USED directly by a thread,
|
||||
* TO_BE_RECLAIMED state if it is present in the Lock Free Queue of TBR pools
|
||||
|
||||
A pool of nodes will go to the following state:
|
||||
FREE --> EMPTY --> TO_BE_RECLAIMED
|
||||
^ |
|
||||
+----------------------+
|
||||
|
||||
The ABA problem is taken into account as a node cannot be reused in the
|
||||
same queue without performing a full cycle of its state. Moreover
|
||||
it can only move from TO_BE_RECLAIMED to FREE if and only if a grace
|
||||
period is finished (and then we are sure that no thread references any
|
||||
older node).
|
||||
|
||||
Each thread has its own backoff structure (with local pseudo-random
|
||||
generator).
|
||||
|
||||
The grace period is detected through a global age counter (ticket)
|
||||
that is incremented each time a thread is awaken / sleep.
|
||||
Each thread has its own age that is set to the global ticket on sleep/awaken.
|
||||
The age of the pool to be reclaimed is also set to this global age counter.
|
||||
|
||||
To ensure that the grace period is finished, it tests if all threads
|
||||
are younger than the age of the pool to be reclaimed.
|
||||
|
||||
From a performance point of view, this puts a bottleneck on the global
|
||||
age counter that is shared and incremented by all threads. However,
|
||||
the sleep/awaken operations are much less frequent than other operations.
|
||||
Thus, it shall not have a huge impact on the performance if the user
|
||||
code is intelligent with the sleep/awaken operations.
|
||||
|
||||
As such it won't support more than ULONG_MAX sleep for all threads.
|
||||
*/
|
||||
#define M_CMEMP00L_DEF_LFMP_THREAD_MEMPOOL(name, type_t) \
|
||||
\
|
||||
typedef struct M_C(name, _lfmp_thread_s) { \
|
||||
M_C(name, _slist_ct) free; \
|
||||
M_C(name, _slist_ct) to_be_reclaimed; \
|
||||
M_CACHELINE_ALIGN(align1, M_C(name, _slist_ct), M_C(name, _slist_ct)); \
|
||||
} M_C(name, _lfmp_thread_ct); \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lfmp_thread_init)(M_C(name, _lfmp_thread_ct) *t) \
|
||||
{ \
|
||||
M_C(name, _slist_init)(t->free); \
|
||||
M_C(name, _slist_init)(t->to_be_reclaimed); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _lfmp_thread_clear)(M_C(name, _lfmp_thread_ct) *t) \
|
||||
{ \
|
||||
M_ASSERT(M_C(name, _slist_empty_p)(t->to_be_reclaimed)); \
|
||||
M_C(name, _slist_clear)(t->free); \
|
||||
M_C(name, _slist_clear)(t->to_be_reclaimed); \
|
||||
} \
|
||||
|
||||
/* NOTE: once a node is deleted, its data are kept readable until the future GC */
|
||||
#define M_CMEMP00L_DEF_LF_MEMPOOL(name, type_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
unsigned initial; \
|
||||
M_C(name, _lfmp_thread_ct) *thread_data; \
|
||||
M_C(name, _lflist_ct) free; \
|
||||
M_C(name, _lflist_ct) to_be_reclaimed; \
|
||||
M_C(name, _lflist_ct) empty; \
|
||||
m_gM_CMEMP00L_list_ct mempool_node; \
|
||||
struct m_gc_s *gc_mem; \
|
||||
} M_C(name, _t)[1]; \
|
||||
\
|
||||
/* Garbage collect of the nodes of the mempool on sleep */ \
|
||||
static inline void \
|
||||
M_C3(m_cmemp00l_,name,_gc_on_sleep)(m_gc_t gc_mem, m_gM_CMEMP00L_list_ct *data, \
|
||||
m_gc_tid_t id, m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket) \
|
||||
{ \
|
||||
/* Get back the mempool from the node */ \
|
||||
struct M_C(name, _s) *mempool = \
|
||||
M_TYPE_FROM_FIELD(struct M_C(name, _s), data, m_gM_CMEMP00L_list_ct, mempool_node); \
|
||||
\
|
||||
/* Move the local nodes of the mempool to be reclaimed to the thread into the global pool */ \
|
||||
if (!M_C(name, _slist_empty_p)(mempool->thread_data[id].to_be_reclaimed)) { \
|
||||
M_C(name, _lf_node_t) *node; \
|
||||
/* Get a new empty group of nodes */ \
|
||||
node = M_C(name, _lflist_pop)(mempool->empty, gc_mem->thread_data[id].bkoff); \
|
||||
if (M_UNLIKELY (node == NULL)) { \
|
||||
/* Fail to get an empty group of node. \
|
||||
Alloc a new one from the system */ \
|
||||
node = M_C(name, _alloc_node)(0); \
|
||||
M_ASSERT(node != NULL); \
|
||||
} \
|
||||
M_ASSERT(M_C(name, _slist_empty_p)(node->list)); \
|
||||
M_C(name, _slist_move)(node->list, mempool->thread_data[id].to_be_reclaimed); \
|
||||
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed); \
|
||||
M_C(name, _lflist_push)(mempool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
\
|
||||
/* Perform a GC of the freelist of nodes */ \
|
||||
while (true) { \
|
||||
M_C(name, _lf_node_t) *node; \
|
||||
node = M_C(name, _lflist_pop_if)(mempool->to_be_reclaimed, \
|
||||
min_ticket, gc_mem->thread_data[id].bkoff); \
|
||||
if (node == NULL) break; \
|
||||
M_C(name, _lflist_push)(mempool->free, node, gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _init)(M_C(name, _t) mem, m_gc_t gc_mem, \
|
||||
unsigned init_node_count, unsigned init_group_count) \
|
||||
{ \
|
||||
const size_t max_thread = gc_mem->max_thread; \
|
||||
/* Initialize the thread data of the mempool */ \
|
||||
mem->thread_data = M_MEMORY_REALLOC(M_C(name, _lfmp_thread_ct), NULL, max_thread); \
|
||||
if (mem->thread_data == NULL) { \
|
||||
M_MEMORY_FULL(max_thread * sizeof(M_C(name, _lfmp_thread_ct))); \
|
||||
return; \
|
||||
} \
|
||||
for(unsigned i = 0; i < max_thread;i++) { \
|
||||
M_C(name, _lfmp_thread_init)(&mem->thread_data[i]); \
|
||||
} \
|
||||
/* Preallocate some group of nodes for the mempool */ \
|
||||
mem->initial = M_MAX(M_CMEMP00L_MIN_NODE_PER_GROUP, init_node_count); \
|
||||
M_C(name, _lflist_init)(mem->free, M_C(name, _alloc_node)(init_node_count)); \
|
||||
M_C(name, _lflist_init)(mem->to_be_reclaimed, M_C(name, _alloc_node)(init_node_count)); \
|
||||
M_C(name, _lflist_init)(mem->empty, M_C(name, _alloc_node)(0)); \
|
||||
for(unsigned i = 1; i < init_group_count; i++) { \
|
||||
M_C(name, _lflist_push)(mem->free, M_C(name, _alloc_node)(init_node_count), \
|
||||
gc_mem->thread_data[0].bkoff); \
|
||||
M_C(name, _lflist_push)(mem->empty, M_C(name, _alloc_node)(0), \
|
||||
gc_mem->thread_data[0].bkoff); \
|
||||
} \
|
||||
/* Register the mempool in the GC */ \
|
||||
mem->mempool_node.gc_on_sleep = M_C3(m_cmemp00l_,name,_gc_on_sleep); \
|
||||
mem->mempool_node.next = gc_mem->mempool_list; \
|
||||
gc_mem->mempool_list = &mem->mempool_node; \
|
||||
mem->gc_mem = gc_mem; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _clear)(M_C(name, _t) mem) \
|
||||
{ \
|
||||
const unsigned max_thread = mem->gc_mem->max_thread; \
|
||||
for(unsigned i = 0; i < max_thread;i++) { \
|
||||
M_C(name, _lfmp_thread_clear)(&mem->thread_data[i]); \
|
||||
} \
|
||||
M_MEMORY_FREE(mem->thread_data); \
|
||||
mem->thread_data = NULL; \
|
||||
M_C(name, _lflist_clear)(mem->empty); \
|
||||
M_C(name, _lflist_clear)(mem->free); \
|
||||
M_ASSERT(M_C(name, _lflist_empty_p)(mem->to_be_reclaimed)); \
|
||||
M_C(name, _lflist_clear)(mem->to_be_reclaimed); \
|
||||
/* TODO: Unregister from the GC? */ \
|
||||
} \
|
||||
\
|
||||
static inline type_t * \
|
||||
M_C(name, _new)(M_C(name, _t) mem, m_gc_tid_t id) \
|
||||
{ \
|
||||
M_C(name, _slist_node_ct) *snode; \
|
||||
M_C(name, _lf_node_t) *node; \
|
||||
while (true) { \
|
||||
/* Fast & likely path where we access the thread pool of nodes */ \
|
||||
if (M_LIKELY(!M_C(name, _slist_empty_p)(mem->thread_data[id].free))) { \
|
||||
snode = M_C(name, _slist_pop)(mem->thread_data[id].free); \
|
||||
return &snode->data; \
|
||||
} \
|
||||
/* Request a group node to the freelist of groups */ \
|
||||
node = M_C(name, _lflist_pop)(mem->free, mem->gc_mem->thread_data[id].bkoff); \
|
||||
if (M_UNLIKELY (node == NULL)) { \
|
||||
/* Request a new group to the system. Non Lock Free path */ \
|
||||
M_ASSERT(mem->initial > 0); \
|
||||
node = M_C(name, _alloc_node)(mem->initial); \
|
||||
M_ASSERT(node != NULL); \
|
||||
M_ASSERT(!M_C(name, _slist_empty_p)(node->list)); \
|
||||
} \
|
||||
M_C(name, _slist_move)(mem->thread_data[id].free, node->list); \
|
||||
/* Push back the empty group */ \
|
||||
M_ASSERT (M_C(name, _slist_empty_p)(node->list)); \
|
||||
M_C(name, _lflist_push)(mem->empty, node, mem->gc_mem->thread_data[id].bkoff); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _del)(M_C(name, _t) mem, type_t *d, m_gc_tid_t id) \
|
||||
{ \
|
||||
M_C(name, _slist_node_ct) *snode; \
|
||||
M_ASSERT( d != NULL); \
|
||||
snode = M_TYPE_FROM_FIELD(M_C(name, _slist_node_ct), d, type_t, data); \
|
||||
M_C(name, _slist_push)(mem->thread_data[id].to_be_reclaimed, snode); \
|
||||
} \
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
/* Define the ID of a thread */
|
||||
typedef unsigned int m_gc_tid_t;
|
||||
|
||||
/* Define the age of a node */
|
||||
/* TODO: Compute if sufficient (worst cast ULONG_MAX is 32 bits) */
|
||||
typedef unsigned long m_gc_ticket_ct;
|
||||
typedef atomic_ulong m_gc_atomic_ticket_ct;
|
||||
|
||||
/* Define the Linked List of mempools that are registered in the GC */
|
||||
struct m_gc_s;
|
||||
typedef struct m_gM_CMEMP00L_list_s {
|
||||
struct m_gM_CMEMP00L_list_s *next;
|
||||
void (*gc_on_sleep)(struct m_gc_s *gc_mem,
|
||||
struct m_gM_CMEMP00L_list_s *data, m_gc_tid_t id,
|
||||
m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket);
|
||||
void *data;
|
||||
} m_gM_CMEMP00L_list_ct;
|
||||
|
||||
/* Define the Garbage collector thread data */
|
||||
typedef struct m_gc_lfmp_thread_s {
|
||||
m_gc_atomic_ticket_ct ticket;
|
||||
m_core_backoff_ct bkoff;
|
||||
M_CACHELINE_ALIGN(align1, atomic_ulong, m_core_backoff_ct);
|
||||
} m_gc_lfmp_thread_ct;
|
||||
|
||||
/* Define the Garbage collector coordinator */
|
||||
typedef struct m_gc_s {
|
||||
m_gc_atomic_ticket_ct ticket;
|
||||
m_gc_tid_t max_thread;
|
||||
genint_t thread_alloc;
|
||||
m_gc_lfmp_thread_ct *thread_data;
|
||||
m_gM_CMEMP00L_list_ct *mempool_list;
|
||||
} m_gc_t[1];
|
||||
|
||||
static inline void
|
||||
m_gc_init(m_gc_t gc_mem, size_t max_thread)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL);
|
||||
M_ASSERT(max_thread > 0 && max_thread < INT_MAX);
|
||||
|
||||
atomic_init(&gc_mem->ticket, 0UL);
|
||||
genint_init(gc_mem->thread_alloc, (unsigned int) max_thread);
|
||||
gc_mem->thread_data = M_MEMORY_REALLOC(m_gc_lfmp_thread_ct, NULL, max_thread);
|
||||
if (gc_mem->thread_data == NULL) {
|
||||
M_MEMORY_FULL(max_thread * sizeof(m_gc_lfmp_thread_ct));
|
||||
return;
|
||||
}
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
atomic_init(&gc_mem->thread_data[i].ticket, ULONG_MAX);
|
||||
m_core_backoff_init(gc_mem->thread_data[i].bkoff);
|
||||
}
|
||||
gc_mem->max_thread = (unsigned int) max_thread;
|
||||
gc_mem->mempool_list = NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_gc_clear(m_gc_t gc_mem)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
|
||||
for(m_gc_tid_t i = 0; i < gc_mem->max_thread;i++) {
|
||||
m_core_backoff_clear(gc_mem->thread_data[i].bkoff);
|
||||
}
|
||||
M_MEMORY_FREE(gc_mem->thread_data);
|
||||
gc_mem->thread_data = NULL;
|
||||
genint_clear(gc_mem->thread_alloc);
|
||||
}
|
||||
|
||||
static inline m_gc_tid_t
|
||||
m_gc_attach_thread(m_gc_t gc_mem)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
|
||||
unsigned id = genint_pop(gc_mem->thread_alloc);
|
||||
return M_ASSIGN_CAST(m_gc_tid_t, id);
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_gc_detach_thread(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
M_ASSERT(id < gc_mem->max_thread);
|
||||
M_ASSERT(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
|
||||
|
||||
genint_push(gc_mem->thread_alloc, id);
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_gc_awake(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
M_ASSERT(gc_mem != NULL && gc_mem->max_thread > 0);
|
||||
M_ASSERT(id < gc_mem->max_thread);
|
||||
M_ASSERT(atomic_load(&gc_mem->thread_data[id].ticket) == ULONG_MAX);
|
||||
|
||||
m_gc_ticket_ct t = atomic_fetch_add(&gc_mem->ticket, 1UL) + 1;
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, t);
|
||||
}
|
||||
|
||||
static inline m_gc_ticket_ct
|
||||
m_cmemp00l_gc_min_ticket(m_gc_t gc_mem)
|
||||
{
|
||||
m_gc_ticket_ct min = atomic_load(&gc_mem->thread_data[0].ticket);
|
||||
for(m_gc_tid_t i = 1; i < gc_mem->max_thread; i++) {
|
||||
m_gc_ticket_ct t = atomic_load(&gc_mem->thread_data[i].ticket);
|
||||
min = M_MIN(t, min);
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_gc_sleep(m_gc_t gc_mem, m_gc_tid_t id)
|
||||
{
|
||||
/* Increase life time of the thread */
|
||||
m_gc_ticket_ct t = atomic_fetch_add(&gc_mem->ticket, 1UL);
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, t+1);
|
||||
const m_gc_ticket_ct min_ticket = m_cmemp00l_gc_min_ticket(gc_mem);
|
||||
/* Iterate over all registered mempools */
|
||||
m_gM_CMEMP00L_list_ct *it = gc_mem->mempool_list;
|
||||
|
||||
while (it) {
|
||||
/* Perform a garbage collect of the mempool */
|
||||
it->gc_on_sleep(gc_mem, it, id, t, min_ticket);
|
||||
/* Next mempool to scan for GC */
|
||||
it = it->next;
|
||||
}
|
||||
/* Sleep the thread */
|
||||
atomic_store(&gc_mem->thread_data[id].ticket, ULONG_MAX);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
/* */
|
||||
/* Variable Length Array MEMPOOL */
|
||||
/* */
|
||||
/***********************************************************************/
|
||||
|
||||
M_CMEMP00L_DEF_SINGLY_LIST(m_vlapool, char)
|
||||
M_CMEMP00L_DEF_LF_QUEUE(m_vlapool, char)
|
||||
M_CMEMP00L_DEF_SYSTEM_ALLOC(m_vlapool, char)
|
||||
|
||||
typedef struct m_vlapool_lfmp_thread_s {
|
||||
m_vlapool_slist_ct to_be_reclaimed;
|
||||
M_CACHELINE_ALIGN(align1, m_vlapool_slist_ct);
|
||||
} m_vlapool_lfmp_thread_ct;
|
||||
|
||||
static inline void
|
||||
m_vlapool_lfmp_thread_init(m_vlapool_lfmp_thread_ct *t)
|
||||
{
|
||||
m_vlapool_slist_init(t->to_be_reclaimed);
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_vlapool_lfmp_thread_clear(m_vlapool_lfmp_thread_ct *t)
|
||||
{
|
||||
M_ASSERT(m_vlapool_slist_empty_p(t->to_be_reclaimed));
|
||||
m_vlapool_slist_clear(t->to_be_reclaimed);
|
||||
}
|
||||
|
||||
typedef struct m_vlapool_s {
|
||||
m_vlapool_lflist_ct to_be_reclaimed;
|
||||
m_vlapool_lflist_ct empty;
|
||||
m_vlapool_lfmp_thread_ct *thread_data;
|
||||
m_gM_CMEMP00L_list_ct mvla_node;
|
||||
struct m_gc_s *gc_mem;
|
||||
} m_vlapool_t[1];
|
||||
|
||||
/* Garbage collect of the nodes of the vla mempool on sleep */
|
||||
static inline void
|
||||
m_cmemp00l_vlapool_on_sleep(m_gc_t gc_mem, m_gM_CMEMP00L_list_ct *data,
|
||||
m_gc_tid_t id, m_gc_ticket_ct ticket, m_gc_ticket_ct min_ticket)
|
||||
{
|
||||
/* Get back the mempool from the node */
|
||||
struct m_vlapool_s *vlapool =
|
||||
M_TYPE_FROM_FIELD(struct m_vlapool_s, data, m_gM_CMEMP00L_list_ct, mvla_node);
|
||||
|
||||
/* Move the local nodes of the vlapool to be reclaimed to the thread into the global pool */
|
||||
if (!m_vlapool_slist_empty_p(vlapool->thread_data[id].to_be_reclaimed)) {
|
||||
m_vlapool_lf_node_t *node;
|
||||
/* Get a new empty group of nodes */
|
||||
node = m_vlapool_lflist_pop(vlapool->empty, gc_mem->thread_data[id].bkoff);
|
||||
if (M_UNLIKELY (node == NULL)) {
|
||||
/* Fail to get an empty group of node.
|
||||
Alloc a new one from the system */
|
||||
node = m_vlapool_alloc_node(0);
|
||||
M_ASSERT(node != NULL);
|
||||
}
|
||||
M_ASSERT(m_vlapool_slist_empty_p(node->list));
|
||||
m_vlapool_slist_move(node->list, vlapool->thread_data[id].to_be_reclaimed);
|
||||
atomic_store_explicit(&node->cpt, ticket, memory_order_relaxed);
|
||||
m_vlapool_lflist_push(vlapool->to_be_reclaimed, node, gc_mem->thread_data[id].bkoff);
|
||||
}
|
||||
|
||||
/* Perform a GC of the freelist of nodes */
|
||||
while (true) {
|
||||
m_vlapool_lf_node_t *node;
|
||||
node = m_vlapool_lflist_pop_if(vlapool->to_be_reclaimed,
|
||||
min_ticket, gc_mem->thread_data[id].bkoff);
|
||||
if (node == NULL) break;
|
||||
// No reuse of VLA nodes. Free physically the node back to the system
|
||||
m_vlapool_slist_clear(node->list);
|
||||
// Add back the empty group of nodes
|
||||
m_vlapool_slist_init(node->list);
|
||||
m_vlapool_lflist_push(vlapool->empty, node, gc_mem->thread_data[id].bkoff);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_vlapool_init(m_vlapool_t mem, m_gc_t gc_mem)
|
||||
{
|
||||
const size_t max_thread = gc_mem->max_thread;
|
||||
|
||||
/* Initialize the thread data of the vlapool */
|
||||
mem->thread_data = M_MEMORY_REALLOC(m_vlapool_lfmp_thread_ct, NULL, max_thread);
|
||||
if (mem->thread_data == NULL) {
|
||||
M_MEMORY_FULL(max_thread * sizeof(m_vlapool_lfmp_thread_ct));
|
||||
return;
|
||||
}
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
m_vlapool_lfmp_thread_init(&mem->thread_data[i]);
|
||||
}
|
||||
|
||||
/* Initialize the lists */
|
||||
m_vlapool_lflist_init(mem->to_be_reclaimed, m_vlapool_alloc_node(0));
|
||||
m_vlapool_lflist_init(mem->empty, m_vlapool_alloc_node(0));
|
||||
|
||||
/* Register the mempool in the GC */
|
||||
mem->mvla_node.gc_on_sleep = m_cmemp00l_vlapool_on_sleep;
|
||||
mem->mvla_node.next = gc_mem->mempool_list;
|
||||
gc_mem->mempool_list = &mem->mvla_node;
|
||||
mem->gc_mem = gc_mem;
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_vlapool_clear(m_vlapool_t mem)
|
||||
{
|
||||
const unsigned max_thread = mem->gc_mem->max_thread;
|
||||
for(unsigned i = 0; i < max_thread;i++) {
|
||||
m_vlapool_lfmp_thread_clear(&mem->thread_data[i]);
|
||||
}
|
||||
M_MEMORY_FREE(mem->thread_data);
|
||||
mem->thread_data = NULL;
|
||||
m_vlapool_lflist_clear(mem->empty);
|
||||
M_ASSERT(m_vlapool_lflist_empty_p(mem->to_be_reclaimed));
|
||||
m_vlapool_lflist_clear(mem->to_be_reclaimed);
|
||||
/* TODO: Unregister from the GC? */
|
||||
}
|
||||
|
||||
static inline void *
|
||||
m_vlapool_new(m_vlapool_t mem, m_gc_tid_t id, size_t size)
|
||||
{
|
||||
M_ASSERT(mem != NULL && mem->gc_mem != NULL);
|
||||
M_ASSERT(id < mem->gc_mem->max_thread);
|
||||
M_ASSERT( atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
|
||||
|
||||
// Nothing to do with theses parameters yet
|
||||
(void) mem;
|
||||
(void) id;
|
||||
|
||||
// Ensure the size is big enough to also represent a node
|
||||
size += offsetof(struct m_vlapool_slist_node_s, data);
|
||||
|
||||
// Simply wrap around a system call to get the memory
|
||||
char *ptr = M_MEMORY_REALLOC(char, NULL, size);
|
||||
return (ptr == NULL) ? NULL : M_ASSIGN_CAST(void *, ptr + offsetof(struct m_vlapool_slist_node_s, data));
|
||||
}
|
||||
|
||||
static inline void
|
||||
m_vlapool_del(m_vlapool_t mem, void *d, m_gc_tid_t id)
|
||||
{
|
||||
M_ASSERT(mem != NULL && mem->gc_mem != NULL);
|
||||
M_ASSERT(id < mem->gc_mem->max_thread);
|
||||
M_ASSERT(atomic_load(&mem->gc_mem->thread_data[id].ticket) != ULONG_MAX);
|
||||
M_ASSERT(d != NULL);
|
||||
|
||||
// Get back the pointer to a struct m_vlapool_slist_node_s.
|
||||
d = M_ASSIGN_CAST(void *, M_ASSIGN_CAST(char *, d) - offsetof(struct m_vlapool_slist_node_s, data));
|
||||
m_vlapool_slist_node_ct *snode = M_ASSIGN_CAST(m_vlapool_slist_node_ct *, d);
|
||||
// Push the logicaly free memory into the list of the nodes to be reclaimed.
|
||||
m_vlapool_slist_push(mem->thread_data[id].to_be_reclaimed, snode);
|
||||
}
|
||||
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
#endif
|
||||
+909
@@ -0,0 +1,909 @@
|
||||
/*
|
||||
* M*LIB - Basic Protected Concurrent module over container.
|
||||
*
|
||||
* Copyright (c) 2017-2021, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef MSTARLIB_CONCURRENT_H
|
||||
#define MSTARLIB_CONCURRENT_H
|
||||
|
||||
#include "m-core.h"
|
||||
#include "m-mutex.h"
|
||||
#include "m-atomic.h"
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
based on the given container.
|
||||
USAGE: CONCURRENT_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define CONCURRENT_DEF(name, ...) \
|
||||
CONCURRENT_DEF_AS(name, M_C(name,_t), __VA_ARGS__)
|
||||
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
based on the given container as the given name name_t
|
||||
USAGE: CONCURRENT_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
|
||||
#define CONCURRENT_DEF_AS(name, name_t, ...) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_C0NCURRENT_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \
|
||||
(name, __VA_ARGS__, name_t ))) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
based on its given container. Operations that perform only read of the container
|
||||
can be done in parallel.
|
||||
USAGE: CONCURRENT_RP_DEF(name, type [, oplist_of_the_type]) */
|
||||
#define CONCURRENT_RP_DEF(name, ...) \
|
||||
CONCURRENT_RP_DEF_AS(name, M_C(name,_t), __VA_ARGS__)
|
||||
|
||||
|
||||
/* Define a protected concurrent container and its associated functions
|
||||
as the given name name_t
|
||||
based on its given container. Operations that perform only read of the container
|
||||
can be done in parallel.
|
||||
USAGE: CONCURRENT_RP_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
|
||||
#define CONCURRENT_RP_DEF_AS(name, name_t, ...) \
|
||||
M_BEGIN_PROTECTED_CODE \
|
||||
M_C0NCURRENT_RP_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t ), \
|
||||
(name, __VA_ARGS__, name_t ))) \
|
||||
M_END_PROTECTED_CODE
|
||||
|
||||
|
||||
/* Define the oplist of a protected concurrent container given its name and its oplist.
|
||||
USAGE: CONCURRENT_OPLIST(name[, oplist of the type]) */
|
||||
#define CONCURRENT_OPLIST(...) \
|
||||
M_C0NCURRENT_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||
((__VA_ARGS__, M_DEFAULT_OPLIST), \
|
||||
(__VA_ARGS__ )))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
/* Deferred evaluation for the oplist definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_C0NCURRENT_OPLIST_P1(arg) M_C0NCURRENT_OPLIST_P2 arg
|
||||
|
||||
/* Validation of the given oplist */
|
||||
#define M_C0NCURRENT_OPLIST_P2(name, oplist) \
|
||||
M_IF_OPLIST(oplist)(M_C0NCURRENT_OPLIST_P3, M_C0NCURRENT_OPLIST_FAILURE)(name, oplist)
|
||||
|
||||
/* Prepare a clean compilation failure */
|
||||
#define M_C0NCURRENT_OPLIST_FAILURE(name, oplist) \
|
||||
((M_LIB_ERROR(ARGUMENT_OF_CONCURRENT_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
||||
|
||||
/* OPLIST definition
|
||||
GET_KEY is not present as its interface is not compatible with a concurrent
|
||||
container (_get returns a pointer to an internal data, data that may be
|
||||
destroyed by another thread).
|
||||
*/
|
||||
#define M_C0NCURRENT_OPLIST_P3(name, oplist) \
|
||||
(M_IF_METHOD(INIT, oplist)(INIT(M_C(name, _init)),) \
|
||||
,M_IF_METHOD(INIT_SET, oplist)(INIT_SET(M_C(name, _init_set)),) \
|
||||
,M_IF_METHOD(SET, oplist)(SET(M_C(name, _set)),) \
|
||||
,M_IF_METHOD(CLEAR, oplist)(CLEAR(M_C(name, _clear)),) \
|
||||
,M_IF_METHOD(INIT_MOVE, oplist)(INIT_MOVE(M_C(name, _init_move)),) \
|
||||
,M_IF_METHOD(MOVE, oplist)(MOVE(M_C(name, _move)),) \
|
||||
,M_IF_METHOD(SWAP,oplist)(SWAP(M_C(name, _swap)),) \
|
||||
,NAME(name) \
|
||||
,TYPE(M_C(name,_ct)) \
|
||||
,SUBTYPE(M_C(name, _subtype_ct)) \
|
||||
,OPLIST(oplist) \
|
||||
,M_IF_METHOD(EMPTY_P, oplist)(EMPTY_P(M_C(name,_empty_p)),) \
|
||||
,M_IF_METHOD(GET_SIZE, oplist)(GET_SIZE(M_C(name,_size)),) \
|
||||
,M_IF_METHOD(RESET, oplist)(RESET(M_C(name,_reset)),) \
|
||||
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_TYPE(M_GET_KEY_TYPE oplist),) \
|
||||
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_TYPE(M_GET_VALUE_TYPE oplist),) \
|
||||
,M_IF_METHOD(KEY_TYPE, oplist)(KEY_OPLIST(M_GET_KEY_OPLIST oplist),) \
|
||||
,M_IF_METHOD(VALUE_TYPE, oplist)(VALUE_OPLIST(M_GET_VALUE_OPLIST oplist), ) \
|
||||
,M_IF_METHOD(SET_KEY, oplist)(SET_KEY(M_C(name, _set_at)),) \
|
||||
,M_IF_METHOD(ERASE_KEY, oplist)(ERASE_KEY(M_C(name, _erase)),) \
|
||||
,M_IF_METHOD(PUSH, oplist)(PUSH(M_C(name,_push)),) \
|
||||
,M_IF_METHOD(POP, oplist)(POP(M_C(name,_pop)),) \
|
||||
,M_IF_METHOD(PUSH_MOVE, oplist)(PUSH_MOVE(M_C(name,_push_move)),) \
|
||||
,M_IF_METHOD(POP_MOVE, oplist)(POP_MOVE(M_C(name,_pop_move)),) \
|
||||
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_C(name, _get_str)),) \
|
||||
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_C(name, _parse_str)),) \
|
||||
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_C(name, _out_str)),) \
|
||||
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_C(name, _in_str)),) \
|
||||
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_C(name, _out_serial)),) \
|
||||
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_C(name, _in_serial)),) \
|
||||
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_C(name, _equal_p)),) \
|
||||
,M_IF_METHOD(HASH, oplist)(HASH(M_C(name, _hash)),) \
|
||||
,M_IF_METHOD(NEW, oplist)(NEW(M_GET_NEW oplist),) \
|
||||
,M_IF_METHOD(REALLOC, oplist)(REALLOC(M_GET_REALLOC oplist),) \
|
||||
,M_IF_METHOD(DEL, oplist)(DEL(M_GET_DEL oplist),) \
|
||||
)
|
||||
|
||||
/* Internal contract
|
||||
NOTE: Can't check too much without locking the container itself
|
||||
*/
|
||||
#define M_C0NCURRENT_CONTRACT(c) do { \
|
||||
M_ASSERT ((c) != NULL); \
|
||||
M_ASSERT ( (c)->self == (c)); \
|
||||
} while (0)
|
||||
|
||||
/* Deferred evaluation for the concurrent definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_C0NCURRENT_DEF_P1(arg) M_C0NCURRENT_DEF_P2 arg
|
||||
|
||||
/* Validate the value oplist before going further */
|
||||
#define M_C0NCURRENT_DEF_P2(name, type, oplist, concurrent_t) \
|
||||
M_IF_OPLIST(oplist)(M_C0NCURRENT_DEF_P3, M_C0NCURRENT_DEF_FAILURE)(name, type, oplist, concurrent_t)
|
||||
|
||||
/* Stop processing with a compilation failure */
|
||||
#define M_C0NCURRENT_DEF_FAILURE(name, type, oplist, concurrent_t) \
|
||||
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist))
|
||||
|
||||
/* Internal concurrent definition
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
|
||||
*/
|
||||
#define M_C0NCURRENT_DEF_P3(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
/* Define a concurrent container using a lock */ \
|
||||
typedef struct M_C(name, _s) { \
|
||||
struct M_C(name, _s) *self; \
|
||||
m_mutex_t lock; \
|
||||
m_cond_t there_is_data; /* condition raised when there is data */ \
|
||||
type data; \
|
||||
} concurrent_t[1]; \
|
||||
\
|
||||
/* Define alias for pointer types */ \
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
/* Internal types for oplist */ \
|
||||
typedef concurrent_t M_C(name, _ct); \
|
||||
typedef type M_C(name, _subtype_ct); \
|
||||
\
|
||||
/* Don't define iterator as it cannot be reliable in a concurrent type */ \
|
||||
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
||||
\
|
||||
/* Define the internal services used for the lock strategy */ \
|
||||
\
|
||||
/* Initial the fields of the concurrent object not associated to the \
|
||||
sub-container. */ \
|
||||
static inline void \
|
||||
M_C(name, _internal_init)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
\
|
||||
/* Clear the fields of the concurrent object not associated to the \
|
||||
sub-container. */ \
|
||||
static inline void \
|
||||
M_C(name, _internal_clear)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_clear(out->lock); \
|
||||
m_cond_clear(out->there_is_data); \
|
||||
out->self = NULL; \
|
||||
} \
|
||||
\
|
||||
/* Get the read lock. Multiple threads can get it, but only for reading. \
|
||||
write lock is exclusive. \
|
||||
NOTE: This instance doesn't implement the read/write strategy, \
|
||||
and only get the lock */ \
|
||||
static inline void \
|
||||
M_C(name, _read_lock)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
} \
|
||||
\
|
||||
/* Free the read lock. See above. \
|
||||
NOTE: This instance doesn't implement the read/write strategy, \
|
||||
and only get the lock */ \
|
||||
static inline void \
|
||||
M_C(name, _read_unlock)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
} \
|
||||
\
|
||||
/* Wait for a thread pushing some data in the container. \
|
||||
CONSTRAINT: the read lock shall be get before calling this service */ \
|
||||
static inline void \
|
||||
M_C(name, _read_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_cond_wait(out->self->there_is_data, out->self->lock); \
|
||||
} \
|
||||
\
|
||||
/* Get the write lock. Only one threads can get it, and no other threads \
|
||||
can get the read lock too. \
|
||||
NOTE: This instance doesn't implement the read/write strategy, \
|
||||
and only get the lock */ \
|
||||
static inline void \
|
||||
M_C(name, _write_lock)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
} \
|
||||
\
|
||||
/* Free the write lock. \
|
||||
NOTE: This instance doesn't implement the read/write strategy, \
|
||||
and only get the lock */ \
|
||||
static inline void \
|
||||
M_C(name, _write_unlock)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
/* Wait for a thread pushing some data in the container. \
|
||||
CONSTRAINT: the write lock shall be get before calling this service */ \
|
||||
static inline void \
|
||||
M_C(name, _write_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_cond_wait(out->self->there_is_data, out->self->lock); \
|
||||
} \
|
||||
\
|
||||
/* Wait to all threads that some data are available in the container. \
|
||||
CONSTRAINT: the write lock shall be get before calling this service */ \
|
||||
static inline void \
|
||||
M_C(name, _write_signal)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
/* We need to signal this to ALL waiting threads as multiple threads \
|
||||
may wait on a some data of this container. */ \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
} \
|
||||
\
|
||||
M_C0NCURRENT_DEF_FUNC_P3(name, type, oplist, concurrent_t)
|
||||
|
||||
|
||||
/* Internal definition of the functions commons to concurrent and rp-concurrent
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
|
||||
A function is defined only if the underlying container exports the needed
|
||||
services. It is usually one service declared per service exported.
|
||||
*/
|
||||
#define M_C0NCURRENT_DEF_FUNC_P3(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
M_IF_METHOD(INIT, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _init)(concurrent_t out) \
|
||||
{ \
|
||||
M_C(name, _internal_init)(out); \
|
||||
M_CALL_INIT(oplist, out->data); \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(INIT_SET, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _init_set)(concurrent_t out, concurrent_t const src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(src); \
|
||||
M_ASSERT (out != src); \
|
||||
M_C(name, _internal_init)(out); \
|
||||
M_C(name, _read_lock)(src); \
|
||||
M_CALL_INIT_SET(oplist, out->data, src->data); \
|
||||
M_C(name, _read_unlock)(src); \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SET, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _set)(concurrent_t out, concurrent_t const src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
if (M_UNLIKELY (out == src)) return; \
|
||||
/* Need to order the locks in a total way to avoid lock deadlock. \
|
||||
Indeed, two call to _set can be done in two threads with : \
|
||||
T1: A := B \
|
||||
T2: B := A \
|
||||
If we lock first the mutex of out, then the src, it could be possible \
|
||||
in the previous scenario that both mutexs are locked: T1 has locked A \
|
||||
and T2 has locked B, and T1 is waiting for locking B, and T2 is waiting \
|
||||
for locking A, resulting in a deadlock. \
|
||||
To avoid this problem, we **always** lock the mutex which address is \
|
||||
the lowest. */ \
|
||||
if (out < src) { \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_C(name, _read_lock)(src); \
|
||||
} else { \
|
||||
M_C(name, _read_lock)(src); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
} \
|
||||
M_CALL_SET(oplist, out->data, src->data); \
|
||||
if (out < src) { \
|
||||
M_C(name, _read_lock)(src); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} else { \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
M_C(name, _read_unlock)(src); \
|
||||
} \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(CLEAR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _clear)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
/* No need to lock. A clear is supposed to be called when all operations \
|
||||
of the container in other threads are terminated */ \
|
||||
M_CALL_CLEAR(oplist, out->data); \
|
||||
M_C(name, _internal_clear)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(INIT_MOVE, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _init_move)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(src); \
|
||||
M_ASSERT (out != src); \
|
||||
/* No need to lock 'src' ? */ \
|
||||
M_C(name, _internal_init)(out); \
|
||||
M_CALL_INIT_MOVE(oplist, out->data, src->data); \
|
||||
M_C(name, _internal_clear)(src); \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(MOVE, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _move)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C0NCURRENT_CONTRACT(src); \
|
||||
/* No need to lock 'src' ? */ \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_MOVE(oplist, out->data, src->data); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
M_C(name, _internal_clear)(src); \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SWAP, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _swap)(concurrent_t out, concurrent_t src) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C0NCURRENT_CONTRACT(src); \
|
||||
if (M_UNLIKELY (out == src)) return; \
|
||||
/* See comment above */ \
|
||||
if (out < src) { \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_C(name, _write_lock)(src); \
|
||||
} else { \
|
||||
M_C(name, _write_lock)(src); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
} \
|
||||
M_CALL_SWAP(oplist, out->data, src->data); \
|
||||
if (out < src) { \
|
||||
M_C(name, _write_unlock)(src); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} else { \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
M_C(name, _write_unlock)(src); \
|
||||
} \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(RESET, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _reset)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_RESET(oplist, out->data); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
static inline void M_ATTR_DEPRECATED \
|
||||
M_C(name, _clean)(concurrent_t out) \
|
||||
{ \
|
||||
M_C(name,_reset)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(EMPTY_P, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _empty_p)(concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
bool b = M_CALL_EMPTY_P(oplist, out->data); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_SIZE, oplist)( \
|
||||
static inline size_t \
|
||||
M_C(name, _size)(concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
size_t r = M_CALL_GET_SIZE(oplist, out->data); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SET_KEY, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _set_at)(concurrent_t out, M_GET_KEY_TYPE oplist const key, M_GET_VALUE_TYPE oplist const data) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_SET_KEY(oplist, out->data, key, data); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_KEY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _get_copy)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (out_data != NULL); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
|
||||
if (p != NULL) { \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
} \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return p != NULL; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(SAFE_GET_KEY, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _safe_get_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (out_data != NULL); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_SAFE_GET_KEY(oplist, out->data, key); \
|
||||
M_ASSERT (p != NULL); \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
static inline M_ATTR_DEPRECATED void \
|
||||
M_C(name, _get_at_copy)(M_GET_VALUE_TYPE oplist *out_data, concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
M_C(name, _safe_get_copy)(out_data, out, key); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(ERASE_KEY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _erase)(concurrent_t out, M_GET_KEY_TYPE oplist const key) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
bool b = M_CALL_ERASE_KEY(oplist, out->data, key); \
|
||||
/* We suppose that the container has 'infinite' capacity, so \
|
||||
we won't signal that a free space has been created */ \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PUSH, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _push)(concurrent_t out, M_GET_SUBTYPE oplist const data) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_PUSH(oplist, out->data, data); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(POP, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _pop)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_POP(oplist, p, out->data); \
|
||||
/* See comment above */ \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PUSH_MOVE, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _push_move)(concurrent_t out, M_GET_SUBTYPE oplist *data) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_PUSH_MOVE(oplist, out->data, data); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(POP_MOVE, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _pop_move)(M_GET_SUBTYPE oplist *p, concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
M_CALL_POP_MOVE(oplist, p, out->data); \
|
||||
/* See comment above */ \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _get_str)(string_t str, concurrent_t const out, bool a) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
M_CALL_GET_STR(oplist, str, out->data, a); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(OUT_STR, oplist)( \
|
||||
static inline void \
|
||||
M_C(name, _out_str)(FILE *f, concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
M_CALL_OUT_STR(oplist, f, out->data); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(PARSE_STR, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _parse_str)(concurrent_t out, const char str[], const char **e) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
bool b = M_CALL_PARSE_STR(oplist, out->data, str, e); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(IN_STR, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _in_str)(concurrent_t out, FILE *f) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
bool b = M_CALL_IN_STR(oplist, out->data, f); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _out_serial)(m_serial_write_t f, concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
m_serial_return_code_t r = M_CALL_OUT_SERIAL(oplist, f, out->data); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(IN_SERIAL, oplist)( \
|
||||
static inline m_serial_return_code_t \
|
||||
M_C(name, _in_serial)(concurrent_t out, m_serial_read_t f) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _write_lock)(out); \
|
||||
m_serial_return_code_t r = M_CALL_IN_SERIAL(oplist, out->data, f); \
|
||||
M_C(name, _write_signal)(out); \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return r; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(EQUAL, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _equal_p)(concurrent_t const out1, concurrent_t const out2) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out1); \
|
||||
M_C0NCURRENT_CONTRACT(out2); \
|
||||
if (M_UNLIKELY (out1 == out2)) return true; \
|
||||
/* See comment above on mutal mutexs */ \
|
||||
if (out1 < out2) { \
|
||||
M_C(name, _read_lock)(out1); \
|
||||
M_C(name, _read_lock)(out2); \
|
||||
} else { \
|
||||
M_C(name, _read_lock)(out2); \
|
||||
M_C(name, _read_lock)(out1); \
|
||||
} \
|
||||
bool b = M_CALL_EQUAL(oplist, out1->data, out2->data); \
|
||||
if (out1 < out2) { \
|
||||
M_C(name, _read_unlock)(out2); \
|
||||
M_C(name, _read_unlock)(out1); \
|
||||
} else { \
|
||||
M_C(name, _read_unlock)(out1); \
|
||||
M_C(name, _read_unlock)(out2); \
|
||||
} \
|
||||
return b; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(GET_KEY, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _get_blocking)(M_GET_VALUE_TYPE oplist *out_data, const concurrent_t out, M_GET_KEY_TYPE oplist const key, bool blocking) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (out_data != NULL); \
|
||||
bool ret = false; \
|
||||
M_C(name, _read_lock)(out); \
|
||||
while (true) { \
|
||||
M_GET_VALUE_TYPE oplist *p = M_CALL_GET_KEY(oplist, out->data, key); \
|
||||
if (p != NULL) { \
|
||||
M_CALL_SET(M_GET_VALUE_OPLIST oplist, *out_data, *p); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
/* No data: wait for a write to signal some data */ \
|
||||
M_C(name, _read_wait)(out); \
|
||||
} \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD2(POP, EMPTY_P, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _pop_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (p != NULL); \
|
||||
bool ret = false; \
|
||||
M_C(name, _write_lock)(out); \
|
||||
while (true) { \
|
||||
if (!M_CALL_EMPTY_P(oplist, out->data)) { \
|
||||
M_CALL_POP(oplist, p, out->data); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
/* No data: wait for a write to signal some data */ \
|
||||
M_C(name, _write_wait)(out); \
|
||||
} \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD2(POP_MOVE, EMPTY_P, oplist)( \
|
||||
static inline bool \
|
||||
M_C(name, _pop_move_blocking)(M_GET_SUBTYPE oplist *p, concurrent_t out, bool blocking) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_ASSERT (p != NULL); \
|
||||
bool ret = false; \
|
||||
M_C(name, _write_lock)(out); \
|
||||
while (true) { \
|
||||
if (!M_CALL_EMPTY_P(oplist, out->data)) { \
|
||||
M_CALL_POP_MOVE(oplist, p, out->data); \
|
||||
ret = true; \
|
||||
break; \
|
||||
} \
|
||||
if (blocking == false) break; \
|
||||
/* No data: wait for a write to signal some data */ \
|
||||
M_C(name, _write_wait)(out); \
|
||||
} \
|
||||
M_C(name, _write_unlock)(out); \
|
||||
return ret; \
|
||||
} \
|
||||
,) \
|
||||
\
|
||||
M_IF_METHOD(HASH, oplist)( \
|
||||
static inline size_t \
|
||||
M_C(name, _hash)(concurrent_t const out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
M_C(name, _read_lock)(out); \
|
||||
size_t h = M_CALL_HASH(oplist, out->data); \
|
||||
M_C(name, _read_unlock)(out); \
|
||||
/* The hash is unchanged by the concurrent container */ \
|
||||
return h; \
|
||||
} \
|
||||
,) \
|
||||
|
||||
|
||||
|
||||
/* Deferred evaluation for the RP concurrent definition,
|
||||
so that all arguments are evaluated before further expansion */
|
||||
#define M_C0NCURRENT_RP_DEF_P1(arg) M_C0NCURRENT_RP_DEF_P2 arg
|
||||
|
||||
/* Validate the value oplist before going further */
|
||||
#define M_C0NCURRENT_RP_DEF_P2(name, type, oplist, concurrent_t) \
|
||||
M_IF_OPLIST(oplist)(M_C0NCURRENT_RP_DEF_P3, M_C0NCURRENT_RP_DEF_FAILURE)(name, type, oplist, concurrent_t)
|
||||
|
||||
/* Stop processing with a compilation failure */
|
||||
#define M_C0NCURRENT_RP_DEF_FAILURE(name, type, oplist, concurrent_t) \
|
||||
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(CONCURRENT_RP_DEF): the given argument is not a valid oplist: " M_AS_STR(oplist))
|
||||
|
||||
/* Internal RP concurrent definition
|
||||
- name: prefix to be used
|
||||
- type: type of the sub container
|
||||
- oplist: oplist of the type of the sub container
|
||||
- concurrent_t: alias for M_C(name, _t) [ type of the container ]
|
||||
*/
|
||||
#define M_C0NCURRENT_RP_DEF_P3(name, type, oplist, concurrent_t) \
|
||||
\
|
||||
typedef struct M_C(name, _s) { \
|
||||
struct M_C(name, _s) *self; \
|
||||
m_mutex_t lock; \
|
||||
m_cond_t rw_done; \
|
||||
size_t read_count; \
|
||||
bool writer_waiting; \
|
||||
m_cond_t there_is_data; /* condition raised when there is data */ \
|
||||
type data; \
|
||||
} concurrent_t[1]; \
|
||||
\
|
||||
typedef struct M_C(name, _s) *M_C(name, _ptr); \
|
||||
typedef const struct M_C(name, _s) *M_C(name, _srcptr); \
|
||||
\
|
||||
typedef type M_C(name, _subtype_ct); \
|
||||
\
|
||||
/* Define the internal services for the lock strategy */ \
|
||||
static inline void \
|
||||
M_C(name, _internal_init)(concurrent_t out) \
|
||||
{ \
|
||||
m_mutex_init(out->lock); \
|
||||
m_cond_init(out->rw_done); \
|
||||
m_cond_init(out->there_is_data); \
|
||||
out->self = out; \
|
||||
out->read_count = 0; \
|
||||
out->writer_waiting = false; \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _internal_clear)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_clear(out->lock); \
|
||||
m_cond_clear(out->rw_done); \
|
||||
m_cond_clear(out->there_is_data); \
|
||||
out->self = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_lock)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
struct M_C(name, _s) *self = out->self; \
|
||||
m_mutex_lock (self->lock); \
|
||||
while (self->writer_waiting == true) { \
|
||||
m_cond_wait(self->rw_done, self->lock); \
|
||||
} \
|
||||
self->read_count ++; \
|
||||
m_mutex_unlock (self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_unlock)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
struct M_C(name, _s) *self = out->self; \
|
||||
m_mutex_lock (self->lock); \
|
||||
self->read_count --; \
|
||||
if (self->read_count == 0) { \
|
||||
m_cond_broadcast (self->rw_done); \
|
||||
} \
|
||||
m_mutex_unlock (self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_lock)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
while (out->writer_waiting == true) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
out->writer_waiting = true; \
|
||||
while (out->read_count > 0) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_unlock)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
out->writer_waiting = false; \
|
||||
m_cond_broadcast (out->rw_done); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _read_wait)(const concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
struct M_C(name, _s) *self = out->self; \
|
||||
M_ASSERT (self == out); \
|
||||
m_mutex_lock (out->self->lock); \
|
||||
self->read_count --; \
|
||||
if (self->read_count == 0) { \
|
||||
m_cond_broadcast (self->rw_done); \
|
||||
} \
|
||||
m_cond_wait(self->there_is_data, self->lock); \
|
||||
while (self->writer_waiting == true) { \
|
||||
m_cond_wait(self->rw_done, self->lock); \
|
||||
} \
|
||||
self->read_count ++; \
|
||||
m_mutex_unlock (out->self->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_wait)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
out->writer_waiting = false; \
|
||||
m_cond_broadcast (out->rw_done); \
|
||||
m_cond_wait(out->there_is_data, out->lock); \
|
||||
while (out->writer_waiting == true) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
out->writer_waiting = true; \
|
||||
while (out->read_count > 0) { \
|
||||
m_cond_wait(out->rw_done, out->lock); \
|
||||
} \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
static inline void \
|
||||
M_C(name, _write_signal)(concurrent_t out) \
|
||||
{ \
|
||||
M_C0NCURRENT_CONTRACT(out); \
|
||||
m_mutex_lock (out->lock); \
|
||||
m_cond_broadcast(out->there_is_data); \
|
||||
m_mutex_unlock (out->lock); \
|
||||
} \
|
||||
\
|
||||
M_C0NCURRENT_DEF_FUNC_P3(name, type, oplist, concurrent_t)
|
||||
|
||||
|
||||
#endif
|
||||
Loaded 100 of 167 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user