mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
1369 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 933c2ebeac | |||
| db3e813aa6 | |||
| 9c95a48fe6 | |||
| cb20f7e7df | |||
| 18b9168eec | |||
| 241ce7304c | |||
| 5dbe96da96 | |||
| 40b397a3d4 | |||
| 5510089d45 | |||
| 62a57907a7 | |||
| becbe99e81 | |||
| bd3d67e889 | |||
| a89d57d0d4 | |||
| e04d400c64 | |||
| 1075e8609c | |||
| 3fde8a4eac | |||
| 62bdb9a2f7 | |||
| 2fbacb0058 | |||
| 509066f06a | |||
| 070f0b26a3 | |||
| 1a195623a5 | |||
| 5f7a56e7f1 | |||
| 5e60f0482d | |||
| e6c90b8efb | |||
| 7c450fbb70 | |||
| 4e1e002cb0 | |||
| de196dd7a3 | |||
| 08cb8dd81c | |||
| 57d54792ba | |||
| 79879802f9 | |||
| fbe955e9a4 | |||
| a49ac04046 | |||
| 49c7654a70 | |||
| 3e777c1759 | |||
| a410c723c8 | |||
| f91a1ae07e | |||
| c3954b1fb8 | |||
| ce74ece545 | |||
| dd4dce848e | |||
| e8f370b085 | |||
| 645033a8c8 | |||
| 3b5ceeb80d | |||
| 82433a67f9 | |||
| 8c1bfe782b | |||
| 730939f01c | |||
| b169dc2ea7 | |||
| 1aa1d537fe | |||
| c122462278 | |||
| b6b338bea9 | |||
| f72636b5b0 | |||
| f6a2bac11b | |||
| e4ddc5446b | |||
| e0e0aa9e7b | |||
| 9f0a2e0e86 | |||
| 203c03e90b | |||
| 6698eb96b9 | |||
| 30f7de387c | |||
| e0788507fe | |||
| e4740c87fb | |||
| 5beef701e7 | |||
| c6f9c93c33 | |||
| 3a93e45dd1 | |||
| 6db8ceb46c | |||
| 23651f0d41 | |||
| 7e1893db42 | |||
| b4b1927b0f | |||
| e275589832 | |||
| 69ee84973a | |||
| 76bed68ec3 | |||
| 003d970cd2 | |||
| 4aaf3f030c | |||
| 2763f7e719 | |||
| 39035c39be | |||
| ac179ff40f | |||
| b41bece32c | |||
| 9cb0c5f92d | |||
| 1dec7b04fe | |||
| c6eb6f98b8 | |||
| 1b01969bda | |||
| 17f3148ac7 | |||
| 35b4a48e99 | |||
| c0d18452fc | |||
| e5c9a310cf | |||
| 6d308a08c5 | |||
| 9e477ddb00 | |||
| b7c4d1eeef | |||
| 4a2f10ebcf | |||
| 41cb909604 | |||
| 91908ade4d | |||
| 9a32c48098 | |||
| d996ffc494 | |||
| d56fdc3d04 | |||
| 1dce4fed6e | |||
| cae5e5342f | |||
| 4e609aa955 | |||
| d28e5534d9 | |||
| 3bd8b0b575 | |||
| af4ff7cc33 | |||
| c9179ad81d | |||
| d6b5124c88 | |||
| 64b62fd3b3 | |||
| cebe3fb299 | |||
| ed7343f7f2 | |||
| 35158257c6 | |||
| b935ce2e06 | |||
| 4afe7565b4 | |||
| 6bed34ad61 | |||
| 0ad52a7e22 | |||
| aa52cf6868 | |||
| cd11838e5f | |||
| c31157bafe | |||
| 419b7b2ce8 | |||
| ca3f3cc49d | |||
| d3f0e2afb3 | |||
| eac3832ec0 | |||
| db19c7675f | |||
| d036fdf919 | |||
| fc0934755a | |||
| de4deb8c4e | |||
| ba46616cbc | |||
| a552638373 | |||
| e30123d58f | |||
| b7dee3e9c8 | |||
| 717d0c24a6 | |||
| 40813752f4 | |||
| 19902abaf8 | |||
| 06643fc9f5 | |||
| 0fa68d8930 | |||
| cc98358453 | |||
| 5f0b35b2c5 | |||
| bd0f5e9322 | |||
| cb0e4b23c7 | |||
| d83e69d977 | |||
| f657516a7e | |||
| 8d29c5f931 | |||
| eeac91579c | |||
| e5a4b8b81b | |||
| 7b8f775893 | |||
| c6ef2105ab | |||
| e681234e45 | |||
| eb93b98d6a | |||
| 7d887fdc1e | |||
| 9d405a5df4 | |||
| 18c003e8bf | |||
| ec4316aa84 | |||
| b79261eebc | |||
| 47a62db559 | |||
| 01645cbd69 | |||
| 3dbecab494 | |||
| 5c590b8434 | |||
| 2dac3705d2 | |||
| 774999ee95 | |||
| c906f864d7 | |||
| 68e3c4f984 | |||
| b08818fab9 | |||
| 33f73b577c | |||
| 30422cfdd3 | |||
| b085b56e32 | |||
| ea3d4e7ce5 | |||
| bea1483cde | |||
| 90efd79055 | |||
| 7e646efd0f | |||
| eb849662c0 | |||
| f146294a85 | |||
| 374de826ab | |||
| 1fd3e32051 | |||
| e607958a7b | |||
| 0f068fb7c4 | |||
| b991a4c7f3 | |||
| 5b99a75ae1 | |||
| 1ba73b9e6b | |||
| cfe3adb599 | |||
| be6052bcdc | |||
| ca8e21583c | |||
| 1c01fc35eb | |||
| ce38fe7bea | |||
| 6cd04aa858 | |||
| 03f7396d50 | |||
| a4803d50c5 | |||
| 40cba172ed | |||
| 3ba221eb8e | |||
| 8bc12fe7cb | |||
| 34bb2079e7 | |||
| 7ca016652e | |||
| 13ab123daf | |||
| f54bd69b5b | |||
| 16e8db1f17 | |||
| eb0ae041e3 | |||
| 893e613faa | |||
| 714f0ba222 | |||
| 19c3b1315a | |||
| f44a53271d | |||
| d90714e8df | |||
| c7fd7353a8 | |||
| 05f15d88b6 | |||
| d7d81c7152 | |||
| 73b510225f | |||
| 5d2eca2363 | |||
| 939b6b854a | |||
| 4f8bdf517a | |||
| 369f66be35 | |||
| 8a75dbf719 | |||
| 1032f70ddf | |||
| 16e2323153 | |||
| 2ec23bdf37 | |||
| 4fb09824bf | |||
| ad1cd6a2ce | |||
| a27367210a | |||
| efe9761f80 | |||
| 0b75de12ef | |||
| af5c8175b4 | |||
| 8df32cea33 | |||
| e4150443ca | |||
| d539781cf3 | |||
| 2bbab7d892 | |||
| 729c35c0f8 | |||
| 2abcc35031 | |||
| 85b910814e | |||
| c1dffac28c | |||
| 911b06186b | |||
| be9d5d4e31 | |||
| b32d66e7b6 | |||
| 939bfc701a | |||
| 9c470822a1 | |||
| 59195bd5dc | |||
| ef8c2c434e | |||
| 5ec85197f8 | |||
| 8eed8f5155 | |||
| 48f5e8b6c3 | |||
| 0a2da45794 | |||
| a79bbd63a3 | |||
| c667b0cfdf | |||
| b3a22bea56 | |||
| 6ca6ee5a6f | |||
| 78cff7518b | |||
| d0821adf0e | |||
| 461bc4c47e | |||
| 99406266b1 | |||
| a6576f1d09 | |||
| 95b4870e20 | |||
| 5607253be5 | |||
| 94563328c4 | |||
| ddf610125f | |||
| b6cce3d744 | |||
| 8a3b2f20e4 | |||
| 62cd5f7984 | |||
| 2db4592571 | |||
| e35088d6ff | |||
| 156b1b20e3 | |||
| 9294e8b098 | |||
| 6dc98561a9 | |||
| 4cfad7adf2 | |||
| 3ab0d3ed94 | |||
| 430f230940 | |||
| 727644c13a | |||
| 3ab8eca09b | |||
| 0dc07b34a7 | |||
| 221c55b620 | |||
| 02f9b83353 | |||
| cfc965ff9a | |||
| 033df0ebfb | |||
| ac34640224 | |||
| a987192040 | |||
| 751696d7eb | |||
| 50aa1566ae | |||
| 0a5bba7235 | |||
| 8b8af6aee5 | |||
| 8e8fbc4cff | |||
| 985dfab2c4 | |||
| 633161fe86 | |||
| f51d50399c | |||
| cf4e538536 | |||
| ec5ba79447 | |||
| 0948573e63 | |||
| bad582c2d3 | |||
| f55893807a | |||
| dc2f767171 | |||
| f0e92e3bdd | |||
| 3db1a214ce | |||
| 79e94227c2 | |||
| 665514692a | |||
| 9944db6d73 | |||
| 2ed24666b5 | |||
| 0c199cffc3 | |||
| 29fe1866ef | |||
| 4811c8036b | |||
| 036151c1e3 | |||
| 9d22372cc9 | |||
| a3d3e347a2 | |||
| 9577c54999 | |||
| 72c40be553 | |||
| 6aa8877bf6 | |||
| ef1e256fa7 | |||
| ad37651726 | |||
| 92caeb039f | |||
| cdb607f202 | |||
| bcab8d3abf | |||
| 9d747642fe | |||
| a755203aee | |||
| 814726e5d4 | |||
| c4a312e6c3 | |||
| 81609393f1 | |||
| 0634958329 | |||
| b352b903e7 | |||
| 74d6658f39 | |||
| 3076de0405 | |||
| 4a0a0ed4c6 | |||
| c5def8f706 | |||
| 610b3ad302 | |||
| 4c63a929bc | |||
| 0f72ff3a57 | |||
| 67afcd4edd | |||
| 6e9ada2d6c | |||
| 9651efe626 | |||
| df7201ba42 | |||
| 14315ec5cd | |||
| 0f10fc9ad9 | |||
| ce678fd986 | |||
| 9d693da852 | |||
| 22742b6bd6 | |||
| a33bf40a7d | |||
| 3801ea7777 | |||
| c7935ceed1 | |||
| ea119a5679 | |||
| 7a324da548 | |||
| 0d1c99a6ad | |||
| e4626d233c | |||
| 9934f65987 | |||
| b2de2dfd1b | |||
| 3f2639a655 | |||
| 26b5b19f88 | |||
| 5449365658 | |||
| 96536239c2 | |||
| 0e18453e34 | |||
| 777202e1f1 | |||
| 6f61ed1477 | |||
| a509e04f9b | |||
| 152eb983ff | |||
| d6f33e4830 | |||
| 5613d30e97 | |||
| 1bfbb6448a | |||
| c5b44f44f9 | |||
| 22b3ea93a8 | |||
| fe726b0f80 | |||
| 18ecc0032d | |||
| 1a1532c8cc | |||
| e6d2b7759a | |||
| c96ff018fe | |||
| 1005c62e90 | |||
| 2a714f4e37 | |||
| 73063e2dab | |||
| 6e5d232ccc | |||
| 8e96e38099 | |||
| 7517b9b122 | |||
| 55faf4c5bc | |||
| 92372412d9 | |||
| 990da22249 | |||
| 3849cc400e | |||
| 84b0e84447 | |||
| 3b8486665a | |||
| 451c393ef1 | |||
| 095691160b | |||
| 7597cb354c | |||
| bd2a31a0fe | |||
| be61650102 | |||
| 3279c2f15b | |||
| 38da15b501 | |||
| 0f515785c6 | |||
| 66db102c70 | |||
| ab859f7952 | |||
| 6367e55a5f | |||
| b61f2799a8 | |||
| 1b4d3bcbb6 | |||
| 45479558ba | |||
| 6fed8d2a26 | |||
| 7dbe4caf3f | |||
| ce1756425f | |||
| 920f535500 | |||
| f29b70c63e | |||
| 1e690e505b | |||
| cd27bf0745 | |||
| 62ded15cd8 | |||
| bc6907d280 | |||
| dcd2e13aec | |||
| cfff8a5ed5 | |||
| 2d760e75dc | |||
| f071a15591 | |||
| 6a595231b0 | |||
| 065ea00066 | |||
| 680cd6df34 | |||
| 22846f7577 | |||
| 5583a3c89b | |||
| 25d1c7e622 | |||
| 5add8ac255 | |||
| 3af54a9978 | |||
| 1dc4e9a84c | |||
| 9af41dd988 | |||
| 0039c5b981 | |||
| 17f4f82827 | |||
| 0314889c6d | |||
| 7387e9f9a8 | |||
| 158a3c53d8 | |||
| 98666e84ea | |||
| d91491bf13 | |||
| 92443772d2 | |||
| 041d59cc17 | |||
| a405173d59 | |||
| 93807bf230 | |||
| 0b67847b56 | |||
| a72cb8a37a | |||
| ad4f718e0c | |||
| 5f92cd2b00 | |||
| a1cf588d5f | |||
| dfc510f009 | |||
| e7e09e444c | |||
| 195acc3e45 | |||
| b8426584fc | |||
| 6a2435ab32 | |||
| 85001c55fb | |||
| 8c4cf7757f | |||
| f785f76d98 | |||
| c90ee57203 | |||
| d670906ff3 | |||
| c578f63f34 | |||
| 9a404bdcdc | |||
| 91b07ba075 | |||
| f9c6dedca1 | |||
| 73c7b7db3d | |||
| 6b02b06581 | |||
| 5f7b2bac12 | |||
| 806cb39103 | |||
| 2eaeac53e4 | |||
| 3baba73cf5 | |||
| db6bf15361 | |||
| 3aa3175378 | |||
| 4c63956624 | |||
| c89647af9e | |||
| 1303a88769 | |||
| aa1eabbb56 | |||
| cbacec0760 | |||
| 82fcf141f7 | |||
| aaa12c8fb6 | |||
| 9b5150daef | |||
| 9304d88920 | |||
| 725ca010e7 | |||
| 59c857e969 | |||
| 45c9136443 | |||
| bc4087ac96 | |||
| d9c4191e8a | |||
| 3fa40b8dc2 | |||
| dc69bc28ae | |||
| 53577f11e1 | |||
| 1abb64f6f6 | |||
| b442b29ccf | |||
| 5609851747 | |||
| 68a8004518 | |||
| 0b39e3a6cf | |||
| 924ad2d592 | |||
| 5202d07a77 | |||
| b632107a7a | |||
| f62ca21dd1 | |||
| 553befa012 | |||
| def624a50c | |||
| 268f26a84f | |||
| d9456b3030 | |||
| f195f849d0 | |||
| 669e5b91b4 | |||
| a06e0958ca | |||
| a56149e79a | |||
| c805fc28a4 | |||
| 218c274090 | |||
| 921c79f26f | |||
| af4db55e66 | |||
| 9f78559cc8 | |||
| 23b4bc93aa | |||
| 6321df078a | |||
| e6258377d9 | |||
| b3a26fd0cc | |||
| 47669566da | |||
| 03a1bb0a5b | |||
| 54ffbbe7db | |||
| 0b82f07115 | |||
| 265db2e533 | |||
| 2b2abf4895 | |||
| f93fb21c95 | |||
| b7fe764e6c | |||
| 500e4c3572 | |||
| 55281c01fb | |||
| a8bf10ea5a | |||
| 9f60093b41 | |||
| 562d4f4f58 | |||
| ac87437588 | |||
| b1444b4dfb | |||
| ed21875083 | |||
| 0249b5b311 | |||
| f46a0f64f2 | |||
| 1fd0447dbb | |||
| 9856201f5c | |||
| a75c07065f | |||
| 6a86ef5a7d | |||
| 500e5d6759 | |||
| e3f2c9f29a | |||
| 3458e6248b | |||
| 14039d05a9 | |||
| c592da4937 | |||
| 61fb4244f2 | |||
| a503e022d8 | |||
| 0942dc0764 | |||
| 0d6919dd99 | |||
| 3e8e797bc2 | |||
| 0a907ec694 | |||
| f1b4a54991 | |||
| 58e7106df1 | |||
| 7bf391c54a | |||
| 07a6e098c8 | |||
| e4897d6b54 | |||
| 23026d966b | |||
| 3cd98df30d | |||
| 001be23258 | |||
| c85b6682e0 | |||
| bb2bc98a22 | |||
| 43da4f7ccc | |||
| 37e6d1e9c7 | |||
| 1d9926698e | |||
| b04f64e02c | |||
| c6d710b14b | |||
| 2e07850622 | |||
| 1b6888281e | |||
| ce94411dff | |||
| 58a3098cd8 | |||
| 8a68163905 | |||
| 1f98e64b71 | |||
| a9480a768b | |||
| ed94514fc2 | |||
| 1f41cc2030 | |||
| 6455ec36ec | |||
| 676a3d068c | |||
| 5533f8d87b | |||
| 93af7b61ce | |||
| 364ad5529d | |||
| 1974a46fe0 | |||
| 5327ab9903 | |||
| 235d191bae | |||
| 4eb80ec75a | |||
| 2900459222 | |||
| 9088792b0e | |||
| b41fe7beab | |||
| 00f9bb8a07 | |||
| a90e1637cb | |||
| 4bad5c0241 | |||
| ce09d82fc7 | |||
| 5b926b8196 | |||
| c719ccdb48 | |||
| 9f1786aeb1 | |||
| 0bb83e06bc | |||
| 2ba67c2bc2 | |||
| cae91983ec | |||
| 3190ef0c1f | |||
| c7c1372833 | |||
| ba02cda55f | |||
| 512a94afaa | |||
| 6d978c383a | |||
| 938678f87f | |||
| 9dcf5fca5b | |||
| 88f0dc4726 | |||
| 8ae7910aba | |||
| b70e85fd10 | |||
| 99bc591366 | |||
| f4963cd1c5 | |||
| 4859cb8528 | |||
| c42b91980b | |||
| 8b3c8820e0 | |||
| 29bc78a486 | |||
| 30fe86ed32 | |||
| 85cefd5a00 | |||
| b5a328e0ca | |||
| 209a2e8fc3 | |||
| 8b978e6aea | |||
| b4df0e7c9e | |||
| 76aeda6b00 | |||
| 1a0ad6d9c3 | |||
| 6b219e3e25 | |||
| 1e90691013 | |||
| 3577c87c88 | |||
| baf6607e74 | |||
| a700848bae | |||
| 9bbfd5804e | |||
| b234d74f43 | |||
| cd49ff330d | |||
| 49faf7af1a | |||
| c892b83c93 | |||
| 985b52331a | |||
| 021dded9dd | |||
| 8fd0cdc732 | |||
| fe7a4d42d3 | |||
| e89d6353af | |||
| c5bb74d184 | |||
| 874349c928 | |||
| 157604b3a5 | |||
| b935544d65 | |||
| 03271df579 | |||
| 0633d3a07d | |||
| 045377a594 | |||
| b5c8030f19 | |||
| 7c2072789c | |||
| f75e856d2b | |||
| 44d689bc6e | |||
| 9e433c2f19 | |||
| 21b6279b74 | |||
| 4d89076bdc | |||
| 71e4ff7e03 | |||
| d2dfda6583 | |||
| 6a855f528b | |||
| fb93109c2d | |||
| bd190af7a3 | |||
| 44268b0c6b | |||
| 6451e5e7d1 | |||
| 2b3c4c68e4 | |||
| 7030cf2433 | |||
| 26d7881b80 | |||
| 21fe42b28c | |||
| d5ecf68d26 | |||
| 0ddff4ec7d | |||
| 283ac3191f | |||
| 4dd0c80dad | |||
| 3b53c6ca47 | |||
| 4e3b4809ea | |||
| a90b8fb449 | |||
| 1da509027e | |||
| 5b96e4761e | |||
| 311ea79238 | |||
| 98be2c91df | |||
| 2657e5e226 | |||
| cfcb0d4fb7 | |||
| 97d03f3215 | |||
| 4065529bdf | |||
| 0a6260b1d8 | |||
| 12caf2510e | |||
| a58d2f710d | |||
| 6be2db8c42 | |||
| 5cf68416d8 | |||
| ebcb3c6b3b | |||
| 04267e0f6b | |||
| a540e6afc5 | |||
| f1841e48b3 | |||
| 9865bb6904 | |||
| e06ddea784 | |||
| 8b5a89c136 | |||
| da093c1982 | |||
| 048fb6278a | |||
| f1b0778f79 | |||
| 0e584fa4a5 | |||
| f44386008c | |||
| 60c139a844 | |||
| f410213003 | |||
| 19cb5d57db | |||
| 30b912fc81 | |||
| 7fc07e2d5e | |||
| 72c83d9430 | |||
| bfbac12f76 | |||
| 461f7dc9f9 | |||
| 3e5497e2f9 | |||
| 6ecbcc7c19 | |||
| 8cef02e8e8 | |||
| caabfd14b3 | |||
| 2ffbaa9578 | |||
| c40aeaec3a | |||
| 80e84a3ad0 | |||
| 0552335ec1 | |||
| 7aea774b21 | |||
| 4d4ed92055 | |||
| f0ec26992a | |||
| 62e8332b34 | |||
| 0925f71987 | |||
| 4c11652808 | |||
| 5b10c38e43 | |||
| 3316df9195 | |||
| 5d355f1a8b | |||
| 2ff91103ca | |||
| a954d50ad4 | |||
| 5be4d37aff | |||
| 1e6c9dbcfa | |||
| 708a56872d | |||
| 0a2bca3f73 | |||
| 1ec710c985 | |||
| d5a44f9ad4 | |||
| e64dca7144 | |||
| b2779c35df | |||
| 9b11e119d4 | |||
| 988c62baed | |||
| eb3e640003 | |||
| 9475b947f5 | |||
| 18564f1ae2 | |||
| 638f1deb62 | |||
| 7e74d30f45 | |||
| ce8d0f8135 | |||
| 872127b722 | |||
| e180dc44bc | |||
| 268b8845a9 | |||
| 74c47995a3 | |||
| 24f5936cbf | |||
| bdfa8aca28 | |||
| 15eb1ad922 | |||
| 6ec98ee8b1 | |||
| c5862d6de9 | |||
| 57eb55446f | |||
| abd1399a7f | |||
| 57eba21ee5 | |||
| 1b56211a70 | |||
| d8974d53b2 | |||
| 5ecd17f49e | |||
| 6bb99aec3c | |||
| a67e83e24e | |||
| 5a3c3134ec | |||
| ce8b9ee8c4 | |||
| 970dfc9f67 | |||
| 04d39c0961 | |||
| d6339aa015 | |||
| 11076bf337 | |||
| 8a8eea53a2 | |||
| 09bd7e8ef8 | |||
| 9356619380 | |||
| fc15147cf5 | |||
| ab6b7a8044 | |||
| 07c2fe726e | |||
| fa355603fb | |||
| 109bb505d8 | |||
| 4a6eebc0e4 | |||
| 78ce2b473e | |||
| 6beb5f5587 | |||
| 501fed6c4f | |||
| b1bd8e9ee2 | |||
| daeca1bb18 | |||
| 17f6d5208f | |||
| bee4d7a12b | |||
| f32e3e0c7c | |||
| 1b69612246 | |||
| 9e93509a56 | |||
| ef45cd3342 | |||
| 46fe2e6b44 | |||
| 1133c2cc1d | |||
| fde10553e0 | |||
| 83615ff351 | |||
| 352eb4cb6d | |||
| 75c75ac00c | |||
| d9bcf52db2 | |||
| 43f0362e6d | |||
| ed5e313c73 | |||
| c0010f60e6 | |||
| 75301e4cf5 | |||
| 875c8fdcbe | |||
| a0b1642dc0 | |||
| 1d7e54f8c9 | |||
| 77c8581bc0 | |||
| 5dd625916b | |||
| e3d7718cf3 | |||
| 9eccd7b1fb | |||
| 5b05d126b4 | |||
| 03aaf189c1 | |||
| 6ef9395419 | |||
| 3a56e13b78 | |||
| ec28acba3d | |||
| ee6647ce40 | |||
| 03d54f8f6e | |||
| 553e6d7549 | |||
| e6896ee71e | |||
| e6762f9b48 | |||
| 099bb1afef | |||
| 9c33093c91 | |||
| 634d8038b9 | |||
| ad46154f2f | |||
| c7fbb4615c | |||
| fa81068ea8 | |||
| 70c2a1c9f9 | |||
| 164fcb49d9 | |||
| 64cf18aa1e | |||
| 66a68ce264 | |||
| 86162aaddb | |||
| 9cc7a94a94 | |||
| 6bca1225e6 | |||
| 379a4e6a01 | |||
| 460cfcaf3e | |||
| 8e69103822 | |||
| 2f67dab2b6 | |||
| bb65ebd8be | |||
| c46ea0390c | |||
| bc8a6dd2e3 | |||
| b1478c37f6 | |||
| 4e944a9f3c | |||
| c7fa9b5fe8 | |||
| 65148b123b | |||
| 2f92a34bb7 | |||
| 54ed24f481 | |||
| 268df9f67a | |||
| 84dc398d32 | |||
| f6a3205d10 | |||
| 522cb66582 | |||
| f873a140ce | |||
| 36dfc5bbd1 | |||
| f80668e87f | |||
| dcb5d47ee6 | |||
| e95c22eb21 | |||
| 9fb83e61ea | |||
| 1513cdf7bc | |||
| e33af1a3f8 | |||
| 857d77a10a | |||
| 0e431a0250 | |||
| 3acfc0b630 | |||
| 2ce5f69def | |||
| 7d347be902 | |||
| a456d78fe0 | |||
| bf67c967d6 | |||
| 44b7a7145c | |||
| 3867ee71ed | |||
| 464f4813e3 | |||
| af8b52e7e8 | |||
| 796588900c | |||
| 4beb2ed507 | |||
| 0ff6833e96 | |||
| e2cfcc52b3 | |||
| fc8a46025e | |||
| d2bea0c228 | |||
| f016c2b72f | |||
| be62058696 | |||
| af18d5ed81 | |||
| e9c91a1ce2 | |||
| 29767b2886 | |||
| 96a31c69c5 | |||
| 534632dc52 | |||
| 8bf5f3d869 | |||
| c4f92322f5 | |||
| 1e4aa116e5 | |||
| 90cc1411da | |||
| d13ce6768c | |||
| fd4a7f2150 | |||
| 6bd64c6873 | |||
| ba58d868e5 | |||
| c50799ba3b | |||
| 039d82ff1b | |||
| a2f0933d01 | |||
| 77e1e3cc18 | |||
| 7bdd41350a | |||
| 6797a6ab56 | |||
| 86b5928f5e | |||
| 6dbd15aa71 | |||
| 22e5b081c4 | |||
| d848f33c48 | |||
| c64367536d | |||
| fc0102b079 | |||
| 62a39639c2 | |||
| 158aaff384 | |||
| fd836145fe | |||
| 697bafdd0a | |||
| 9675dcac44 | |||
| 48849d7866 | |||
| d0ce2f0b5a | |||
| a19f635a6a | |||
| 676ed59342 | |||
| 4015f46b7d | |||
| 770cee7139 | |||
| a4619a54a7 | |||
| f7d99f97a3 | |||
| 8b7df0c12e | |||
| bd780817f7 | |||
| 82fb45aa2a | |||
| 74870a8189 | |||
| 7a9f6b48f4 | |||
| d3c089130d | |||
| e0f3060527 | |||
| 4c1256acc4 | |||
| 85f6f5bd29 | |||
| 4d9eac663a | |||
| 0ef4d90ad0 | |||
| 1a1e7edb02 | |||
| 51b835f71b | |||
| e38fe3d361 | |||
| cc042c9936 | |||
| 599e3bc937 | |||
| 1fa0d940bc | |||
| 7dc4a9525b | |||
| b6f1f4ef64 | |||
| 3faae67663 | |||
| ccc94c9b05 | |||
| 1fd30db726 | |||
| 0ba76ac066 | |||
| 8b661fe556 | |||
| 077907b7c3 | |||
| 172d669780 | |||
| 6b85b9a416 | |||
| 5d3001279c | |||
| 4582a13360 | |||
| 3a064535ae | |||
| 444ec4ad27 | |||
| 94e910586d | |||
| bb5ce007e6 | |||
| deaa74d378 | |||
| 13e1794e91 | |||
| 67b3595008 | |||
| 6c33f518a8 | |||
| 88da62ba09 | |||
| b6997a56df | |||
| 74178fd1c2 | |||
| 34c59bfa90 | |||
| 2956bce047 | |||
| 41f33ecbb9 | |||
| 86241e2871 | |||
| 1e32897d3e | |||
| 1b63a9a9b5 | |||
| 4c9f11b78a | |||
| 32348c2b0b | |||
| 5e690c5d04 | |||
| 4ec56484a1 | |||
| 8f2a5649fe | |||
| c3b25e12a5 | |||
| 6d3e33d440 | |||
| c11f7ce54f | |||
| 84806cc174 | |||
| 16f12f7d59 | |||
| 8609b8e589 | |||
| 29e744fdbb | |||
| 515b87bcbe | |||
| 5fa9faca20 | |||
| a4ebdfd47c | |||
| 188d8d4b64 | |||
| ce5581d428 | |||
| 5b4acf14ea | |||
| 5fc6cb15b8 | |||
| f6e9a8eee4 | |||
| 6c0950cb2e | |||
| cb8a9ef2c0 | |||
| 470cbbe9ff | |||
| e01f1434fb | |||
| 187084ce46 | |||
| b6f9382b5b | |||
| 57f68381ed | |||
| 3e35729eb6 | |||
| 544fa57641 | |||
| 7e94309046 | |||
| 21eff5b825 | |||
| b408d7c95e | |||
| d9929edbc1 | |||
| 843b73dedb | |||
| 02a8145b18 | |||
| cfcb315b14 | |||
| c8a70a0a73 | |||
| b84a3a0230 | |||
| 49d70232f8 | |||
| 8cc9f496ee | |||
| 1547f2ec80 | |||
| 42a8b40de0 | |||
| b4b968ff44 | |||
| 52e3c063c5 | |||
| 257089884f | |||
| c650ea9765 | |||
| e369d45b9c | |||
| 2d84b6f6d9 | |||
| eef1171944 | |||
| 12ccdcf858 | |||
| f1a03bfb04 | |||
| 696b0e29e4 | |||
| 5eb748ae17 | |||
| aa4340ef5c | |||
| 0062e54e93 | |||
| dada5090b0 | |||
| 1c4593c648 | |||
| e7004cef76 | |||
| 2bb101bd19 | |||
| 26baf70912 | |||
| 89c2582376 | |||
| 33e003616d | |||
| bf03d77ab9 | |||
| a6cbf1f922 | |||
| d6f056f266 | |||
| 69a247d500 | |||
| a76c67c19f | |||
| b836164a38 | |||
| 058507badf | |||
| ad40e90790 | |||
| 0c9dc11550 | |||
| 1ff55c2729 | |||
| 066269153e | |||
| 38bb08778a | |||
| 5dbcdf1484 | |||
| f03a6ab5a4 | |||
| 6fa5abcd7e | |||
| 5dc07ed295 | |||
| 064d4255d5 | |||
| 04139eb82e | |||
| 1b1a122b1f | |||
| ccb132320c | |||
| c0e7f824df | |||
| 05bba71eaf | |||
| 7ebe5c4bcf | |||
| ae1bd891e7 | |||
| 9899e5021d | |||
| 94440e0170 | |||
| c25928e44f | |||
| a7fc7d4ffb | |||
| f336103f63 | |||
| 56e2b38048 | |||
| a5ccff720a | |||
| 1d8c2d6c22 | |||
| 0b8c357eff | |||
| efc168f473 | |||
| d8428f98d9 | |||
| 60f17d26a3 | |||
| 05bc664c11 | |||
| 2cc84b6e51 | |||
| 5ccdbef7d5 | |||
| c13c2650a2 | |||
| ec6c998a3a | |||
| 2f6091419f | |||
| 2022dd7d74 | |||
| b8202dab3b | |||
| ef688a74fe | |||
| f632e7c043 | |||
| 04a19f9813 | |||
| 2cbc591c9d | |||
| 3f00e79bcb | |||
| 3586fc4910 | |||
| c9a6bbeb64 | |||
| 0655a135e6 | |||
| d3e8bb1889 | |||
| 14ceacac73 | |||
| e4f33b5970 | |||
| 4474f8ef18 | |||
| 76c9f4f5a6 | |||
| 942ef3b7f2 | |||
| 0b9df6d8c4 | |||
| b5ea504ad2 | |||
| 803b0c4bdb | |||
| 6537d0dc76 | |||
| f8f36c085c | |||
| 7339f67dd7 | |||
| 0d4e501239 | |||
| 8d609607e2 | |||
| 71a889ed73 | |||
| 27a75a9085 | |||
| 954d6c326d | |||
| 7ea05d038e | |||
| 33930ff046 | |||
| 16f41ea059 | |||
| 0a7270fc29 | |||
| 23fbd9d004 | |||
| 610c79fbf3 | |||
| fd44c2a2ff | |||
| a86a82b39c | |||
| 89b059b1ea | |||
| bd2d0f769f | |||
| d830422489 | |||
| d1a54249e7 | |||
| 4dfbf98e4e | |||
| 1b6258ec8c | |||
| be707dbb6f | |||
| 664b03bb13 | |||
| 7c6723d912 | |||
| 1febf2ec83 | |||
| fe69928764 | |||
| bbd61eb13f | |||
| e15e1e253d | |||
| 45e2178ada | |||
| a6e4933d93 | |||
| 98599e0972 | |||
| b4837f2e2f | |||
| ea08e7d192 | |||
| d178e089a6 | |||
| 5f00b37e21 | |||
| 8a8792d47f | |||
| 59d9bc9e48 | |||
| 8793dd3ceb | |||
| 48062380fa | |||
| 3636aa5522 | |||
| a1aea4588f | |||
| 1d4fffb799 | |||
| 6f90f5dc5f | |||
| 9dd6972d26 | |||
| d731a7d52c | |||
| 059468b74e | |||
| 5e69fb782a | |||
| a5beffda78 | |||
| 7de7ce5fdc | |||
| 383e8c7f68 | |||
| 0dbda65e44 | |||
| fe01da077e | |||
| d43a4e9df9 | |||
| 3e226795f0 | |||
| c4a0fe1606 | |||
| ef63a84a3e | |||
| 8c16ba372e | |||
| 9be4a17687 | |||
| 89332e1696 | |||
| 8a56129def | |||
| ed0c815735 | |||
| 7a69da16e4 | |||
| 351717414d | |||
| 52f44de257 | |||
| ae6dddfff4 | |||
| 539f555e23 | |||
| b75fa26dc1 | |||
| 3d22a2d845 | |||
| 1aab4752e2 | |||
| 86f8a4a9d2 | |||
| db2cb061cb | |||
| 6a71b24495 | |||
| 84712a8bbc | |||
| b86fb95306 | |||
| a3a9bde83e | |||
| 2fe2dd170b | |||
| f772bf4fbc | |||
| ac0c3093f4 | |||
| 12150baa5e | |||
| 219b02c1e5 | |||
| 4551e60f8b | |||
| ea842e78af | |||
| 5651fbedc4 | |||
| 2f87dfd4a9 | |||
| 332dd76867 | |||
| d2c9ea8a9a | |||
| 40d57da83c | |||
| 561813eb2a | |||
| e6c9dfbd91 | |||
| 603b6596af | |||
| 64abc3e86c | |||
| fac42fa3e8 | |||
| 7ad4020829 | |||
| 72ab0d11ff | |||
| 4ea866f050 | |||
| a476531524 | |||
| e7e6ac5bb3 | |||
| f346362b00 | |||
| d9ba455695 | |||
| 8927a0561f | |||
| e03c5e9f23 | |||
| 1f79200db8 | |||
| c009e4a57d | |||
| c615d52cf4 | |||
| dbb3316511 | |||
| cd6f204c77 | |||
| 269131ed21 | |||
| 65d784e88e | |||
| 35afb6cae0 | |||
| 27bce09be8 | |||
| 4f25b6ac0c | |||
| 3d5ed1a7e3 | |||
| a03115a4a6 | |||
| 7219d28a31 | |||
| 0875bce68f | |||
| 54fe302907 | |||
| edaa8f811f | |||
| 2c8fd109de | |||
| 07fe7ad1a2 | |||
| 16d88cc095 | |||
| 2a6e6b3dbd | |||
| 0c19848230 | |||
| 3c3a4db54e | |||
| 0e6bd2224f | |||
| a64d2f4673 | |||
| 1e8a54af0b | |||
| aa53d8708e | |||
| 8c600ca553 | |||
| 25fe6d7dde | |||
| afb369950c | |||
| d7f133c24c | |||
| 5312fd30e5 | |||
| 23dd0bdaa1 | |||
| 1d06624d38 | |||
| d40069a018 | |||
| 73e27bdd48 | |||
| 064eb0b24f | |||
| af968c5b44 | |||
| f93dbe51e2 | |||
| 1c34707925 | |||
| deaca58504 | |||
| 1d519aa9cb | |||
| 191faeae70 | |||
| 1153aaf55b | |||
| 292cb5a5af | |||
| 1e9488d4a6 | |||
| ff1d77ead9 | |||
| 977e1a94b2 | |||
| 60ee5fc844 | |||
| 940c8fcb5e | |||
| 71e0148eb4 | |||
| 293c104cc4 | |||
| 5a3035bb72 | |||
| e04cbd71d0 | |||
| fa4ce6a8bc | |||
| 9863f62321 | |||
| 7cd1f7dbd5 | |||
| 8c45a18524 | |||
| 8cb383ed45 | |||
| 7d1305b169 | |||
| 24ab1f32e4 | |||
| 073ad0dada | |||
| f43b6a0675 | |||
| fc1ddcd2f8 | |||
| e7f774f964 | |||
| cb49af1ea5 | |||
| 73d7d704c1 | |||
| 8b89232f12 | |||
| c3dec1a5ea | |||
| 44b06d70e8 | |||
| eee07e6cfd | |||
| 5051c27c3d | |||
| 76f3506ac5 | |||
| 5d7a84fad7 | |||
| dec161ed26 | |||
| 0f9dbf84b7 | |||
| f0d5337818 | |||
| 4cd9de5c37 | |||
| 76b0bfa7f5 | |||
| 32d6b0eed4 | |||
| 127d962271 | |||
| 92d7af0881 | |||
| af12066f77 | |||
| 2a1f8fa8f1 | |||
| 04e47bde84 | |||
| 49da7e74cd | |||
| 6ac47734c0 | |||
| 0e6ea76e88 | |||
| 0514588175 | |||
| 59d1212039 | |||
| d61cca6720 | |||
| 8c74e88f16 | |||
| 46cf512032 | |||
| 24a185d26b | |||
| b99a7344c9 | |||
| c6a4fb1e13 | |||
| e2718fe845 | |||
| 76314280cb | |||
| d6716218bd | |||
| 9371a92122 | |||
| e8b030ad17 | |||
| e0180b4849 | |||
| 3901bbb401 | |||
| 76bebfd798 | |||
| 3013166d8d | |||
| 8596e702ac | |||
| 414bf4a296 | |||
| 0daa01edef | |||
| 98abd96075 | |||
| 8e3fc826e2 | |||
| c750095241 | |||
| 2a0c0c0ad2 | |||
| 92f3bb89c3 | |||
| ac0e6c5e6e | |||
| f397b6fedf | |||
| 1d069e5077 | |||
| c5684a6278 | |||
| f3ac0be0e6 | |||
| 18c9468af5 | |||
| 32bc0da362 | |||
| c9a3800ce7 | |||
| d4239aaa8f | |||
| 4f72d5cfac | |||
| 66acab4130 | |||
| 9e9e3373e0 | |||
| 502fee1b45 | |||
| 4d0c7d706d | |||
| 6cd418b60a | |||
| a3b39dfd1a | |||
| 74da47e286 | |||
| 587ba9bec0 | |||
| 382392e03b | |||
| 409948a0f9 | |||
| 34919ca394 | |||
| 0d1c574cb1 | |||
| c564815931 | |||
| f6fb667ac1 | |||
| 832bbe734d | |||
| 7a2fda891c | |||
| e50c239a2e | |||
| cc7c8d92da | |||
| 87acab0846 | |||
| f43459d476 | |||
| e030f02776 | |||
| 10f2d01e7f | |||
| 80dbf9a32a | |||
| 185274e70f | |||
| f0ac55ec0c | |||
| 44544635dd | |||
| 3c594b1037 | |||
| ea7100e8c4 | |||
| a198abc485 | |||
| d4a37f6ef5 | |||
| a5c9c31231 | |||
| e3ec78a832 | |||
| a116e68a47 | |||
| e7084de166 | |||
| 398eda6365 | |||
| 349abf5ee6 | |||
| 4ce40b1975 | |||
| 536fe28f8f | |||
| 3e9e14f4d6 | |||
| c8140068ad | |||
| db314bc381 | |||
| 499a26b152 | |||
| a9cdb5be50 | |||
| 5f04208dbd | |||
| ffaa292006 | |||
| d3e44b1108 | |||
| fbf274a42b | |||
| d94cd65dfd | |||
| 3c1b403c4e | |||
| 75564453b3 | |||
| 3091e2dc0e | |||
| fc50a36cc5 | |||
| 9bf9fba2ec | |||
| 121615da70 | |||
| 53d28a713c | |||
| cf37704193 | |||
| 38289fe381 | |||
| f9337a1111 | |||
| 6d059b479f | |||
| b6e23b2d3e | |||
| e5e6a46c37 | |||
| 22b9a53bef | |||
| 3be81e3206 | |||
| ff09b6c824 | |||
| a8e892ba90 | |||
| 289cc3e7a0 | |||
| 7480b87e07 | |||
| befa6423be | |||
| fd418f568c | |||
| 09cf18a646 | |||
| 326c175dcb | |||
| 6d7c77ddc1 | |||
| efd706528b | |||
| b523c43927 | |||
| 75545ff70d | |||
| 3c6ef83046 | |||
| b9ac0a79f1 | |||
| 8539896f3d | |||
| a3b508ceff | |||
| 334a486737 | |||
| d7370cc916 | |||
| 92c34f7f38 | |||
| 93328c8d6d | |||
| 5710ec13d4 | |||
| fa637fcecb | |||
| 4af7d6f108 | |||
| 0fd159dadb | |||
| 1ff22c78b3 | |||
| ceb1def55c | |||
| 893a1d8306 | |||
| 3c91690e55 | |||
| 6835dd73bc | |||
| 7317fe1440 | |||
| 7b58fea911 | |||
| c1ff74c9a6 | |||
| 6dabfa176a | |||
| 0714f5fc67 | |||
| 3b1b1bfd48 | |||
| 218c867f46 | |||
| 2bc12f9730 | |||
| 5e564a8e0c | |||
| beaa6a9a7a | |||
| a9c8224f40 | |||
| 3dcc188d93 | |||
| 10b7556a37 | |||
| 54b7291c34 | |||
| 1e30b6e334 | |||
| 406240bae3 | |||
| 74d9b41b7d | |||
| ff0b0c54b7 | |||
| 6eec2d6b4f | |||
| 5731c5437a | |||
| 04f14ec026 | |||
| 12ed6336b1 |
+47
-20
@@ -1,29 +1,56 @@
|
|||||||
version: '{build}'
|
version: '{build}'
|
||||||
branches:
|
branches: { only: [ master ] }
|
||||||
only:
|
configuration: Release
|
||||||
- master
|
image: Visual Studio 2019
|
||||||
image:
|
platform: x64
|
||||||
- Visual Studio 2017
|
|
||||||
clone_folder: c:\projects\simdjson
|
|
||||||
|
|
||||||
platform:
|
cache:
|
||||||
- x64
|
- C:\dependencies -> dependencies\CMakeLists.txt
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
# Forward slash is used because this is used in CMake as is
|
||||||
|
simdjson_DEPENDENCY_CACHE_DIR: C:/dependencies
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
- SIMDJSON_BUILD_STATIC: "OFF"
|
- job_name: VS2019
|
||||||
THREADS: "ON"
|
CMAKE_ARGS: -A %Platform%
|
||||||
- SIMDJSON_BUILD_STATIC: "OFF"
|
- job_name: VS2019ARM
|
||||||
THREADS: "OFF"
|
CMAKE_ARGS: -A ARM64 -DSIMDJSON_DEVELOPER_MODE=ON -DCMAKE_CROSSCOMPILING=1 -D SIMDJSON_GOOGLE_BENCHMARKS=OFF # Does Google Benchmark builds under VS ARM?
|
||||||
- SIMDJSON_BUILD_STATIC: "ON"
|
- job_name: VS2017 (Static, No Threads)
|
||||||
THREADS: "ON"
|
image: Visual Studio 2017
|
||||||
# - SIMDJSON_BUILD_STATIC: "ON"
|
CMAKE_ARGS: -A %Platform% -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_ENABLE_THREADS=OFF
|
||||||
# THREADS: "OFF"
|
CTEST_ARGS: -LE explicitonly
|
||||||
|
- job_name: VS2019 (Win32)
|
||||||
|
platform: Win32
|
||||||
|
CMAKE_ARGS: -A %Platform% -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DSIMDJSON_ENABLE_THREADS=ON # This should be the default. Testing anyway.
|
||||||
|
CTEST_ARGS: -LE explicitonly
|
||||||
|
- job_name: VS2019 (Win32, No Exceptions)
|
||||||
|
platform: Win32
|
||||||
|
CMAKE_ARGS: -A %Platform% -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=ON -DSIMDJSON_ENABLE_THREADS=ON -DSIMDJSON_EXCEPTIONS=OFF
|
||||||
|
CTEST_ARGS: -LE explicitonly
|
||||||
|
- job_name: VS2015
|
||||||
|
image: Visual Studio 2015
|
||||||
|
CMAKE_ARGS: -A %Platform% -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_ENABLE_THREADS=OFF
|
||||||
|
CTEST_ARGS: -LE explicitonly
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- mkdir build
|
- mkdir build
|
||||||
- cd build
|
- cd build
|
||||||
- ps: cmake -DSIMDJSON_BUILD_STATIC="$env:SIMDJSON_BUILD_STATIC" -DSIMDJSON_ENABLE_THREADS="$env:THREADS" -DCMAKE_BUILD_TYPE=Release -DCMAKE_GENERATOR_PLATFORM=x64 -DSIMDJSON_GOOGLE_BENCHMARKS=OFF ..
|
- cmake --version
|
||||||
- cmake --build .
|
- cmake %CMAKE_ARGS% ..
|
||||||
- ctest --verbose --output-on-failure
|
- cmake -LH ..
|
||||||
|
- cmake --build . --config %Configuration% --verbose --parallel
|
||||||
|
|
||||||
|
for:
|
||||||
|
-
|
||||||
|
matrix:
|
||||||
|
except:
|
||||||
|
- job_name: VS2019ARM
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- ctest --output-on-failure -C %Configuration% --verbose %CTEST_ARGS% --parallel
|
||||||
|
|
||||||
|
clone_folder: c:\projects\simdjson
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
|||||||
+277
-187
@@ -1,226 +1,316 @@
|
|||||||
version: 2.1
|
version: 2.1
|
||||||
|
|
||||||
|
|
||||||
|
# We constantly run out of memory so please do not use parallelism (-j, -j4).
|
||||||
|
|
||||||
# Reusable image / compiler definitions
|
# Reusable image / compiler definitions
|
||||||
executors:
|
executors:
|
||||||
gcc7:
|
|
||||||
docker:
|
|
||||||
- image: gcc:7
|
|
||||||
environment:
|
|
||||||
CXX: g++
|
|
||||||
gcc8:
|
gcc8:
|
||||||
docker:
|
docker:
|
||||||
- image: gcc:8
|
- image: conanio/gcc8
|
||||||
environment:
|
environment:
|
||||||
CXX: g++
|
CXX: g++-8
|
||||||
|
CC: gcc-8
|
||||||
|
CMAKE_BUILD_FLAGS:
|
||||||
|
CTEST_FLAGS: --output-on-failure
|
||||||
|
|
||||||
gcc9:
|
gcc9:
|
||||||
docker:
|
docker:
|
||||||
- image: gcc:9
|
- image: conanio/gcc9
|
||||||
environment:
|
environment:
|
||||||
CXX: g++
|
CXX: g++-9
|
||||||
|
CC: gcc-9
|
||||||
|
CMAKE_BUILD_FLAGS:
|
||||||
|
CTEST_FLAGS: --output-on-failure
|
||||||
|
|
||||||
|
gcc10:
|
||||||
|
docker:
|
||||||
|
- image: conanio/gcc10
|
||||||
|
environment:
|
||||||
|
CXX: g++-10
|
||||||
|
CC: gcc-10
|
||||||
|
CMAKE_BUILD_FLAGS:
|
||||||
|
CTEST_FLAGS: --output-on-failure
|
||||||
|
|
||||||
|
clang10:
|
||||||
|
docker:
|
||||||
|
- image: conanio/clang10
|
||||||
|
environment:
|
||||||
|
CXX: clang++-10
|
||||||
|
CC: clang-10
|
||||||
|
CMAKE_BUILD_FLAGS:
|
||||||
|
CTEST_FLAGS: --output-on-failure
|
||||||
|
|
||||||
|
clang9:
|
||||||
|
docker:
|
||||||
|
- image: conanio/clang9
|
||||||
|
environment:
|
||||||
|
CXX: clang++-9
|
||||||
|
CC: clang-9
|
||||||
|
CMAKE_BUILD_FLAGS:
|
||||||
|
CTEST_FLAGS: --output-on-failure
|
||||||
|
|
||||||
clang6:
|
clang6:
|
||||||
docker:
|
docker:
|
||||||
- image: ubuntu:18.04
|
- image: conanio/clang60
|
||||||
environment:
|
environment:
|
||||||
CXX: clang++-6.0
|
CXX: clang++-6.0
|
||||||
|
CC: clang-6.0
|
||||||
|
CMAKE_BUILD_FLAGS:
|
||||||
|
CTEST_FLAGS: --output-on-failure
|
||||||
|
|
||||||
# Reusable test commands (and initializer for clang 6)
|
# Reusable test commands (and initializer for clang 6)
|
||||||
commands:
|
commands:
|
||||||
init_clang6:
|
dependency_restore:
|
||||||
steps:
|
steps:
|
||||||
- run: apt-get update -qq
|
- restore_cache:
|
||||||
- run: apt-get install -y clang build-essential git
|
keys:
|
||||||
make_test:
|
- cmake-cache-{{ checksum "dependencies/CMakeLists.txt" }}
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: make
|
|
||||||
- run: make amalgamate
|
|
||||||
- run: ARCHFLAGS=-march=haswell make amalgamate # some users do this: https://github.com/lemire/simdjson/issues/444
|
|
||||||
- run: make test
|
|
||||||
- run: make checkperf
|
|
||||||
- run: make clean
|
|
||||||
- run: ARCHFLAGS=-march=haswell make test # this breaks runtime dispatch, but see https://github.com/lemire/simdjson/issues/444... this is a code robustness test
|
|
||||||
- run: make clean
|
|
||||||
- run: EXTRAFLAGS=-DSIMDJSON_NO_COMPUTED_GOTO=true make test # this should run tests with computed gotos disabled
|
|
||||||
make_test_strict: # this version fails when a warning is detected.
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- run: EXTRAFLAGS=-Werror make
|
|
||||||
- run: EXTRAFLAGS=-Werror make amalgamate
|
|
||||||
- run: ARCHFLAGS=-march=haswell make amalgamate # some users do this: https://github.com/lemire/simdjson/issues/444
|
|
||||||
- run: EXTRAFLAGS=-Werror make quicktests
|
|
||||||
- run: make clean
|
|
||||||
|
|
||||||
cmake_simple_test: # this version just builds and test
|
|
||||||
steps:
|
|
||||||
- run: apt-get update -qq
|
|
||||||
- run: apt-get install -y cmake
|
|
||||||
- checkout
|
|
||||||
- run: cmake $CMAKE_TEST_FLAGS
|
|
||||||
- run: make all
|
|
||||||
- run: make test
|
|
||||||
|
|
||||||
cmake_test: # this version builds, install, test and then verifyi from the installation
|
dependency_cache:
|
||||||
|
steps:
|
||||||
|
- save_cache:
|
||||||
|
key: cmake-cache-{{ checksum "dependencies/CMakeLists.txt" }}
|
||||||
|
paths:
|
||||||
|
- dependencies/.cache
|
||||||
|
|
||||||
|
install_cmake:
|
||||||
steps:
|
steps:
|
||||||
- run: apt-get update -qq
|
- run: apt-get update -qq
|
||||||
- run: apt-get install -y cmake
|
- run: apt-get install -y cmake
|
||||||
|
|
||||||
|
cmake_prep:
|
||||||
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: cmake $CMAKE_TEST_FLAGS -DCMAKE_INSTALL_PREFIX:PATH=destination
|
- run: mkdir -p build
|
||||||
- run: make all install
|
|
||||||
- run: make test
|
cmake_build_cache:
|
||||||
- run: echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json # we not only want cmake to build and run tests, but we want also a succesful installation from which we can build, link and run programs
|
steps:
|
||||||
|
- cmake_prep
|
||||||
|
- dependency_restore
|
||||||
|
- run: cmake -DSIMDJSON_DEVELOPER_MODE=ON $CMAKE_FLAGS -DCMAKE_INSTALL_PREFIX:PATH=destination -B build .
|
||||||
|
- dependency_cache # dependencies are produced in the configure step
|
||||||
|
|
||||||
|
cmake_build:
|
||||||
|
steps:
|
||||||
|
- cmake_build_cache
|
||||||
|
- run: cmake --build build
|
||||||
|
|
||||||
|
cmake_test:
|
||||||
|
steps:
|
||||||
|
- cmake_build
|
||||||
|
- run: |
|
||||||
|
cd build &&
|
||||||
|
tools/json2json -h &&
|
||||||
|
ctest $CTEST_FLAGS -L acceptance &&
|
||||||
|
ctest $CTEST_FLAGS -LE acceptance -LE explicitonly
|
||||||
|
|
||||||
|
cmake_assert_test:
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
cd build &&
|
||||||
|
tools/json2json -h &&
|
||||||
|
ctest $CTEST_FLAGS -L assert
|
||||||
|
|
||||||
|
cmake_test_all:
|
||||||
|
steps:
|
||||||
|
- cmake_build
|
||||||
|
- run: |
|
||||||
|
cd build &&
|
||||||
|
tools/json2json -h &&
|
||||||
|
ctest $CTEST_FLAGS -DSIMDJSON_IMPLEMENTATION="haswell;westmere;fallback" -L acceptance -LE per_implementation &&
|
||||||
|
SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
|
||||||
|
SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
|
||||||
|
SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation -LE explicitonly &&
|
||||||
|
ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
|
||||||
|
|
||||||
|
|
||||||
|
cmake_perftest:
|
||||||
|
steps:
|
||||||
|
- cmake_build_cache
|
||||||
|
- run: |
|
||||||
|
cmake -DSIMDJSON_ENABLE_DOM_CHECKPERF=ON --build build --target checkperf &&
|
||||||
|
cd build &&
|
||||||
|
ctest --output-on-failure -R checkperf
|
||||||
|
|
||||||
|
# we not only want cmake to build and run tests, but we want also a successful installation from which we can build, link and run programs
|
||||||
|
cmake_install_test: # this version builds, install, test and then verify from the installation
|
||||||
|
steps:
|
||||||
|
- run: cd build && make install
|
||||||
|
- run: echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Ibuild/destination/include -Lbuild/destination/lib -std=c++17 -Wl,-rpath,build/destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
|
||||||
|
|
||||||
|
cmake_installed_test_cxx20: # assuming that it was installed, this tries to build using C++20
|
||||||
|
steps:
|
||||||
|
- run: echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Ibuild/destination/include -Lbuild/destination/lib -std=c++20 -Wl,-rpath,build/destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
gcc-avx-unthreaded:
|
# static
|
||||||
description: Build, run tests and check performance on GCC 7 and AVX 2 *without* threads
|
justlib-gcc10:
|
||||||
executor: gcc7
|
description: Build just the library, install it and do a basic test
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_ENABLE_THREADS=OFF }
|
executor: gcc10
|
||||||
|
environment: { CMAKE_FLAGS: -DSIMDJSON_JUST_LIBRARY=ON }
|
||||||
|
steps: [ cmake_build, cmake_install_test, cmake_installed_test_cxx20 ]
|
||||||
|
assert-gcc10:
|
||||||
|
description: Build the library with asserts on, install it and run tests
|
||||||
|
executor: gcc10
|
||||||
|
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DCMAKE_CXX_FLAGS_RELEASE=-O3 }
|
||||||
|
steps: [ cmake_test, cmake_assert_test ]
|
||||||
|
assert-clang10:
|
||||||
|
description: Build just the library, install it and do a basic test
|
||||||
|
executor: clang10
|
||||||
|
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DCMAKE_CXX_FLAGS_RELEASE=-O3 }
|
||||||
|
steps: [ cmake_test, cmake_assert_test ]
|
||||||
|
gcc10-perftest:
|
||||||
|
description: Build and run performance tests on GCC 10 and AVX 2 with a cmake static build, this test performance regression
|
||||||
|
executor: gcc10
|
||||||
|
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DBUILD_SHARED_LIBS=OFF }
|
||||||
|
steps: [ cmake_perftest ]
|
||||||
|
gcc10:
|
||||||
|
description: Build and run tests on GCC 10 and AVX 2 with a cmake static build
|
||||||
|
executor: gcc10
|
||||||
|
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF }
|
||||||
|
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
|
||||||
|
clang6:
|
||||||
|
description: Build and run tests on clang 6 and AVX 2 with a cmake static build
|
||||||
|
executor: clang6
|
||||||
|
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF }
|
||||||
|
steps: [ cmake_test, cmake_install_test ]
|
||||||
|
clang10:
|
||||||
|
description: Build and run tests on clang 10 and AVX 2 with a cmake static build
|
||||||
|
executor: clang10
|
||||||
|
environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF }
|
||||||
|
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
|
||||||
|
# libcpp
|
||||||
|
libcpp-clang10:
|
||||||
|
description: Build and run tests on clang 10 and AVX 2 with a cmake static build and libc++
|
||||||
|
executor: clang10
|
||||||
|
environment: { CMAKE_FLAGS: -DSIMDJSON_USE_LIBCPP=ON -DBUILD_SHARED_LIBS=OFF }
|
||||||
|
steps: [ cmake_test, cmake_install_test, cmake_installed_test_cxx20 ]
|
||||||
|
# sanitize
|
||||||
|
sanitize-gcc10:
|
||||||
|
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
|
||||||
|
executor: gcc10
|
||||||
|
environment: { CMAKE_FLAGS: -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
|
||||||
steps: [ cmake_test ]
|
steps: [ cmake_test ]
|
||||||
gcc-avx-threaded:
|
sanitize-clang10:
|
||||||
description: Build, run tests and check performance on GCC 7 and AVX 2 with threads
|
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
|
||||||
executor: gcc7
|
executor: clang10
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_ENABLE_THREADS=ON }
|
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
|
||||||
steps: [ cmake_test ]
|
steps: [ cmake_test ]
|
||||||
|
threadsanitize-gcc10:
|
||||||
|
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
|
||||||
|
executor: gcc10
|
||||||
|
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
|
||||||
|
steps: [ cmake_test ]
|
||||||
|
threadsanitize-clang10:
|
||||||
|
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
|
||||||
|
executor: clang10
|
||||||
|
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE_THREADS=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
|
||||||
|
steps: [ cmake_test ]
|
||||||
|
# dynamic
|
||||||
|
dynamic-gcc10:
|
||||||
|
description: Build and run tests on GCC 10 and AVX 2 with a cmake dynamic build
|
||||||
|
executor: gcc10
|
||||||
|
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON }
|
||||||
|
steps: [ cmake_test, cmake_install_test ]
|
||||||
|
dynamic-clang10:
|
||||||
|
description: Build and run tests on clang 10 and AVX 2 with a cmake dynamic build
|
||||||
|
executor: clang10
|
||||||
|
environment: { CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON }
|
||||||
|
steps: [ cmake_test, cmake_install_test ]
|
||||||
|
|
||||||
clang-avx-unthreaded:
|
# unthreaded
|
||||||
description: Build, run tests and check performance on Clang 6 and AVX 2 *without* threads
|
unthreaded-gcc10:
|
||||||
executor: clang6
|
description: Build and run tests on GCC 10 and AVX 2 *without* threads
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_ENABLE_THREADS=OFF }
|
executor: gcc10
|
||||||
steps: [ init_clang6, cmake_test ]
|
environment: { CMAKE_FLAGS: -DSIMDJSON_ENABLE_THREADS=OFF }
|
||||||
clang-avx-threaded:
|
steps: [ cmake_test, cmake_install_test ]
|
||||||
description: Build, run tests and check performance on Clang 6 and AVX 2 with threads
|
unthreaded-clang10:
|
||||||
executor: clang6
|
description: Build and run tests on Clang 10 and AVX 2 *without* threads
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_ENABLE_THREADS=ON }
|
executor: clang10
|
||||||
steps: [ init_clang6, cmake_test ]
|
environment: { CMAKE_FLAGS: -DSIMDJSON_ENABLE_THREADS=OFF }
|
||||||
|
steps: [ cmake_test, cmake_install_test ]
|
||||||
|
|
||||||
gcc9-avx:
|
# noexcept
|
||||||
description: Build, run tests and check performance on GCC 9 and AVX 2
|
noexcept-gcc10:
|
||||||
executor: gcc9
|
description: Build and run tests on GCC 10 and AVX 2 with exceptions off
|
||||||
steps: [ make_test_strict ]
|
executor: gcc10
|
||||||
gcc8-avx:
|
environment: { CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF }
|
||||||
description: Build, run tests and check performance on GCC 8 and AVX 2
|
steps: [ cmake_test, cmake_install_test ]
|
||||||
executor: gcc9
|
noexcept-clang10:
|
||||||
steps: [ make_test_strict ]
|
description: Build and run tests on Clang 10 and AVX 2 with exceptions off
|
||||||
gcc-avx:
|
executor: clang10
|
||||||
description: Build, run tests and check performance on GCC 7 and AVX 2
|
environment: { CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF }
|
||||||
executor: gcc7
|
steps: [ cmake_test, cmake_install_test ]
|
||||||
steps: [ make_test ]
|
|
||||||
gcc-avx-dynamic:
|
|
||||||
description: Build, run tests and check performance on GCC 7 and AVX 2 with a cmake dynamic build
|
|
||||||
executor: gcc7
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF }
|
|
||||||
steps: [ cmake_test ]
|
|
||||||
gcc-avx-static:
|
|
||||||
description: Build, run tests and check performance on GCC 7 and AVX 2 with a cmake static build
|
|
||||||
executor: gcc7
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=ON }
|
|
||||||
steps: [ cmake_test ]
|
|
||||||
gcc-avx-google-benchmarks:
|
|
||||||
description: Build, run tests and check performance on GCC 7 with google benchmarks enabled
|
|
||||||
executor: gcc7
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON }
|
|
||||||
steps: [ cmake_test ]
|
|
||||||
gcc-avx-sanitize:
|
|
||||||
description: Build, run tests and check performance on GCC 7 and AVX 2 with a cmake sanitize build
|
|
||||||
executor: gcc7
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON }
|
|
||||||
steps: [ cmake_simple_test ]
|
|
||||||
|
|
||||||
gcc-sse:
|
#
|
||||||
description: Build, run tests and check performance on GCC 7 and SSE 4.2
|
# Misc.
|
||||||
executor: gcc7
|
#
|
||||||
environment: { ARCHFLAGS: -march=nehalem }
|
|
||||||
steps: [ make_test ]
|
|
||||||
gcc-sse-dynamic:
|
|
||||||
description: Build, run tests and check performance on GCC 7 and SSE 4.2 with a cmake dynamic build
|
|
||||||
executor: gcc7
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF }
|
|
||||||
steps: [ cmake_test ]
|
|
||||||
gcc-sse-static:
|
|
||||||
description: Build, run tests and check performance on GCC 7 and SSE 4.2 with a cmake static build
|
|
||||||
executor: gcc7
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=ON }
|
|
||||||
steps: [ cmake_test ]
|
|
||||||
gcc-sse-sanitize:
|
|
||||||
description: Build, run tests and check performance on GCC 7 and SSE 4.2 with a cmake sanitize build
|
|
||||||
executor: gcc7
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON }
|
|
||||||
steps: [ cmake_simple_test ]
|
|
||||||
|
|
||||||
clang-avx:
|
# make (test and checkperf)
|
||||||
description: Build, run tests and check performance on clang 6 and AVX 2
|
arch-haswell-gcc10:
|
||||||
executor: clang6
|
description: Build, run tests and check performance on GCC 10 with -march=haswell
|
||||||
steps: [ init_clang6, make_test ]
|
executor: gcc10
|
||||||
clang-avx-dynamic:
|
environment: { CXXFLAGS: -march=haswell }
|
||||||
description: Build, run tests and check performance on clang 6 and AVX 2 with a cmake dynamic build
|
steps: [ cmake_test ]
|
||||||
executor: clang6
|
arch-nehalem-gcc10:
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF }
|
description: Build, run tests and check performance on GCC 10 with -march=nehalem
|
||||||
steps: [ init_clang6, cmake_test ]
|
executor: gcc10
|
||||||
clang-avx-static:
|
environment: { CXXFLAGS: -march=nehalem }
|
||||||
description: Build, run tests and check performance on clang 6 and AVX 2 with a cmake static build
|
steps: [ cmake_test ]
|
||||||
executor: clang6
|
sanitize-haswell-gcc10:
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=ON }
|
description: Build and run tests on GCC 10 and AVX 2 with a cmake sanitize build
|
||||||
steps: [ init_clang6, cmake_test ]
|
executor: gcc10
|
||||||
clang-avx-google-benchmarks:
|
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
|
||||||
description: Build, run tests and check performance on clang 6 with google benchmarks enabled
|
steps: [ cmake_test ]
|
||||||
executor: clang6
|
sanitize-haswell-clang10:
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON }
|
description: Build and run tests on clang 10 and AVX 2 with a cmake sanitize build
|
||||||
steps: [ init_clang6, cmake_test ]
|
executor: clang10
|
||||||
clang-avx-sanitize:
|
environment: { CXXFLAGS: -march=haswell, CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON -DSIMDJSON_NO_FORCE_INLINING=ON -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: --output-on-failure -LE explicitonly }
|
||||||
description: Build, run tests and check performance on clang 6 and AVX 2 with a cmake sanitize build
|
steps: [ cmake_test ]
|
||||||
executor: clang6
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON }
|
|
||||||
steps: [ init_clang6, cmake_simple_test ]
|
|
||||||
|
|
||||||
clang-sse:
|
|
||||||
description: Build, run tests and check performance on Clang 6 and SSE 4.2
|
|
||||||
executor: clang6
|
|
||||||
environment: { ARCHFLAGS: -march=nehalem }
|
|
||||||
steps: [ init_clang6, make_test ]
|
|
||||||
clang-sse-dynamic:
|
|
||||||
description: Build, run tests and check performance on Clang 6 and SSE 4.2 with a cmake dynamic build
|
|
||||||
executor: clang6
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF }
|
|
||||||
steps: [ init_clang6, cmake_test ]
|
|
||||||
clang-sse-static:
|
|
||||||
description: Build, run tests and check performance on Clang 6 and SSE 4.2 with a cmake static build
|
|
||||||
executor: clang6
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=ON }
|
|
||||||
steps: [ init_clang6, cmake_test ]
|
|
||||||
clang-sse-sanitize:
|
|
||||||
description: Build, run tests and check performance on Clang 6 and SSE 4.2 with a cmake sanitize build
|
|
||||||
executor: clang6
|
|
||||||
environment: { CMAKE_TEST_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON }
|
|
||||||
steps: [ init_clang6, cmake_simple_test ]
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2.1
|
version: 2.1
|
||||||
build_and_test:
|
build_and_test:
|
||||||
jobs:
|
jobs:
|
||||||
- gcc9-avx
|
# full multi-implementation tests
|
||||||
- gcc8-avx
|
#- gcc7 tested on GitHub actions
|
||||||
- gcc-avx
|
- gcc10 # do not delete this as it tests our performance
|
||||||
- gcc-avx-dynamic
|
- clang6
|
||||||
- gcc-avx-static
|
#- clang10 # this gets tested a lot below
|
||||||
- gcc-avx-google-benchmarks
|
|
||||||
- gcc-avx-sanitize
|
# libc++
|
||||||
- gcc-sse
|
- libcpp-clang10
|
||||||
- gcc-sse-dynamic
|
|
||||||
- gcc-sse-static
|
# full single-implementation tests
|
||||||
- gcc-sse-sanitize
|
- sanitize-gcc10
|
||||||
- clang-avx
|
- sanitize-clang10
|
||||||
- clang-avx-dynamic
|
- threadsanitize-gcc10
|
||||||
- clang-avx-static
|
- threadsanitize-clang10
|
||||||
- clang-avx-google-benchmarks
|
- dynamic-gcc10
|
||||||
- clang-avx-sanitize
|
- dynamic-clang10
|
||||||
- clang-sse
|
- unthreaded-gcc10
|
||||||
- clang-sse-dynamic
|
- unthreaded-clang10
|
||||||
- clang-sse-static
|
|
||||||
- clang-sse-sanitize
|
# no exceptions
|
||||||
- gcc-avx-threaded
|
- noexcept-gcc10
|
||||||
- gcc-avx-unthreaded
|
- noexcept-clang10
|
||||||
- clang-avx-threaded
|
|
||||||
- clang-avx-unthreaded
|
# quicker make single-implementation tests
|
||||||
|
- arch-haswell-gcc10
|
||||||
|
- arch-nehalem-gcc10
|
||||||
|
|
||||||
|
|
||||||
|
# sanitized single-implementation tests
|
||||||
|
- sanitize-haswell-gcc10
|
||||||
|
- sanitize-haswell-clang10
|
||||||
|
|
||||||
|
# testing "just the library"
|
||||||
|
- justlib-gcc10
|
||||||
|
|
||||||
|
# testing asserts
|
||||||
|
- assert-gcc10
|
||||||
|
- assert-clang10
|
||||||
|
|
||||||
# TODO add windows: https://circleci.com/docs/2.0/configuration-reference/#windows
|
# TODO add windows: https://circleci.com/docs/2.0/configuration-reference/#windows
|
||||||
|
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
task:
|
||||||
|
timeout_in: 120m
|
||||||
|
freebsd_instance:
|
||||||
|
matrix:
|
||||||
|
- image_family: freebsd-13-0-snap
|
||||||
|
|
||||||
|
env:
|
||||||
|
ASSUME_ALWAYS_YES: YES
|
||||||
|
simdjson_DEPENDENCY_CACHE_DIR: $HOME/.dep_cache
|
||||||
|
dep_cache:
|
||||||
|
folder: $HOME/.dep_cache
|
||||||
|
reupload_on_changes: false
|
||||||
|
fingerprint_script: cat dependencies/CMakeLists.txt
|
||||||
|
setup_script:
|
||||||
|
- pkg update -f
|
||||||
|
- pkg install bash
|
||||||
|
- pkg install cmake
|
||||||
|
- pkg install git
|
||||||
|
build_script:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake -DSIMDJSON_BASH=OFF -DSIMDJSON_GIT=OFF ..
|
||||||
|
- make
|
||||||
|
test_script:
|
||||||
|
- cd build
|
||||||
|
- ctest --output-on-failure -LE explicitonly
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
*
|
*
|
||||||
!.git
|
!.git
|
||||||
!Makefile
|
!Makefile
|
||||||
!amalgamation.sh
|
!amalgamate.py
|
||||||
!benchmark
|
!benchmark
|
||||||
!dependencies
|
!dependencies
|
||||||
!include
|
!include
|
||||||
@@ -12,4 +12,4 @@
|
|||||||
!src
|
!src
|
||||||
!style
|
!style
|
||||||
!tests
|
!tests
|
||||||
!tools
|
!tools
|
||||||
|
|||||||
+370
-504
@@ -1,207 +1,420 @@
|
|||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-quicktests-libc
|
name: gcc9
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: quicktests
|
- name: Build and Test
|
||||||
image: conanio/clang8
|
image: gcc:9
|
||||||
|
environment:
|
||||||
|
CC: gcc
|
||||||
|
CXX: g++
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=icelake;haswell;westmere;fallback
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
|
||||||
|
- apt-get update -qq
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS -L acceptance -LE per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=icelake ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: clang6
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
|
steps:
|
||||||
|
- name: Build and Test
|
||||||
|
image: conanio/clang60
|
||||||
user: root
|
user: root
|
||||||
commands: [ EXTRAFLAGS=-stdlib=libc++ make quicktests ]
|
environment:
|
||||||
|
CC: clang-6.0
|
||||||
|
CXX: clang++-6.0
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=icelake;haswell;westmere;fallback
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS -L acceptance -LE per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=icelake ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=haswell ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=westmere ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-quicktests
|
name: dynamic-gcc9
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: quicktests
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: gcc:9
|
||||||
commands: [ make quicktests ]
|
environment:
|
||||||
|
CC: gcc
|
||||||
|
CXX: g++
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
|
||||||
|
- apt-get update -qq
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-checkperf
|
name: dynamic-clang9
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: checkperf
|
- name: Build and Test
|
||||||
|
image: conanio/clang9
|
||||||
|
user: root
|
||||||
|
environment:
|
||||||
|
CC: clang-9
|
||||||
|
CXX: clang++-9
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: sanitize-gcc9
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
|
steps:
|
||||||
|
- name: Build and Test
|
||||||
|
image: gcc:9
|
||||||
|
environment:
|
||||||
|
CC: gcc
|
||||||
|
CXX: g++
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=icelake;haswell;westmere;fallback
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
|
||||||
|
- apt-get update -qq
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L acceptance -LE per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=icelake ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=haswell ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=westmere ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: sanitize-clang9
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
|
steps:
|
||||||
|
- name: Build and Test
|
||||||
|
image: conanio/clang9
|
||||||
|
user: root
|
||||||
|
environment:
|
||||||
|
CC: clang-9
|
||||||
|
CXX: clang++-9
|
||||||
|
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=icelake;haswell;westmere;fallback
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L acceptance -LE per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=icelake ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=haswell ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=westmere ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: cpp20-clang11-libcpp
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
|
steps:
|
||||||
|
- name: Build and Test
|
||||||
|
image: pauldreik/llvm-11
|
||||||
|
user: root
|
||||||
|
environment:
|
||||||
|
CC: clang-11
|
||||||
|
CXX: clang++-11
|
||||||
|
CMAKE_FLAGS: -GNinja
|
||||||
|
BUILD_FLAGS:
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
CXXFLAGS: -std=c++20 -stdlib=libc++
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: arm64-gcc8
|
||||||
|
platform: { os: linux, arch: arm64 }
|
||||||
|
steps:
|
||||||
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: gcc:8
|
||||||
environment:
|
environment:
|
||||||
CHECKPERF_REPOSITORY: https://github.com/lemire/simdjson
|
CC: gcc
|
||||||
commands: [ sed '/^$/Q' /proc/cpuinfo, make checkperf ]
|
CXX: g++
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=arm64;fallback
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
|
||||||
|
- apt-get update -qq
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS -L acceptance -LE per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=arm64 ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-build
|
name: arm64-clang6
|
||||||
|
platform: { os: linux, arch: arm64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: debian:buster-backports
|
||||||
commands: [ make, make amalgamate ]
|
environment:
|
||||||
|
CC: clang-6.0
|
||||||
|
CXX: clang++-6.0
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- apt-get -qq update
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- apt-get install -y clang-6.0 git
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-slowtests
|
name: arm64-dynamic-gcc8
|
||||||
|
platform: { os: linux, arch: arm64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: slowtests
|
- name: Build and Test
|
||||||
image: gcc:8
|
|
||||||
commands: [ make slowtests ]
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: x64-noexceptions-quicktests
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: quicktests
|
|
||||||
image: gcc:8
|
image: gcc:8
|
||||||
environment:
|
environment:
|
||||||
EXTRA_FLAGS: -fno-exceptions
|
CC: gcc
|
||||||
commands: [ make quicktests ]
|
CXX: g++
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
|
||||||
|
- apt-get update -qq
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-noexceptions-build
|
name: arm64-dynamic-clang6
|
||||||
|
platform: { os: linux, arch: arm64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: Build and Test
|
||||||
|
image: debian:buster-backports
|
||||||
|
environment:
|
||||||
|
CC: clang-6.0
|
||||||
|
CXX: clang++-6.0
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=ON
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- apt-get -qq update
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- apt-get install -y clang-6.0 git
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: arm64-sanitize-gcc8
|
||||||
|
platform: { os: linux, arch: arm64 }
|
||||||
|
steps:
|
||||||
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: gcc:8
|
||||||
environment:
|
environment:
|
||||||
EXTRA_FLAGS: -fno-exceptions
|
BUILD_FLAGS: -- -j
|
||||||
commands: [ make, make amalgamate ]
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_IMPLEMENTATION=arm64;fallback
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
CC: gcc
|
||||||
|
CXX: g++
|
||||||
|
commands:
|
||||||
|
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
|
||||||
|
- apt-get update -qq
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- apt-get install -y libstdc++6
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L acceptance -LE per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=arm64 ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-noexceptions-slowtests
|
name: arm64-sanitize-clang6
|
||||||
|
platform: { os: linux, arch: arm64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: slowtests
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: debian:buster-backports
|
||||||
environment:
|
environment:
|
||||||
EXTRA_FLAGS: -fno-exceptions
|
CC: clang-6.0
|
||||||
commands: [ make slowtests ]
|
CXX: clang++-6.0
|
||||||
|
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION=arm64;fallback
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- apt-get -qq update
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- apt-get install -y clang-6.0 git
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L acceptance -LE per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=arm64 ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- ASAN_OPTIONS="detect_leaks=0" ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now.
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: arm64-quicktests
|
name: ninja-clang9
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: quicktests
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: conanio/clang9
|
||||||
commands: [ make quicktests ]
|
user: root
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: arm64-checkperf
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: checkperf
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
environment:
|
||||||
CHECKPERF_REPOSITORY: https://github.com/lemire/simdjson
|
CC: clang-9
|
||||||
commands: [ cat /proc/cpuinfo, make checkperf ]
|
CXX: clang++-9
|
||||||
|
BUILD_FLAGS: -- -j 4
|
||||||
|
CMAKE_FLAGS: -GNinja -DBUILD_SHARED_LIBS=OFF
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
CXXFLAGS: -stdlib=libc++
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: arm64-build
|
name: libcpp-clang9
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: conanio/clang9
|
||||||
commands: [ make, make amalgamate ]
|
user: root
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: arm64-slowtests
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: slowtests
|
|
||||||
image: gcc:8
|
|
||||||
commands: [ make slowtests ]
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: x64-amalgamated-build
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
environment:
|
||||||
SIMDJSON_TEST_AMALGAMATED_HEADERS: 1
|
CC: clang-9
|
||||||
commands: [ make amalgamate, make ]
|
CXX: clang++-9
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
CXXFLAGS: -stdlib=libc++
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-amalgamated-quicktests
|
name: libcpp-clang7
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: quicktests
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: conanio/clang7
|
||||||
|
user: root
|
||||||
environment:
|
environment:
|
||||||
SIMDJSON_TEST_AMALGAMATED_HEADERS: 1
|
CC: clang-7
|
||||||
commands: [ make amalgamate, make quicktests ]
|
CXX: clang++-7
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
CXXFLAGS: -stdlib=libc++
|
||||||
|
commands:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: x64-amalgamated-slowtests
|
name: noexceptions-gcc9
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: slowtests
|
- name: Build and Test
|
||||||
image: gcc:8
|
image: gcc:9
|
||||||
environment:
|
environment:
|
||||||
SIMDJSON_TEST_AMALGAMATED_HEADERS: 1
|
CC: gcc
|
||||||
commands: [ make amalgamate, make slowtests ]
|
CXX: g++
|
||||||
|
BUILD_FLAGS: -- -j
|
||||||
|
CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF
|
||||||
|
CTEST_FLAGS: -j4 --output-on-failure -LE explicitonly
|
||||||
|
commands:
|
||||||
|
- echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list
|
||||||
|
- apt-get update -qq
|
||||||
|
- apt-get -t buster-backports install -y cmake
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . $BUILD_FLAGS
|
||||||
|
- ctest $CTEST_FLAGS
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: arm64-fuzz
|
||||||
|
platform: { os: linux, arch: arm64 }
|
||||||
|
steps:
|
||||||
|
- name: Build and run fuzzers shortly
|
||||||
|
image: ubuntu:20.04
|
||||||
|
environment:
|
||||||
|
CC: clang
|
||||||
|
CXX: clang++
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
ASAN_OPTIONS: detect_leaks=0
|
||||||
|
commands:
|
||||||
|
- apt-get -qq update
|
||||||
|
- apt-get install -q -y clang cmake git wget zip ninja-build
|
||||||
|
- wget -O corpus.tar.gz https://readonly:readonly@www.pauldreik.se/fuzzdata/index.php?project=simdjson
|
||||||
|
- tar xf corpus.tar.gz && rm corpus.tar.gz
|
||||||
|
- fuzz/build_like_ossfuzz.sh
|
||||||
|
- mkdir -p common_out
|
||||||
|
- for fuzzer in build/fuzz/fuzz_* ; do echo $fuzzer;$fuzzer common_out out/* -max_total_time=40; done
|
||||||
---
|
---
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: stylecheck
|
name: stylecheck
|
||||||
|
platform: { os: linux, arch: amd64 }
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Build and Test
|
- name: Build and Test
|
||||||
image: ubuntu:18.04
|
image: ubuntu:18.04
|
||||||
@@ -209,350 +422,3 @@ steps:
|
|||||||
- apt-get update -y
|
- apt-get update -y
|
||||||
- apt-get install -y python clang-format
|
- apt-get install -y python clang-format
|
||||||
- ./style/run-clang-format.py -r include/ benchmark/ src/ tests/
|
- ./style/run-clang-format.py -r include/ benchmark/ src/ tests/
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_clang_cmake_dynamic
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: ubuntu:18.04
|
|
||||||
environment:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y clang make cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_clang_cmake_no_exceptions
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: ubuntu:18.04
|
|
||||||
environment:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y clang make cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_clang_cmake_static
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: ubuntu:18.04
|
|
||||||
environment:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y clang make cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_gcc_cmake_static
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
|
||||||
CC: gcc
|
|
||||||
CXX: g++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_gcc_cmake_dynamic
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
|
||||||
CC: gcc
|
|
||||||
CXX: g++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: arm64_clang_cmake_dynamic
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: ubuntu:18.04
|
|
||||||
environment:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y clang make cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: arm64_gcc_cmake_dynamic
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
|
||||||
CC: gcc
|
|
||||||
CXX: g++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: arm64_clang_cmake_static
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: ubuntu:18.04
|
|
||||||
environment:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y clang make cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: arm64_gcc_cmake_static
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
|
||||||
CC: gcc
|
|
||||||
CXX: g++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=ON
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ctest --output-on-failure
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: arm64_clang_cmake_sanitize
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: ubuntu:18.04
|
|
||||||
environment:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y clang make cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ASAN_OPTIONS="detect_leaks=0" ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: arm64_gcc_cmake_sanitize
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
|
||||||
CC: gcc
|
|
||||||
CXX: g++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ASAN_OPTIONS="detect_leaks=0" ctest --output-on-failure
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_clang_cmake_sanitize
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: ubuntu:18.04
|
|
||||||
environment:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON
|
|
||||||
commands:
|
|
||||||
- apt-get update -y
|
|
||||||
- apt-get install -y make $CC g++ cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ASAN_OPTIONS="detect_leaks=0" ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_gcc_cmake_sanitize
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
|
||||||
CC: gcc
|
|
||||||
CXX: g++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ASAN_OPTIONS="detect_leaks=0" ctest --output-on-failure
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_clang_cmake_sanitize_noavx
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: ubuntu:18.04
|
|
||||||
environment:
|
|
||||||
CC: clang
|
|
||||||
CXX: clang++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION_HASWELL=OFF
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y clang make cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ASAN_OPTIONS="detect_leaks=0" ctest --output-on-failure
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: amd64_gcc_cmake_sanitize_noavx
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Build and Test
|
|
||||||
image: gcc:8
|
|
||||||
environment:
|
|
||||||
CC: gcc
|
|
||||||
CXX: g++
|
|
||||||
CMAKE_FLAGS: -DSIMDJSON_SANITIZE=ON -DSIMDJSON_IMPLEMENTATION_HASWELL=OFF
|
|
||||||
commands:
|
|
||||||
- apt-get update -qq
|
|
||||||
- apt-get install -y cmake
|
|
||||||
- $CC --version
|
|
||||||
- mkdir build && cd build
|
|
||||||
- cmake $CMAKE_FLAGS ..
|
|
||||||
- make -j
|
|
||||||
- ASAN_OPTIONS="detect_leaks=0" ctest --output-on-failure
|
|
||||||
|
|||||||
+10
-10
@@ -3,7 +3,7 @@
|
|||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
# we don't want json files to be modified for this project
|
# we don't want json files to be modified for this project
|
||||||
*.json binary
|
*.json binary diff=astextplain
|
||||||
|
|
||||||
|
|
||||||
# Common settings that generally should always be used with your language specific settings
|
# Common settings that generally should always be used with your language specific settings
|
||||||
@@ -80,15 +80,15 @@
|
|||||||
.gitignore export-ignore
|
.gitignore export-ignore
|
||||||
|
|
||||||
# Sources
|
# Sources
|
||||||
*.c text diff=c
|
*.c text eol=lf diff=c
|
||||||
*.cc text diff=cpp
|
*.cc text eol=lf diff=cpp
|
||||||
*.cxx text diff=cpp
|
*.cxx text eol=lf diff=cpp
|
||||||
*.cpp text diff=cpp
|
*.cpp text eol=lf diff=cpp
|
||||||
*.c++ text diff=cpp
|
*.c++ text eol=lf diff=cpp
|
||||||
*.hpp text diff=cpp
|
*.hpp text eol=lf diff=cpp
|
||||||
*.h text diff=c
|
*.h text eol=lf diff=c
|
||||||
*.h++ text diff=cpp
|
*.h++ text eol=lf diff=cpp
|
||||||
*.hh text diff=cpp
|
*.hh text eol=lf diff=cpp
|
||||||
|
|
||||||
# Compiled Object files
|
# Compiled Object files
|
||||||
*.slo binary
|
*.slo binary
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug (unverified)
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Before submitting an issue, please ensure that you have read the documentation:
|
||||||
|
|
||||||
|
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
|
||||||
|
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
|
||||||
|
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
|
||||||
|
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
|
||||||
|
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
A compiler or static-analyzer warning is not a bug.
|
||||||
|
|
||||||
|
We are committed to providing good documentation. We accept the lack of documentation or a misleading documentation as a bug (a 'documentation bug').
|
||||||
|
|
||||||
|
We accept the identification of an issue by a sanitizer or some checker tool (e.g., valgrind) as a bug, but you must first ensure that it is not a false positive.
|
||||||
|
|
||||||
|
We recommend that you run your tests using different optimization levels.
|
||||||
|
|
||||||
|
Before reporting a bug, please ensure that you have read our documentation.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behaviour: provide a code sample if possible.
|
||||||
|
|
||||||
|
If we cannot reproduce the issue, then we cannot address it. Note that a stack trace from your own program is not enough. A sample of your source code is insufficient: please provide a complete test for us to reproduce the issue. Please reduce the issue: use as small and as simple an example of the bug as possible.
|
||||||
|
|
||||||
|
It should be possible to trigger the bug by using solely simdjson with our default build setup. If you can only observe the bug within some specific context, with some other software, please reduce the issue first.
|
||||||
|
|
||||||
|
**simjson release**
|
||||||
|
|
||||||
|
Unless you plan to contribute to simdjson, you should only work from releases. Please be mindful that our main branch may have additional features, bugs and documentation items.
|
||||||
|
|
||||||
|
It is fine to report bugs against our main branch, but if that is what you are doing, please be explicit.
|
||||||
|
|
||||||
|
**Configuration (please complete the following information if relevant)**
|
||||||
|
- OS: [e.g. Ubuntu 16.04.6 LTS]
|
||||||
|
- Compiler [e.g. Apple clang version 11.0.3 (clang-1103.0.32.59) x86_64-apple-darwin19.4.0]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
- Optimization setting (e.g., -O3)
|
||||||
|
|
||||||
|
We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux systems. Please ensure that your configuration is supported before labelling the issue as a bug. In particular, we do not support legacy 32-bit systems.
|
||||||
|
|
||||||
|
**Indicate whether you are willing or able to provide a bug fix as a pull request**
|
||||||
|
|
||||||
|
If you plan to contribute to simdjson, please read our
|
||||||
|
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
|
||||||
|
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Before submitting an issue, please ensure that you have read the documentation:
|
||||||
|
|
||||||
|
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
|
||||||
|
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
|
||||||
|
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
|
||||||
|
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
|
||||||
|
|
||||||
|
We do not make changes to simdjson without clearly identifiable benefits, which typically means either performance improvements, bug fixes or new features. Avoid bike-shedding: we all have opinions about how to write code, but we want to focus on what makes simdjson objectively better.
|
||||||
|
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
Please provide a clear rationale for the feature. Be advised that simdjson is a community-based project: you should consider providing help.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
|
|
||||||
|
** Are you willing to contribute code or documentation toward this new feature? **
|
||||||
|
If you plan to contribute to simdjson, please read our
|
||||||
|
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
|
||||||
|
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
name: Standard issue template
|
||||||
|
about: Issue
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Before submitting an issue, please ensure that you have read the documentation:
|
||||||
|
|
||||||
|
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
|
||||||
|
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
|
||||||
|
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
|
||||||
|
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
|
||||||
|
|
||||||
|
We do not make changes to simdjson without clearly identifiable benefits, which typically means either performance improvements, bug fixes or new features. Avoid bike-shedding: we all have opinions about how to write code, but we want to focus on what makes simdjson objectively better.
|
||||||
|
|
||||||
|
Is your issue:
|
||||||
|
|
||||||
|
1. A bug report? If so, please point at a reproducible test. Indicate whether you are willing or able to provide a bug fix as a pull request.
|
||||||
|
|
||||||
|
2. A build issue? If so, provide all possible details regarding your system configuration. If we cannot reproduce your issue, we cannot fix it.
|
||||||
|
|
||||||
|
3. A feature request? Please provide a clear rationale for the feature. Be advised that simdjson is a community-based project: you should consider providing help.
|
||||||
|
|
||||||
|
4. A documentation issue? Can you suggest an improvement?
|
||||||
|
|
||||||
|
|
||||||
|
If you plan to contribute to simdjson, please read our
|
||||||
|
* CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
|
||||||
|
* HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
|
||||||
|
Our tests check whether you have introduced trailing white space. If such a test fails, please check the "artifacts button" above, which if you click it gives a link to a downloadable file to help you identify the issue. You can also run scripts/remove_trailing_whitespace.sh locally if you have a bash shell and the sed command available on your system.
|
||||||
|
|
||||||
|
If you plan to contribute to simdjson, please read our
|
||||||
|
|
||||||
|
CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
|
||||||
|
HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Alpine Linux
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: start docker
|
||||||
|
run: |
|
||||||
|
docker run -w /src -dit --name alpine -v $PWD:/src alpine:latest
|
||||||
|
echo 'docker exec alpine "$@";' > ./alpine.sh
|
||||||
|
chmod +x ./alpine.sh
|
||||||
|
- name: install packages
|
||||||
|
run: |
|
||||||
|
./alpine.sh apk update
|
||||||
|
./alpine.sh apk add build-base cmake g++ linux-headers git bash
|
||||||
|
- name: cmake
|
||||||
|
run: |
|
||||||
|
./alpine.sh cmake -DSIMDJSON_DEVELOPER_MODE=ON -B build_for_alpine
|
||||||
|
- name: build
|
||||||
|
run: |
|
||||||
|
./alpine.sh cmake --build build_for_alpine
|
||||||
|
- name: test
|
||||||
|
run: |
|
||||||
|
./alpine.sh bash -c "cd build_for_alpine && ctest -LE explicitonly"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
name: CIFuzz
|
||||||
|
on: [pull_request]
|
||||||
|
jobs:
|
||||||
|
Fuzzing:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Build Fuzzers
|
||||||
|
id: build
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'simdjson'
|
||||||
|
dry-run: false
|
||||||
|
- name: Run Fuzzers
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'simdjson'
|
||||||
|
fuzz-seconds: 600
|
||||||
|
dry-run: false
|
||||||
|
- name: Upload Crash
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
if: failure() && steps.build.outcome == 'success'
|
||||||
|
with:
|
||||||
|
name: artifacts
|
||||||
|
path: ./out/artifacts
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Detect trailing whitespace
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
whitespace:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Remove whitespace and check the diff
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
scripts/remove_trailing_whitespace.sh
|
||||||
|
git diff >whitespace.patch
|
||||||
|
cat whitespace.patch
|
||||||
|
if [ $(wc -c <whitespace.patch) -ne 0 ] ; then
|
||||||
|
echo " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! "
|
||||||
|
echo "You have trailing whitespace, please download the artifact"
|
||||||
|
echo "and apply with git apply <whitespace.patch or"
|
||||||
|
echo "run scripts/remove_trailing_whitespace.sh locally."
|
||||||
|
echo " !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! "
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "no trailing whitespace found, good!"
|
||||||
|
fi
|
||||||
|
- name: Archive whitespace patch
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: whitespace-patch
|
||||||
|
path: |
|
||||||
|
whitespace.patch
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
+113
-74
@@ -1,21 +1,33 @@
|
|||||||
name: Run fuzzers on stored corpus and test it with valgrind
|
name: Fuzz and run valgrind
|
||||||
|
|
||||||
# In the case of a pull request happening at the same time as a cron
|
|
||||||
# job, there is a risk two jobs run at the same time. Therefore,
|
|
||||||
# the corpus is only uploaded for the master branch. Pull requests will
|
|
||||||
# fuzz for a short while, but the results are not uploaded.
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
schedule:
|
schedule:
|
||||||
- cron: 23 */8 * * *
|
- cron: 23 */8 * * *
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
allfuzzers: parser dump dump_raw_tape print_json
|
# fuzzers that change behaviour with SIMDJSON_FORCE_IMPLEMENTATION
|
||||||
artifactsprefix: -artifact_prefix=fuzzfailure/
|
defaultimplfuzzers: atpointer dump dump_raw_tape element minify parser print_json
|
||||||
|
# fuzzers that loop over the implementations themselves, or don't need to switch.
|
||||||
|
implfuzzers: implementations minifyimpl ndjson ondemand padded utf8
|
||||||
|
implementations: haswell westmere fallback
|
||||||
|
UBSAN_OPTIONS: halt_on_error=1
|
||||||
|
MAXLEN: -max_len=4000
|
||||||
|
CLANGVERSION: 11
|
||||||
|
# which optimization level to use for the sanitizer build (see build_fuzzer.variants.sh)
|
||||||
|
OPTLEVEL: -O3
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install packages necessary for building
|
- name: Install packages necessary for building
|
||||||
run: |
|
run: |
|
||||||
@@ -23,105 +35,132 @@ jobs:
|
|||||||
sudo apt-get install --quiet ninja-build valgrind zip unzip
|
sudo apt-get install --quiet ninja-build valgrind zip unzip
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 8
|
sudo ./llvm.sh $CLANGVERSION
|
||||||
|
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
id: cache-corpus
|
||||||
|
with:
|
||||||
|
path: out/
|
||||||
|
key: corpus-${{ github.run_id }}
|
||||||
|
restore-keys: corpus-
|
||||||
|
|
||||||
|
- name: show statistics for the cached corpus
|
||||||
|
run: |
|
||||||
|
echo number of files in github action corpus cache:
|
||||||
|
find out -type f |wc -l
|
||||||
|
|
||||||
- name: Create and prepare the initial seed corpus
|
- name: Create and prepare the initial seed corpus
|
||||||
run: |
|
run: |
|
||||||
fuzz/build_corpus.sh
|
fuzz/build_corpus.sh
|
||||||
mv corpus.zip seed_corpus.zip
|
mv corpus.zip seed_corpus.zip
|
||||||
- name: Download the corpus from the last run
|
mkdir seedcorpus
|
||||||
run: |
|
unzip -q -d seedcorpus seed_corpus.zip
|
||||||
wget --quiet https://dl.bintray.com/pauldreik/simdjson-fuzz-corpus/corpus/corpus.tar
|
|
||||||
tar xf corpus.tar
|
|
||||||
rm corpus.tar
|
|
||||||
- name: List clang versions
|
- name: List clang versions
|
||||||
run: |
|
run: |
|
||||||
ls /usr/bin/clang*
|
ls /usr/bin/clang*
|
||||||
which clang++
|
which clang++
|
||||||
clang++ --version
|
clang++ --version
|
||||||
|
|
||||||
- name: Build all the variants
|
- name: Build all the variants
|
||||||
run: fuzz/build_fuzzer_variants.sh
|
run: CLANGSUFFIX=-$CLANGVERSION fuzz/build_fuzzer_variants.sh
|
||||||
- name: Verify that the oss-fuzz seed corpus passes without problems
|
|
||||||
|
- name: Explore fast (release build, default implementation)
|
||||||
run: |
|
run: |
|
||||||
mkdir seedcorpus
|
set -eux
|
||||||
unzip -q -d seedcorpus seed_corpus.zip
|
for fuzzer in $defaultimplfuzzers $implfuzzers; do
|
||||||
for buildvariant in noavx withavx; do
|
mkdir -p out/$fuzzer # in case this is a new fuzzer, or the github action cached corpus is broken
|
||||||
for fuzzer in $allfuzzers; do
|
# get input from everyone else (corpus cross pollination)
|
||||||
build-ossfuzz-$buildvariant/fuzz/fuzz_$fuzzer seedcorpus -max_total_time=1
|
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
|
||||||
|
build-fast/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=30 $MAXLEN
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Fuzz default impl. fuzzers with sanitizer+asserts (good at detecting errors)
|
||||||
|
run: |
|
||||||
|
set -eux
|
||||||
|
for fuzzer in $defaultimplfuzzers; do
|
||||||
|
# get input from everyone else (corpus cross pollination)
|
||||||
|
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
|
||||||
|
for implementation in $implementations; do
|
||||||
|
export SIMDJSON_FORCE_IMPLEMENTATION=$implementation
|
||||||
|
build-sanitizers$OPTLEVEL/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=20 $MAXLEN
|
||||||
done
|
done
|
||||||
done
|
|
||||||
- name: Run the fastest fuzzer to explore fast
|
|
||||||
run: |
|
|
||||||
for fuzzer in $allfuzzers; do
|
|
||||||
mkdir -p out/$fuzzer # in case this is a new fuzzer, or corpus.tar is broken
|
|
||||||
build-ossfuzz-fast8/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=30 $artifactsprefix || touch failed
|
|
||||||
# make sure the failing output is visible in the log
|
|
||||||
if [ -e failed ] ; then
|
|
||||||
ls fuzzfailure/* |xargs -n1 base64
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
- name: Run the other fuzzer variants for $fuzzer, with sanitizers etc
|
|
||||||
run: |
|
|
||||||
set -x
|
|
||||||
for fuzzer in $allfuzzers; do
|
|
||||||
build-ossfuzz-withavx/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=20 $artifactsprefix || touch failed
|
|
||||||
build-ossfuzz-noavx/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=10 $artifactsprefix || touch failed
|
|
||||||
build-ossfuzz-noavx8/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=10 $artifactsprefix || touch failed
|
|
||||||
if [ -e failed ] ; then
|
|
||||||
# make sure the failing output is visible in the log
|
|
||||||
ls fuzzfailure/* |xargs -n1 base64
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo disable msan runs, it fails inside the fuzzing engine and not the fuzzed code!
|
|
||||||
echo build-ossfuzz-msan-noavx8/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=10 -reload=0 $artifactsprefix
|
|
||||||
echo build-ossfuzz-msan-withavx8/fuzz/fuzz_$fuzzer out/$fuzzer -max_total_time=10 -reload=0 $artifactsprefix
|
|
||||||
echo now have $(ls out/$fuzzer |wc -l) files in corpus
|
echo now have $(ls out/$fuzzer |wc -l) files in corpus
|
||||||
done
|
done
|
||||||
- name: Minimize the corpus with the fast fuzzer
|
|
||||||
|
- name: Fuzz differential impl. fuzzers with sanitizer+asserts (good at detecting errors)
|
||||||
run: |
|
run: |
|
||||||
for fuzzer in $allfuzzers; do
|
set -eux
|
||||||
|
for fuzzer in $implfuzzers; do
|
||||||
|
# get input from everyone else (corpus cross pollination)
|
||||||
|
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
|
||||||
|
build-sanitizers$OPTLEVEL/fuzz/fuzz_$fuzzer out/$fuzzer $others seedcorpus -max_total_time=20 $MAXLEN
|
||||||
|
echo now have $(ls out/$fuzzer |wc -l) files in corpus
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Minimize the corpus with the fast fuzzer on the default implementation
|
||||||
|
run: |
|
||||||
|
set -eux
|
||||||
|
for fuzzer in $defaultimplfuzzers $implfuzzers; do
|
||||||
mkdir -p out/cmin/$fuzzer
|
mkdir -p out/cmin/$fuzzer
|
||||||
build-ossfuzz-fast8/fuzz/fuzz_$fuzzer -merge=1 out/cmin/$fuzzer out/$fuzzer
|
# get input from everyone else (corpus cross pollination)
|
||||||
|
others=$(find out -type d -not -name $fuzzer -not -name out -not -name cmin)
|
||||||
|
build-fast/fuzz/fuzz_$fuzzer -merge=1 $MAXLEN out/cmin/$fuzzer out/$fuzzer $others seedcorpus
|
||||||
rm -rf out/$fuzzer
|
rm -rf out/$fuzzer
|
||||||
mv out/cmin/$fuzzer out/$fuzzer
|
mv out/cmin/$fuzzer out/$fuzzer
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Package the corpus into an artifact
|
- name: Package the corpus into an artifact
|
||||||
run: |
|
run: |
|
||||||
for fuzzer in $allfuzzers; do
|
for fuzzer in $defaultimplfuzzers $implfuzzers; do
|
||||||
tar rf corpus.tar out/$fuzzer
|
tar rf corpus.tar out/$fuzzer
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Save the corpus as a github artifact
|
- name: Save the corpus as a github artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: corpus
|
name: corpus
|
||||||
path: corpus.tar
|
path: corpus.tar
|
||||||
- name: Run the corpus through valgrind (normal build)
|
|
||||||
|
- name: Store the corpus externally
|
||||||
run: |
|
run: |
|
||||||
for fuzzer in $allfuzzers; do
|
gzip --keep corpus.tar
|
||||||
find out/$fuzzer -type f |sort|xargs valgrind build-plain-noavx/fuzz/fuzz_$fuzzer 2>&1|tee valgrind-$fuzzer-noavx.txt
|
curl -F"filedata=@corpus.tar.gz" https://simdjson:${{ secrets.fuzzdatapassword }}@www.pauldreik.se/fuzzdata/index.php
|
||||||
done
|
|
||||||
- name: Run the corpus through valgrind (noavx build)
|
# This takes a subset of the minimized corpus and run it through valgrind. It is slow,
|
||||||
|
# therefore take a "random" subset. The random selection is accomplished by sorting on filenames,
|
||||||
|
# which are hashes of the content.
|
||||||
|
- name: Run some of the minimized corpus through valgrind (replay build, default implementation)
|
||||||
run: |
|
run: |
|
||||||
for fuzzer in $allfuzzers; do
|
for fuzzer in $defaultimplfuzzers $implfuzzers; do
|
||||||
find out/$fuzzer -type f |sort|xargs valgrind build-plain-normal/fuzz/fuzz_$fuzzer 2>&1|tee valgrind-$fuzzer-normal.txt
|
find out/$fuzzer -type f |sort|head -n200|xargs -n40 valgrind build-replay/fuzz/fuzz_$fuzzer 2>&1|tee valgrind-$fuzzer.txt
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Compress the valgrind output
|
- name: Compress the valgrind output
|
||||||
run: tar cf valgrind.tar valgrind-*.txt
|
run: tar cf valgrind.tar valgrind-*.txt
|
||||||
|
|
||||||
- name: Save valgrind output as a github artifact
|
- name: Save valgrind output as a github artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v2
|
||||||
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: valgrindresults
|
name: valgrindresults
|
||||||
path: valgrind.tar
|
path: valgrind.tar
|
||||||
- name: Upload the corpus and results to bintray if we are on master
|
if-no-files-found: ignore
|
||||||
run: |
|
|
||||||
if [ $(git rev-parse --verify HEAD) = $(git rev-parse --verify origin/master) ] ; then
|
- name: Archive any crashes as an artifact
|
||||||
echo uploading each artifact twice, otherwise it will not be published
|
uses: actions/upload-artifact@v2
|
||||||
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
|
if: always()
|
||||||
curl -T corpus.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/corpus.tar";publish=1;override=1"
|
with:
|
||||||
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
|
name: crashes
|
||||||
curl -T valgrind.tar -upauldreik:${{ secrets.bintrayApiKey }} https://api.bintray.com/content/pauldreik/simdjson-fuzz-corpus/corpus/0/corpus/valgrind.tar";publish=1;override=1"
|
path: |
|
||||||
else
|
crash-*
|
||||||
echo "not on master, won't upload to bintray"
|
leak-*
|
||||||
fi
|
timeout-*
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Macos (Xcode 11)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
macos-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
xcversion select 11.7
|
||||||
|
mkdir builddebug &&
|
||||||
|
cd builddebug &&
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cd .. &&
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cmake --install . &&
|
||||||
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||||
|
cd ../tests/installation_tests/find &&
|
||||||
|
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
name: Macos
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
macos-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir builddebug &&
|
||||||
|
cd builddebug &&
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cd .. &&
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cmake --install . &&
|
||||||
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||||
|
cd ../tests/installation_tests/find &&
|
||||||
|
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
name: MSYS2-CLANG-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
windows-mingw:
|
||||||
|
name: ${{ matrix.msystem }}
|
||||||
|
runs-on: windows-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- msystem: "MINGW64"
|
||||||
|
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
|
||||||
|
type: Release
|
||||||
|
- msystem: "MINGW64"
|
||||||
|
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
|
||||||
|
type: Debug
|
||||||
|
env:
|
||||||
|
CMAKE_GENERATOR: Ninja
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
update: true
|
||||||
|
msystem: ${{ matrix.msystem }}
|
||||||
|
install: ${{ matrix.install }}
|
||||||
|
- name: Build and Test
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
|
||||||
|
cmake --build . --verbose
|
||||||
|
ctest -j4 --output-on-failure -LE explicitonly
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
name: MSYS2-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
windows-mingw:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
name: ${{ matrix.msystem }}
|
||||||
|
runs-on: windows-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- msystem: "MINGW64"
|
||||||
|
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
|
||||||
|
type: Release
|
||||||
|
- msystem: "MINGW64"
|
||||||
|
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
|
||||||
|
type: Debug
|
||||||
|
env:
|
||||||
|
CMAKE_GENERATOR: Ninja
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
update: true
|
||||||
|
msystem: ${{ matrix.msystem }}
|
||||||
|
install: ${{ matrix.install }}
|
||||||
|
- name: Build and Test
|
||||||
|
run: |
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_DO_NOT_USE_THREADS_NO_MATTER_WHAT=ON ..
|
||||||
|
cmake --build . --verbose
|
||||||
|
ctest -j4 --output-on-failure -LE explicitonly
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
name: short fuzz on the power arch
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
armv7_job:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
# The host should always be Linux
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
name: Build on ubuntu-20.04 ppc64le
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.1.0
|
||||||
|
- uses: uraimo/run-on-arch-action@v2.0.5
|
||||||
|
name: Run commands
|
||||||
|
id: runcmd
|
||||||
|
env:
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
with:
|
||||||
|
arch: ppc64le
|
||||||
|
distro: buster
|
||||||
|
|
||||||
|
# Not required, but speeds up builds by storing container images in
|
||||||
|
# a GitHub package registry.
|
||||||
|
githubToken: ${{ github.token }}
|
||||||
|
|
||||||
|
run: |
|
||||||
|
export CLANGSUFFIX="-7"
|
||||||
|
apt-get -qq update
|
||||||
|
apt-get install -q -y clang-7 libfuzzer-7-dev git wget zip ninja-build gnupg software-properties-common
|
||||||
|
wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | apt-key add -
|
||||||
|
apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main"
|
||||||
|
apt-get -qq update
|
||||||
|
apt-get purge cmake cmake-data
|
||||||
|
apt-get -t simdjson -y install cmake
|
||||||
|
mkdir -p build ; cd build
|
||||||
|
cmake .. -GNinja \
|
||||||
|
-DCMAKE_CXX_COMPILER=clang++$CLANGSUFFIX \
|
||||||
|
-DCMAKE_C_COMPILER=clang$CLANGSUFFIX \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DSIMDJSON_DEVELOPER_MODE=ON \
|
||||||
|
-DSIMDJSON_ENABLE_FUZZING=On \
|
||||||
|
-DSIMDJSON_COMPETITION=OFF \
|
||||||
|
-DSIMDJSON_GOOGLE_BENCHMARKS=OFF \
|
||||||
|
-DSIMDJSON_DISABLE_DEPRECATED_API=On \
|
||||||
|
-DSIMDJSON_FUZZ_LDFLAGS=-lFuzzer \
|
||||||
|
-DCMAKE_CXX_FLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=" \
|
||||||
|
-DCMAKE_C_FLAGS="-fsanitize=fuzzer-no-link" \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DSIMDJSON_FUZZ_LINKMAIN=Off
|
||||||
|
cd ..
|
||||||
|
builddir=build
|
||||||
|
cmake --build $builddir
|
||||||
|
wget -O corpus.tar.gz https://readonly:readonly@www.pauldreik.se/fuzzdata/index.php?project=simdjson
|
||||||
|
tar xf corpus.tar.gz
|
||||||
|
fuzzernames=$(cmake --build $builddir --target print_all_fuzzernames |tail -n1)
|
||||||
|
for fuzzer in $fuzzernames ; do
|
||||||
|
exe=$builddir/fuzz/$fuzzer
|
||||||
|
shortname=$(echo $fuzzer |cut -f2- -d_)
|
||||||
|
echo found fuzzer $shortname with executable $exe
|
||||||
|
mkdir -p out/$shortname
|
||||||
|
others=$(find out -type d -not -name $shortname -not -name out -not -name cmin)
|
||||||
|
$exe -max_total_time=20 -max_len=4000 out/$shortname $others
|
||||||
|
echo "*************************************************************************"
|
||||||
|
done
|
||||||
|
echo "all is good, no errors found in any of these fuzzers: $fuzzernames"
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
name: Performance check on Ubuntu 18.04 CI (GCC 7)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_ENABLE_DOM_CHECKPERF=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . --target checkperf &&
|
||||||
|
ctest --output-on-failure -R checkperf ubuntu18-checkperf.yml
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
name: Ubuntu 18.04 CI (LLVM 7)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
|
||||||
|
env:
|
||||||
|
CC: clang-7
|
||||||
|
CXX: clang++-7
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: install clang 7
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install clang-7
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: Ubuntu 18.04 CI (GCC 7) with Thread Sanitizer
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
|
||||||
|
cmake --build . --target document_stream_tests --target ondemand_document_stream_tests --target parse_many_test &&
|
||||||
|
ctest --output-on-failure -R parse_many_test &&
|
||||||
|
ctest --output-on-failure -R document_stream_tests
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: Ubuntu 18.04 CI (GCC 7)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
make install &&
|
||||||
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
name: Performance check on Ubuntu 20.04 CI (GCC 9)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_ENABLE_DOM_CHECKPERF=ON -DCMAKE_CXX_FLAGS="-Werror=old-style-cast -pedantic -Wpedantic" -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . --target checkperf &&
|
||||||
|
ctest --output-on-failure -R checkperf
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
name: Ubuntu 20.04 CI (GCC 8)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
CXX: g++-8
|
||||||
|
CC: gcc-8
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Install GCC 8
|
||||||
|
run: sudo apt-get install -y g++-8
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir builddebug &&
|
||||||
|
cd builddebug &&
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cd .. &&
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cmake --install . &&
|
||||||
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||||
|
cd ../tests/installation_tests/find &&
|
||||||
|
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Ubuntu 20.04 CI (GCC 9) without exceptions
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir builddebug &&
|
||||||
|
cd builddebug &&
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_EXCEPTIONS=OFF -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cd .. &&
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_EXCEPTIONS=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
make install &&
|
||||||
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||||
|
mkdir testfindpackage &&
|
||||||
|
cd testfindpackage &&
|
||||||
|
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Ubuntu 20.04 CI (GCC 9) Without Threads
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir builddebug &&
|
||||||
|
cd builddebug &&
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_ENABLE_THREADS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cd .. &&
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_ENABLE_THREADS=OFF -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
make install &&
|
||||||
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||||
|
mkdir testfindpackage &&
|
||||||
|
cd testfindpackage &&
|
||||||
|
echo -e 'cmake_minimum_required(VERSION 3.1)\nproject(simdjsontester)\nset(CMAKE_CXX_STANDARD 17)\nfind_package(simdjson REQUIRED)'> CMakeLists.txt && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../destination .. && cmake --build .
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Ubuntu 20.04 CI (GCC 9) With Memory Sanitizer
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir builddebug &&
|
||||||
|
cd builddebug &&
|
||||||
|
cmake -DSIMDJSON_SANITIZE=ON -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
name: Ubuntu 20.04 CI (GCC 9) with Thread Sanitizer
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
|
||||||
|
cmake --build . --target document_stream_tests --target ondemand_document_stream_tests --target parse_many_test &&
|
||||||
|
ctest --output-on-failure -R parse_many_test &&
|
||||||
|
ctest --output-on-failure -R document_stream_tests
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
name: Ubuntu 20.04 CI (GCC 9)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir builddebug &&
|
||||||
|
cd builddebug &&
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cd .. &&
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cmake --install . &&
|
||||||
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||||
|
cd ../tests/installation_tests/find &&
|
||||||
|
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: Ubuntu 22.04 CI (CLANG 13)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Install clang++-13
|
||||||
|
run: sudo apt-get install -y clang++-13
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
CXX=clang++-13 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
name: Ubuntu 22.04 CI (GCC 12)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Install gcc12
|
||||||
|
run: sudo apt-get install -y g++-12
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
CXX=g++-12 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
name: Ubuntu 22.04 CI (GCC 11)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir builddebug &&
|
||||||
|
cd builddebug &&
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cd .. &&
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest -j --output-on-failure -LE explicitonly &&
|
||||||
|
cmake --install . &&
|
||||||
|
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
|
||||||
|
cd ../tests/installation_tests/find &&
|
||||||
|
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: VS17-ARM-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: windows-vs17
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {arch: ARM}
|
||||||
|
- {arch: ARM64}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
cmake -A ${{ matrix.arch }} -DCMAKE_CROSSCOMPILING=1 -DSIMDJSON_DEVELOPER_MODE=ON -D SIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_EXCEPTIONS=OFF -B build &&
|
||||||
|
cmake --build build --verbose
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
name: VS17-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
name: windows-vs17
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, shared: ON}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
|
||||||
|
- name: Build Debug
|
||||||
|
run: cmake --build build --config Debug --verbose
|
||||||
|
- name: Build Release
|
||||||
|
run: cmake --build build --config Release --verbose
|
||||||
|
- name: Run Release tests
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest -C Release -LE explicitonly --output-on-failure
|
||||||
|
- name: Run Debug tests
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest -C Debug -LE explicitonly --output-on-failure
|
||||||
|
- name: Install
|
||||||
|
run: |
|
||||||
|
cmake --install build --config Release
|
||||||
|
- name: Test Installation
|
||||||
|
run: |
|
||||||
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||||
|
cmake --build build_install_test --config Release
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: VS17-CLANG-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
if: >-
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
||||||
|
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
||||||
|
name: windows-vs17
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
|
||||||
|
- name: Build Debug
|
||||||
|
run: cmake --build build --config Debug --verbose
|
||||||
|
- name: Build Release
|
||||||
|
run: cmake --build build --config Release --verbose
|
||||||
|
- name: Run Release tests
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest -C Release -LE explicitonly --output-on-failure
|
||||||
|
- name: Run Debug tests
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
ctest -C Debug -LE explicitonly --output-on-failure
|
||||||
|
- name: Install
|
||||||
|
run: |
|
||||||
|
cmake --install build --config Release
|
||||||
|
- name: Test Installation
|
||||||
|
run: |
|
||||||
|
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
|
||||||
|
cmake --build build_install_test --config Release
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
name: VS17-NoExcept-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: windows-vs17
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: dependencies/.cache
|
||||||
|
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
|
||||||
|
- name: 'Run CMake with VS17'
|
||||||
|
uses: lukka/run-cmake@v3
|
||||||
|
with:
|
||||||
|
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
|
||||||
|
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
|
||||||
|
buildDirectory: "${{ github.workspace }}/../../_temp/windows"
|
||||||
|
cmakeBuildType: Release
|
||||||
|
buildWithCMake: true
|
||||||
|
cmakeGenerator: VS16Win64
|
||||||
|
cmakeAppendedArgs: -DSIMDJSON_COMPETITION=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_EXCEPTIONS=OFF
|
||||||
|
buildWithCMakeArgs: --config Release
|
||||||
|
|
||||||
|
- name: 'Run CTest'
|
||||||
|
run: ctest -C Release -LE explicitonly --output-on-failure
|
||||||
|
working-directory: "${{ github.workspace }}/../../_temp/windows"
|
||||||
|
|
||||||
+19
-102
@@ -1,8 +1,3 @@
|
|||||||
visual_studio
|
|
||||||
|
|
||||||
/benchbranch/
|
|
||||||
/submodules/
|
|
||||||
|
|
||||||
# eclipse project files
|
# eclipse project files
|
||||||
.cproject
|
.cproject
|
||||||
.project
|
.project
|
||||||
@@ -52,85 +47,6 @@ gens
|
|||||||
libs
|
libs
|
||||||
objs
|
objs
|
||||||
|
|
||||||
# Build outputs (TODO build to a subdir so we can exclude that instead)
|
|
||||||
/allparserscheckfile
|
|
||||||
/allparsingcompetition
|
|
||||||
/basictests
|
|
||||||
/benchfeatures
|
|
||||||
/benchmark/bench_dom_api
|
|
||||||
/benchmark/bench_parse_call
|
|
||||||
/benchmark/get_corpus_benchmark
|
|
||||||
/benchmark/parse
|
|
||||||
/benchmark/parse_stream
|
|
||||||
/benchmark/perfdiff
|
|
||||||
/benchmark/statisticalmodel
|
|
||||||
/build/
|
|
||||||
/build-ossfuzz-*/
|
|
||||||
/build-plain-*/
|
|
||||||
/corpus.zip
|
|
||||||
/distinctuseridcompetition
|
|
||||||
/errortests
|
|
||||||
/examples/quickstart/quickstart
|
|
||||||
/examples/quickstart/simdjson.cpp
|
|
||||||
/examples/quickstart/simdjson.h
|
|
||||||
/examples/quickstart/twitter.json
|
|
||||||
/fuzz/fuzz_dump
|
|
||||||
/fuzz/fuzz_dump_raw_tape
|
|
||||||
/fuzz/fuzz_parser
|
|
||||||
/fuzz/fuzz_print_json
|
|
||||||
/get_corpus_benchmark
|
|
||||||
/json2json
|
|
||||||
/jsoncheck
|
|
||||||
/jsoncheck_westmere
|
|
||||||
/jsoncheck_fallback
|
|
||||||
/jsonpointer
|
|
||||||
/jsonstats
|
|
||||||
/integer_tests
|
|
||||||
/libsimdjson.so*
|
|
||||||
/minifiercompetition
|
|
||||||
/minify
|
|
||||||
/numberparsingcheck
|
|
||||||
/ossfuzz-out
|
|
||||||
/out
|
|
||||||
/parse
|
|
||||||
/parse_many_test
|
|
||||||
/parse_nonumberparsing
|
|
||||||
/parse_nostringparsing
|
|
||||||
/parse_noutf8validation
|
|
||||||
/parse_stream
|
|
||||||
/parseandstatcompetition
|
|
||||||
/parsingcompetition
|
|
||||||
/perfdiff
|
|
||||||
/pointercheck
|
|
||||||
/readme_examples
|
|
||||||
/readme_examples_noexceptions
|
|
||||||
/statisticalmodel
|
|
||||||
/stringparsingcheck
|
|
||||||
/submodules
|
|
||||||
/ujdecode.o
|
|
||||||
/amalgamation_demo.cpp
|
|
||||||
/simdjson.cpp
|
|
||||||
/simdjson.h
|
|
||||||
/singleheader/amalgamation_demo
|
|
||||||
/singleheader/demo
|
|
||||||
/tests/basictests
|
|
||||||
/tests/errortests
|
|
||||||
/tests/jsoncheck
|
|
||||||
/tests/pointercheck
|
|
||||||
/tests/integer_tests
|
|
||||||
/tests/parse_many_test
|
|
||||||
/tests/readme_examples
|
|
||||||
/tests/readme_examples_noexceptions
|
|
||||||
/tools/json2json
|
|
||||||
/tools/jsonstats
|
|
||||||
/tools/minify
|
|
||||||
|
|
||||||
# Don't check in generated API docs
|
|
||||||
/doc/api
|
|
||||||
|
|
||||||
# Don't check in generated examples
|
|
||||||
/jsonexamples/generated
|
|
||||||
|
|
||||||
# C++ ignore from https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
|
# C++ ignore from https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
|
||||||
|
|
||||||
# Prerequisites
|
# Prerequisites
|
||||||
@@ -167,23 +83,24 @@ objs
|
|||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
|
||||||
### https://raw.github.com/github/gitignore/e92f8db7a027af8cc25da2dc0758317e39697684/CMake.gitignore
|
|
||||||
|
|
||||||
CMakeLists.txt.user
|
|
||||||
CMakeCache.txt
|
|
||||||
CMakeFiles
|
|
||||||
CMakeScripts
|
|
||||||
Testing
|
|
||||||
Makefile
|
|
||||||
cmake_install.cmake
|
|
||||||
install_manifest.txt
|
|
||||||
compile_commands.json
|
|
||||||
CTestTestfile.cmake
|
|
||||||
_deps
|
|
||||||
|
|
||||||
# CMake files that may be specific to our installation
|
# CMake files that may be specific to our installation
|
||||||
/CPackConfig.cmake
|
|
||||||
/CPackSourceConfig.cmake
|
|
||||||
# We check in a custom version of root Makefile that is not generated by CMake
|
|
||||||
!/Makefile
|
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
/build*/
|
||||||
|
/visual_studio/
|
||||||
|
|
||||||
|
# Fuzzer outputs generated by instructions in fuzz/Fuzzing.md
|
||||||
|
/corpus.zip
|
||||||
|
/ossfuzz-out/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# Generated docs
|
||||||
|
/doc/api
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# VSCode workspace files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|||||||
-33
@@ -1,33 +0,0 @@
|
|||||||
[submodule "scalarvssimd/rapidjson"]
|
|
||||||
path = dependencies/rapidjson
|
|
||||||
url = https://github.com/Tencent/rapidjson.git
|
|
||||||
[submodule "dependencies/sajson"]
|
|
||||||
path = dependencies/sajson
|
|
||||||
url = https://github.com/chadaustin/sajson.git
|
|
||||||
[submodule "dependencies/json11"]
|
|
||||||
path = dependencies/json11
|
|
||||||
url = https://github.com/dropbox/json11.git
|
|
||||||
[submodule "dependencies/fastjson"]
|
|
||||||
path = dependencies/fastjson
|
|
||||||
url = https://github.com/mikeando/fastjson.git
|
|
||||||
[submodule "dependencies/gason"]
|
|
||||||
path = dependencies/gason
|
|
||||||
url = https://github.com/vivkin/gason.git
|
|
||||||
[submodule "dependencies/ujson4c"]
|
|
||||||
path = dependencies/ujson4c
|
|
||||||
url = https://github.com/esnme/ujson4c.git
|
|
||||||
[submodule "dependencies/jsmn"]
|
|
||||||
path = dependencies/jsmn
|
|
||||||
url = https://github.com/zserge/jsmn.git
|
|
||||||
[submodule "dependencies/cJSON"]
|
|
||||||
path = dependencies/cJSON
|
|
||||||
url = https://github.com/DaveGamble/cJSON.git
|
|
||||||
[submodule "dependencies/jsoncpp"]
|
|
||||||
path = dependencies/jsoncpp
|
|
||||||
url = https://github.com/open-source-parsers/jsoncpp.git
|
|
||||||
[submodule "dependencies/json"]
|
|
||||||
path = dependencies/json
|
|
||||||
url = https://github.com/nlohmann/json.git
|
|
||||||
[submodule "dependencies/benchmark"]
|
|
||||||
path = dependencies/benchmark
|
|
||||||
url = https://github.com/google/benchmark.git
|
|
||||||
+187
-27
@@ -1,30 +1,190 @@
|
|||||||
language: cpp
|
language: cpp
|
||||||
sudo: false
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- gcc-7
|
|
||||||
- g++-7
|
|
||||||
- clang-format
|
|
||||||
- python
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
script:
|
dist: bionic
|
||||||
- export CXX=g++-7
|
|
||||||
- export CC=gcc-7
|
|
||||||
- make
|
|
||||||
- make test
|
|
||||||
- make everything
|
|
||||||
- make amalgamate
|
|
||||||
- make clean
|
|
||||||
- make SANITIZEGOLD=1 test
|
|
||||||
- make clean
|
|
||||||
- ARCHFLAGS="-march=nehalem" make
|
|
||||||
- ARCHFLAGS="-march=nehalem" make test
|
|
||||||
- ARCHFLAGS="-march=nehalem" make everything
|
|
||||||
- ./style/run-clang-format.py -r include/ benchmark/ src/ tests/
|
|
||||||
|
|
||||||
|
arch:
|
||||||
|
- ppc64le
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.dep_cache
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- simdjson_DEPENDENCY_CACHE_DIR=$HOME/.dep_cache
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-8
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=gcc-8 && CXX=g++-8"
|
||||||
|
compiler: gcc-8
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-9
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=gcc-9 && CXX=g++-9"
|
||||||
|
compiler: gcc-9
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-10
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=gcc-10 && CXX=g++-10"
|
||||||
|
compiler: gcc-10
|
||||||
|
|
||||||
|
# The sanitizer runs fail systematically
|
||||||
|
# - os: linux
|
||||||
|
# addons:
|
||||||
|
# apt:
|
||||||
|
# sources:
|
||||||
|
# - ubuntu-toolchain-r-test
|
||||||
|
# packages:
|
||||||
|
# - g++-10
|
||||||
|
# env:
|
||||||
|
# - COMPILER="CC=gcc-10 && CXX=g++-10"
|
||||||
|
# - SANITIZE="on"
|
||||||
|
# compiler: gcc-10-sanitize
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-10
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=gcc-10 && CXX=g++-10"
|
||||||
|
- STATIC="on"
|
||||||
|
compiler: gcc-10-static
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- llvm-toolchain-bionic-6.0
|
||||||
|
packages:
|
||||||
|
- clang-6.0
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=clang-6.0 && CXX=clang++-6.0"
|
||||||
|
compiler: clang-6
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- llvm-toolchain-bionic-7
|
||||||
|
packages:
|
||||||
|
- clang-7
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=clang-7 && CXX=clang++-7"
|
||||||
|
compiler: clang-7
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- llvm-toolchain-bionic-8
|
||||||
|
packages:
|
||||||
|
- clang-8
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=clang-8 && CXX=clang++-8"
|
||||||
|
compiler: clang-8
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- llvm-toolchain-bionic-9
|
||||||
|
packages:
|
||||||
|
- clang-9
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=clang-9 && CXX=clang++-9"
|
||||||
|
compiler: clang-9
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- clang-10
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
|
||||||
|
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=clang-10 && CXX=clang++-10"
|
||||||
|
compiler: clang-10
|
||||||
|
|
||||||
|
- os: linux
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- clang-10
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
- sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
|
||||||
|
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||||
|
env:
|
||||||
|
- COMPILER="CC=clang-10 && CXX=clang++-10"
|
||||||
|
- STATIC="on"
|
||||||
|
compiler: clang-10-static
|
||||||
|
|
||||||
|
# The clang sanitizer runs fail frequently at setup time
|
||||||
|
# - os: linux
|
||||||
|
# addons:
|
||||||
|
# apt:
|
||||||
|
# packages:
|
||||||
|
# - clang-10
|
||||||
|
# sources:
|
||||||
|
# - ubuntu-toolchain-r-test
|
||||||
|
# - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main'
|
||||||
|
# key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
|
||||||
|
# env:
|
||||||
|
# - COMPILER="CC=clang-10 && CXX=clang++-10"
|
||||||
|
# - SANITIZE="on"
|
||||||
|
# compiler: clang-10-sanitize
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- eval "${COMPILER}"
|
||||||
|
|
||||||
|
install:
|
||||||
|
- wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | sudo apt-key add -
|
||||||
|
- sudo apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main"
|
||||||
|
- sudo apt-get -qq update
|
||||||
|
- sudo apt-get purge cmake cmake-data
|
||||||
|
- sudo apt-get -t simdjson -y install cmake
|
||||||
|
- export CMAKE_CXX_FLAGS="-maltivec -mcpu=power9 -mtune=power9"
|
||||||
|
- export CMAKE_C_FLAGS="${CMAKE_CXX_FLAGS}"
|
||||||
|
- export CMAKE_FLAGS="-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -DSIMDJSON_IMPLEMENTATION=ppc64;fallback";
|
||||||
|
- if [[ "${SANITIZE}" == "on" ]]; then
|
||||||
|
export CMAKE_FLAGS="${CMAKE_FLAGS} -DSIMDJSON_SANITIZE=ON";
|
||||||
|
export ASAN_OPTIONS="detect_leaks=0";
|
||||||
|
fi
|
||||||
|
- if [[ "${STATIC}" == "on" ]]; then
|
||||||
|
export CMAKE_FLAGS="${CMAKE_FLAGS} -DBUILD_SHARED_LIBS=OFF";
|
||||||
|
fi
|
||||||
|
- export CTEST_FLAGS="-j4 --output-on-failure -LE explicitonly"
|
||||||
|
|
||||||
|
script:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake $CMAKE_FLAGS ..
|
||||||
|
- cmake --build . -- -j2
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=ppc64 ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation
|
||||||
|
- ctest $CTEST_FLAGS -LE "acceptance|per_implementation"
|
||||||
|
|||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||||
|
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||||
|
|
||||||
|
// List of extensions which should be recommended for users of this workspace.
|
||||||
|
"recommendations": [
|
||||||
|
// Syntax
|
||||||
|
"ms-vscode.cpptools",
|
||||||
|
"ms-vscode.cmake-tools",
|
||||||
|
"ms-python.python",
|
||||||
|
"twxs.cmake"
|
||||||
|
],
|
||||||
|
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+101
@@ -0,0 +1,101 @@
|
|||||||
|
{
|
||||||
|
"editor.rulers": [
|
||||||
|
{"column": 95 },
|
||||||
|
{"column": 120 }
|
||||||
|
],
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
"files.associations": {
|
||||||
|
"array": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"__locale": "cpp",
|
||||||
|
"__tuple": "cpp",
|
||||||
|
"__bit_reference": "cpp",
|
||||||
|
"__config": "cpp",
|
||||||
|
"__debug": "cpp",
|
||||||
|
"__errc": "cpp",
|
||||||
|
"__functional_base": "cpp",
|
||||||
|
"__hash_table": "cpp",
|
||||||
|
"__mutex_base": "cpp",
|
||||||
|
"__node_handle": "cpp",
|
||||||
|
"__nullptr": "cpp",
|
||||||
|
"__split_buffer": "cpp",
|
||||||
|
"__string": "cpp",
|
||||||
|
"__threading_support": "cpp",
|
||||||
|
"__tree": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"bit": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"codecvt": "cpp",
|
||||||
|
"complex": "cpp",
|
||||||
|
"condition_variable": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"forward_list": "cpp",
|
||||||
|
"fstream": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"ios": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"list": "cpp",
|
||||||
|
"locale": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"mutex": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"regex": "cpp",
|
||||||
|
"set": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stack": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"thread": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"typeinfo": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"unordered_set": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"valarray": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"*.ipp": "cpp",
|
||||||
|
"__functional_base_03": "cpp",
|
||||||
|
"filesystem": "cpp",
|
||||||
|
"*.inc": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"variant": "cpp",
|
||||||
|
"__bits": "cpp",
|
||||||
|
"csignal": "cpp",
|
||||||
|
"future": "cpp",
|
||||||
|
"queue": "cpp",
|
||||||
|
"shared_mutex": "cpp",
|
||||||
|
"ranges": "cpp"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
# List of authors for copyright purposes
|
# List of authors for copyright purposes, in no particular order
|
||||||
Daniel Lemire
|
Daniel Lemire
|
||||||
Geoff Langdale
|
Geoff Langdale
|
||||||
|
John Keiser
|
||||||
|
|||||||
+266
-87
@@ -1,103 +1,282 @@
|
|||||||
cmake_minimum_required(VERSION 3.9) # CMP0069 NEW
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
project(
|
||||||
message(STATUS "No build type selected, default to Release")
|
simdjson
|
||||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
# The version number is modified by tools/release.py
|
||||||
endif()
|
VERSION 2.2.2
|
||||||
|
DESCRIPTION "Parsing gigabytes of JSON per second"
|
||||||
project(simdjson
|
HOMEPAGE_URL "https://simdjson.org/"
|
||||||
DESCRIPTION "Parsing gigabytes of JSON per second"
|
LANGUAGES CXX C
|
||||||
LANGUAGES CXX
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# LTO seems to create all sorts of fun problems. Let us
|
set(SIMDJSON_GITHUB_REPOSITORY "https://github.com/simdjson/simdjson")
|
||||||
# disable temporarily.
|
|
||||||
#include(CheckIPOSupported)
|
|
||||||
#check_ipo_supported(RESULT ltoresult)
|
|
||||||
#if(ltoresult)
|
|
||||||
# set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
|
||||||
#endif()
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
string(
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
COMPARE EQUAL
|
||||||
set(CMAKE_MACOSX_RPATH OFF)
|
"${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
set(CMAKE_THREAD_PREFER_PTHREAD ON)
|
is_top_project
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
)
|
||||||
|
|
||||||
set(SIMDJSON_LIB_NAME simdjson)
|
# ---- Options, variables ----
|
||||||
set(PROJECT_VERSION_MAJOR 0)
|
|
||||||
set(PROJECT_VERSION_MINOR 3)
|
|
||||||
set(PROJECT_VERSION_PATCH 1)
|
|
||||||
set(SIMDJSON_LIB_VERSION "0.3.1" CACHE STRING "simdjson library version")
|
|
||||||
set(SIMDJSON_LIB_SOVERSION "1" CACHE STRING "simdjson library soversion")
|
|
||||||
|
|
||||||
option(SIMDJSON_IMPLEMENTATION_HASWELL "Include the haswell implementation" ON)
|
# These version numbers are modified by tools/release.py
|
||||||
option(SIMDJSON_IMPLEMENTATION_WESTMERE "Include the westmere implementation" ON)
|
set(SIMDJSON_LIB_VERSION "13.0.0" CACHE STRING "simdjson library version")
|
||||||
option(SIMDJSON_IMPLEMENTATION_ARM64 "Include the arm64 implementation" ON)
|
set(SIMDJSON_LIB_SOVERSION "13" CACHE STRING "simdjson library soversion")
|
||||||
option(SIMDJSON_IMPLEMENTATION_FALLBACK "Include the fallback implementation" ON)
|
|
||||||
if(NOT MSVC)
|
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
|
||||||
option(SIMDJSON_BUILD_STATIC "Build a static library" OFF) # turning it on disables the production of a dynamic library
|
|
||||||
else()
|
include(cmake/simdjson-props.cmake)
|
||||||
option(SIMDJSON_BUILD_STATIC "Build a static library" ON) # turning it on disables the production of a dynamic library
|
include(cmake/implementation-flags.cmake)
|
||||||
|
include(cmake/exception-flags.cmake)
|
||||||
|
|
||||||
|
option(SIMDJSON_DISABLE_DEPRECATED_API "Disables deprecated APIs" OFF)
|
||||||
|
if(SIMDJSON_DISABLE_DEPRECATED_API)
|
||||||
|
simdjson_add_props(
|
||||||
|
target_compile_definitions PUBLIC
|
||||||
|
SIMDJSON_DISABLE_DEPRECATED_API=1
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
option(SIMDJSON_SANITIZE "Sanitize addresses" OFF)
|
|
||||||
option(SIMDJSON_GOOGLE_BENCHMARKS "compile the Google Benchmark benchmarks" OFF)
|
|
||||||
option(SIMDJSON_ENABLE_THREADS "enable threaded operation" ON)
|
|
||||||
option(SIMDJSON_EXCEPTIONS "Enable simdjson's exception-throwing interface" ON)
|
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake")
|
option(SIMDJSON_DEVELOPMENT_CHECKS "Enable development-time aids, such as \
|
||||||
|
checks for incorrect API usage. Enabled by default in DEBUG." OFF)
|
||||||
|
if(SIMDJSON_DEVELOPMENT_CHECKS)
|
||||||
|
simdjson_add_props(
|
||||||
|
target_compile_definitions PUBLIC
|
||||||
|
SIMDJSON_DEVELOPMENT_CHECKS
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(CTargets)
|
if(is_top_project)
|
||||||
find_package(Options)
|
option(SIMDJSON_DEVELOPER_MODE "Enable targets for developing simdjson" OFF)
|
||||||
|
option(BUILD_SHARED_LIBS "Build simdjson as a shared library" OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
# We used to have install(DIRECTORY include/${SIMDJSON_LIB_NAME} DESTINATION include)
|
include(cmake/handle-deprecations.cmake)
|
||||||
# alone.
|
include(cmake/developer-options.cmake)
|
||||||
# However, this fails because we also need the root level simdjson.h file.
|
|
||||||
install(DIRECTORY include/${SIMDJSON_LIB_NAME} DESTINATION include)
|
# ---- simdjson library ----
|
||||||
install(FILES include/${SIMDJSON_LIB_NAME}.h DESTINATION include)
|
|
||||||
set (TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/jsonchecker/")
|
add_library(simdjson src/simdjson.cpp)
|
||||||
set (BENCHMARK_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/jsonexamples/")
|
add_library(simdjson::simdjson ALIAS simdjson)
|
||||||
add_definitions(-DSIMDJSON_TEST_DATA_DIR="${TEST_DATA_DIR}")
|
|
||||||
add_definitions(-DSIMDJSON_BENCHMARK_DATA_DIR="${TEST_DATA_DIR}")
|
set_target_properties(
|
||||||
|
simdjson PROPERTIES
|
||||||
|
VERSION "${SIMDJSON_LIB_VERSION}"
|
||||||
|
SOVERSION "${SIMDJSON_LIB_SOVERSION}"
|
||||||
|
# FIXME: symbols should be hidden by default
|
||||||
|
WINDOWS_EXPORT_ALL_SYMBOLS YES
|
||||||
|
)
|
||||||
|
|
||||||
|
# FIXME: Use proper CMake integration for exports
|
||||||
|
if(MSVC AND BUILD_SHARED_LIBS)
|
||||||
|
target_compile_definitions(
|
||||||
|
simdjson
|
||||||
|
PRIVATE SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY=1
|
||||||
|
INTERFACE SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY=1
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
simdjson_add_props(
|
||||||
|
target_include_directories
|
||||||
|
PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
|
||||||
|
PRIVATE "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>"
|
||||||
|
)
|
||||||
|
|
||||||
|
simdjson_add_props(target_compile_features PUBLIC cxx_std_11)
|
||||||
|
|
||||||
|
# workaround for GNU GCC poor AVX load/store code generation
|
||||||
|
if(
|
||||||
|
CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
|
||||||
|
AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(i.86|x86(_64)?)$"
|
||||||
|
)
|
||||||
|
simdjson_add_props(
|
||||||
|
target_compile_options PRIVATE
|
||||||
|
-mno-avx256-split-unaligned-load -mno-avx256-split-unaligned-store
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SIMDJSON_ENABLE_THREADS)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
simdjson_add_props(target_link_libraries PUBLIC Threads::Threads)
|
||||||
|
simdjson_add_props(target_compile_definitions PUBLIC SIMDJSON_THREADS_ENABLED=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
simdjson_apply_props(simdjson)
|
||||||
|
|
||||||
|
# ---- Install rules ----
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES singleheader/simdjson.h
|
||||||
|
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
|
COMPONENT simdjson_Development
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS simdjson
|
||||||
|
EXPORT simdjsonTargets
|
||||||
|
RUNTIME COMPONENT simdjson_Runtime
|
||||||
|
LIBRARY COMPONENT simdjson_Runtime
|
||||||
|
NAMELINK_COMPONENT simdjson_Development
|
||||||
|
ARCHIVE COMPONENT simdjson_Development
|
||||||
|
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(cmake/simdjson-config.cmake.in simdjson-config.cmake @ONLY)
|
||||||
|
|
||||||
|
write_basic_package_version_file(
|
||||||
|
simdjson-config-version.cmake
|
||||||
|
COMPATIBILITY SameMinorVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
SIMDJSON_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/simdjson"
|
||||||
|
CACHE STRING "CMake package config location relative to the install prefix"
|
||||||
|
)
|
||||||
|
mark_as_advanced(SIMDJSON_INSTALL_CMAKEDIR)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
"${PROJECT_BINARY_DIR}/simdjson-config.cmake"
|
||||||
|
"${PROJECT_BINARY_DIR}/simdjson-config-version.cmake"
|
||||||
|
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT simdjson_Development
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
EXPORT simdjsonTargets
|
||||||
|
NAMESPACE simdjson::
|
||||||
|
DESTINATION "${SIMDJSON_INSTALL_CMAKEDIR}"
|
||||||
|
COMPONENT example_Development
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# CPack
|
||||||
|
#
|
||||||
|
if(is_top_project)
|
||||||
|
set(CPACK_PACKAGE_VENDOR "Daniel Lemire")
|
||||||
|
set(CPACK_PACKAGE_CONTACT "lemire@gmail.com")
|
||||||
|
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
|
||||||
|
set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README.md")
|
||||||
|
|
||||||
|
set(CPACK_RPM_PACKAGE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
|
||||||
|
|
||||||
|
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ---- Developer mode extras ----
|
||||||
|
|
||||||
|
if(NOT SIMDJSON_DEVELOPER_MODE)
|
||||||
|
message(STATUS "Building only the library. Advanced users may want to turn SIMDJSON_DEVELOPER_MODE to ON, e.g., via -D SIMDJSON_DEVELOPER_MODE=ON.")
|
||||||
|
return()
|
||||||
|
elseif(NOT is_top_project)
|
||||||
|
message(AUTHOR_WARNING "Developer mode is intended for developers of simdjson")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
simdjson_apply_props(simdjson-internal-flags)
|
||||||
|
|
||||||
|
set(
|
||||||
|
SIMDJSON_USER_CMAKECACHE
|
||||||
|
"${CMAKE_BINARY_DIR}/.simdjson-user-CMakeCache.txt"
|
||||||
|
)
|
||||||
|
add_custom_target(
|
||||||
|
simdjson-user-cmakecache
|
||||||
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
|
-D "BINARY_DIR=${CMAKE_BINARY_DIR}"
|
||||||
|
-D "USER_CMAKECACHE=${SIMDJSON_USER_CMAKECACHE}"
|
||||||
|
-P "${PROJECT_SOURCE_DIR}/cmake/simdjson-user-cmakecache.cmake"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setup tests
|
||||||
enable_testing()
|
enable_testing()
|
||||||
include(include/CMakeLists.txt)
|
# So we can build just tests with "make all_tests"
|
||||||
add_subdirectory(src)
|
add_custom_target(all_tests)
|
||||||
add_subdirectory(tools)
|
|
||||||
|
add_subdirectory(windows)
|
||||||
|
add_subdirectory(dependencies) ## This needs to be before tools because of cxxopts
|
||||||
|
add_subdirectory(tools) ## This needs to be before tests because of cxxopts
|
||||||
|
|
||||||
|
# Data: jsonexamples is left with only the bare essential.
|
||||||
|
# most of the data has been moved to https://github.com/simdjson/simdjson-data
|
||||||
|
add_subdirectory(jsonexamples)
|
||||||
|
|
||||||
|
|
||||||
|
add_subdirectory(singleheader)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Compile tools / tests / benchmarks
|
||||||
|
#
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
add_subdirectory(examples)
|
||||||
add_subdirectory(benchmark)
|
add_subdirectory(benchmark)
|
||||||
|
add_subdirectory(fuzz)
|
||||||
|
|
||||||
if (SIMDJSON_GOOGLE_BENCHMARKS)
|
#
|
||||||
if(NOT EXISTS dependencies/benchmark/CMakeLists.txt)
|
# Source files should be just ASCII
|
||||||
# message(STATUS "Unable to find dependencies/benchmark/CMakeLists.txt")
|
#
|
||||||
execute_process(COMMAND git submodule update --init -- dependencies/benchmark
|
find_program(FIND find)
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
find_program(FILE file)
|
||||||
|
find_program(GREP grep)
|
||||||
|
if(FIND AND FILE AND GREP)
|
||||||
|
add_test(
|
||||||
|
NAME just_ascii
|
||||||
|
COMMAND sh -c "\
|
||||||
|
${FIND} include src windows tools singleheader tests examples benchmark \
|
||||||
|
-path benchmark/checkperf-reference -prune -name '*.h' -o -name '*.cpp' \
|
||||||
|
-type f -exec ${FILE} '{}' \; | ${GREP} -qv ASCII || exit 0 && exit 1"
|
||||||
|
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
##
|
||||||
|
## In systems like R, libraries must not use stderr or abort to be acceptable.
|
||||||
|
## Thus we make it a hard rule that one is not allowed to call abort or stderr.
|
||||||
|
## The sanitized builds are allowed to abort.
|
||||||
|
##
|
||||||
|
if(NOT SIMDJSON_SANITIZE)
|
||||||
|
find_program(GREP grep)
|
||||||
|
find_program(NM nm)
|
||||||
|
if((NOT GREP) OR (NOT NM))
|
||||||
|
message("grep and nm are unavailable on this system.")
|
||||||
|
else()
|
||||||
|
add_test(
|
||||||
|
NAME "avoid_abort"
|
||||||
|
# Under FreeBSD, the __cxa_guard_abort symbol may appear but it is fine.
|
||||||
|
# So we want to look for <space><possibly _>abort as a test.
|
||||||
|
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} ' _*abort' || exit 0 && exit 1"
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
add_test(
|
||||||
|
NAME "avoid_cout"
|
||||||
|
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} ' _*cout' || exit 0 && exit 1"
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
add_test(
|
||||||
|
NAME "avoid_cerr"
|
||||||
|
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} ' _*cerr' || exit 0 && exit 1"
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
add_test(
|
||||||
|
NAME "avoid_printf"
|
||||||
|
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} ' _*printf' || exit 0 && exit 1"
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
add_test(
|
||||||
|
NAME "avoid_stdout"
|
||||||
|
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} stdout || exit 0 && exit 1"
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
add_test(
|
||||||
|
NAME "avoid_stderr"
|
||||||
|
COMMAND sh -c "${NM} $<TARGET_FILE_NAME:simdjson> | ${GREP} stderr || exit 0 && exit 1"
|
||||||
|
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
option(BENCHMARK_ENABLE_TESTING OFF)
|
|
||||||
set(BENCHMARK_ENABLE_TESTING OFF)
|
|
||||||
option(BENCHMARK_ENABLE_INSTALL OFF)
|
|
||||||
set(BENCHMARK_ENABLE_INSTALL OFF)
|
|
||||||
add_subdirectory(dependencies/benchmark)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# for fuzzing, read the comments in the fuzz/CMakeLists.txt file
|
|
||||||
option(ENABLE_FUZZING "enable building the fuzzers" ON)
|
|
||||||
if(ENABLE_FUZZING)
|
|
||||||
add_subdirectory(fuzz)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CPACK_PACKAGE_VENDOR "Daniel Lemire")
|
|
||||||
set(CPACK_PACKAGE_CONTACT "lemire@gmail.com")
|
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Parsing gigabytes of JSON per second")
|
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
|
||||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
|
||||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
|
||||||
|
|
||||||
set(CPACK_RPM_PACKAGE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
|
||||||
|
|
||||||
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
|
|
||||||
|
|
||||||
include(CPack)
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+50
-4
@@ -5,7 +5,7 @@ The simdjson library is an open project written in C++. Contributions are invite
|
|||||||
agree to the project's license.
|
agree to the project's license.
|
||||||
|
|
||||||
We have an extensive list of issues, and contributions toward any of these issues is invited.
|
We have an extensive list of issues, and contributions toward any of these issues is invited.
|
||||||
Contributions can take the form of code samples, better documentation or design ideas.
|
Contributions can take the form of code samples, better documentation or design ideas.
|
||||||
|
|
||||||
In particular, the following contributions are invited:
|
In particular, the following contributions are invited:
|
||||||
|
|
||||||
@@ -32,7 +32,21 @@ We discourage the following types of contributions:
|
|||||||
|
|
||||||
In short, most code changes should either bring new features or better performance. We want to avoid unmotivated code changes.
|
In short, most code changes should either bring new features or better performance. We want to avoid unmotivated code changes.
|
||||||
|
|
||||||
Guidelines
|
|
||||||
|
Specific rules
|
||||||
|
----------
|
||||||
|
|
||||||
|
We have few hard rules, but we have some:
|
||||||
|
|
||||||
|
- Printing to standard output or standard error (`stderr`, `stdout`, `std::cerr`, `std::cout`) in the core library is forbidden. This follows from the [Writing R Extensions](https://cran.r-project.org/doc/manuals/R-exts.html) manual which states that "Compiled code should not write to stdout or stderr".
|
||||||
|
- Calls to `abort()` are forbidden in the core library. This follows from the [Writing R Extensions](https://cran.r-project.org/doc/manuals/R-exts.html) manual which states that "Under no circumstances should your compiled code ever call abort or exit".
|
||||||
|
- All source code files (.h, .cpp) must be ASCII.
|
||||||
|
- All C macros introduced in public headers need to be prefixed with either `SIMDJSON_` or `simdjson_`.
|
||||||
|
- We avoid trailing white space characters within lines. That is, your lines of code should not terminate with unnecessary spaces. Generally, please avoid making unnecessary changes to white-space characters when contributing code.
|
||||||
|
|
||||||
|
Tools, tests and benchmarks are not held to these same strict rules.
|
||||||
|
|
||||||
|
General Guidelines
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Contributors are encouraged to :
|
Contributors are encouraged to :
|
||||||
@@ -40,14 +54,46 @@ Contributors are encouraged to :
|
|||||||
- Document their changes. Though we do not enforce a rule regarding code comments, we prefer that non-trivial algorithms and techniques be somewhat documented in the code.
|
- Document their changes. Though we do not enforce a rule regarding code comments, we prefer that non-trivial algorithms and techniques be somewhat documented in the code.
|
||||||
- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency.
|
- Follow as much as possible the existing code style. We do not enforce a specific code style, but we prefer consistency.
|
||||||
- Modify as few lines of code as possible when working on an issue. The more lines you modify, the harder it is for your fellow human beings to understand what is going on.
|
- Modify as few lines of code as possible when working on an issue. The more lines you modify, the harder it is for your fellow human beings to understand what is going on.
|
||||||
- Tools may report "problems" with the code, but we never delegate programming to tools: if there is a problem with the code, we need to understand it. Thus we will not "fix" code merely to please a static analyzer if we do not understand.
|
- Tools may report "problems" with the code, but we never delegate programming to tools: if there is a problem with the code, we need to understand it. Thus we will not "fix" code merely to please a static analyzer.
|
||||||
- Provide tests for any new feature. We will not merge a new feature without tests.
|
- Provide tests for any new feature. We will not merge a new feature without tests.
|
||||||
|
|
||||||
|
Pull Requests
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Pull requests are always invited. However, we ask that you follow these guidelines:
|
||||||
|
|
||||||
|
- It is wise to discuss your ideas first as part of an issue before you start coding. If you omit this step and code first, be prepared to have your code receive scrutiny and be dropped.
|
||||||
|
- Users should provide a rationale for their changes. Does it improve performance? Does it add a feature? Does it improve maintainability? Does it fix a bug? This must be explicitly stated as part of the pull request. Do not propose changes based on taste or intuition. We do not delegate programming to tools: that some tool suggested a code change is not reason enough to change the code.
|
||||||
|
1. When your code improves performance, please document the gains with a benchmark using hard numbers.
|
||||||
|
2. If your code fixes a bug, please either fix a failing test, or propose a new test.
|
||||||
|
3. Other types of changes must be clearly motivated. We openly discourage changes with no identifiable benefits.
|
||||||
|
- Changes should be focused and minimal. You should change as few lines of code as possible. Please do not reformat or touch files needlessly.
|
||||||
|
- New features must be accompanied by new tests, in general.
|
||||||
|
- Your code should pass our continuous-integration tests. It is your responsibility to ensure that your proposal pass the tests. We do not merge pull requests that would break our build.
|
||||||
|
- An exception to this would be changes to non-code files, such as documentation and assets, or trivial changes to code, such as comments, where it is encouraged to explicitly ask for skipping a CI run using the `[skip ci]` prefix in your Pull Request title **and** in the first line of the most recent commit in a push. Example for such a commit: `[skip ci] Fixed typo in power_of_ten's docs`
|
||||||
|
This benefits the project in such a way that the CI pipeline is not burdened by running jobs on changes that don't change any behavior in the code, which reduces wait times for other Pull Requests that do change behavior and require testing.
|
||||||
|
|
||||||
|
If the benefits of your proposed code remain unclear, we may choose to discard your code: that is not an insult, we frequently discard our own code. We may also consider various alternatives and choose another path. Again, that is not an insult or a sign that you have wasted your time.
|
||||||
|
|
||||||
|
Style
|
||||||
|
-----
|
||||||
|
|
||||||
|
Our formatting style is inspired by the LLVM style.
|
||||||
|
The simdjson library is written using the snake case: when a variable or a function is a phrase, each space is replaced by an underscore character, and the first letter of each word written in lowercase. Compile-time constants are written entirely in uppercase with the same underscore convention.
|
||||||
|
|
||||||
Code of Conduct
|
Code of Conduct
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Though we do not have a formal code of conduct, we will not tolerate bullying, bigotry or
|
Though we do not have a formal code of conduct, we will not tolerate bullying, bigotry or
|
||||||
intimidation. Everyone is welcome to contribute.
|
intimidation. Everyone is welcome to contribute. If you have concerns, you can raise them privately with the core team members (e.g., D. Lemire, J. Keiser).
|
||||||
|
|
||||||
|
We welcome contributions from women and less represented groups. If you need help, please reach out.
|
||||||
|
|
||||||
|
Consider the following points when engaging with the project:
|
||||||
|
|
||||||
|
- We discourage arguments from authority: ideas are discusssed on their own merits and not based on who stated it.
|
||||||
|
- Be mindful that what you may view as an aggression is maybe merely a difference of opinion or a misunderstanding.
|
||||||
|
- Be mindful that a collection of small aggressions, even if mild in isolation, can become harmful.
|
||||||
|
|
||||||
Getting Started Hacking
|
Getting Started Hacking
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|||||||
+11
-1
@@ -31,5 +31,15 @@ Paul Dreik
|
|||||||
Jeremie Piotte
|
Jeremie Piotte
|
||||||
Matthew Wilson
|
Matthew Wilson
|
||||||
Dušan Jovanović
|
Dušan Jovanović
|
||||||
# if you have contributed to the project and your name does not
|
Matjaž Ostroveršnik
|
||||||
|
Nong Li
|
||||||
|
Furkan Taşkale
|
||||||
|
Brendan Knapp
|
||||||
|
Danila Kutenin
|
||||||
|
Pavel Pavlov
|
||||||
|
Hao Chen
|
||||||
|
Nicolas Boyer
|
||||||
|
Kim Walisch and Jatin Bhateja (AVX-512 bitset decoder)
|
||||||
|
Fangzheng Zhang and Weiqiang Wan (AVX-512 kernel)
|
||||||
|
# if you have contributed to the project and your name does not
|
||||||
# appear in this list, please let us know!
|
# appear in this list, please let us know!
|
||||||
|
|||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
# docker build -t simdjson . && docker run --privileged -t simdjson
|
|
||||||
FROM gcc:8.3
|
|
||||||
COPY . /usr/src/
|
|
||||||
WORKDIR /usr/src/
|
|
||||||
RUN make clean
|
|
||||||
RUN make amalgamate
|
|
||||||
RUN make
|
|
||||||
RUN make test
|
|
||||||
RUN make parsingcompetition
|
|
||||||
CMD ["bash", "scripts/selectparser.sh"]
|
|
||||||
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER =
|
PROJECT_NUMBER = "2.2.2"
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
@@ -51,7 +51,7 @@ PROJECT_BRIEF = "Ridiculously Fast JSON"
|
|||||||
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
|
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
|
||||||
# the logo to the output directory.
|
# the logo to the output directory.
|
||||||
|
|
||||||
PROJECT_LOGO =
|
PROJECT_LOGO = images/logotiny.png
|
||||||
|
|
||||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||||
# into which the generated documentation will be written. If a relative path is
|
# into which the generated documentation will be written. If a relative path is
|
||||||
@@ -829,7 +829,7 @@ WARN_LOGFILE =
|
|||||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = include
|
INPUT = doc include
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||||
@@ -1026,7 +1026,7 @@ FILTER_SOURCE_PATTERNS =
|
|||||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||||
# and want to reuse the introduction page also for the doxygen output.
|
# and want to reuse the introduction page also for the doxygen output.
|
||||||
|
|
||||||
USE_MDFILE_AS_MAINPAGE =
|
USE_MDFILE_AS_MAINPAGE = doc/basics_doxygen.md
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to source browsing
|
# Configuration options related to source browsing
|
||||||
|
|||||||
+189
-285
@@ -1,33 +1,75 @@
|
|||||||
Hacking simdjson
|
Hacking simdjson
|
||||||
================
|
================
|
||||||
|
|
||||||
Here is wisdom about how to build, test and run simdjson from within the repository. *Users* of
|
Here is wisdom about how to build, test and run simdjson from within the repository. This is mostly useful for people who plan to contribute simdjson, or maybe study the design.
|
||||||
simdjson should use the released simdjson.h and simdjson.cpp files.
|
|
||||||
|
If you plan to contribute to simdjson, please read our [CONTRIBUTING](https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md) guide.
|
||||||
|
|
||||||
|
|
||||||
|
Design notes
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
The parser works in two stages:
|
||||||
|
|
||||||
|
- Stage 1. (Find marks) Identifies quickly structure elements, strings, and so forth. We validate UTF-8 encoding at that stage.
|
||||||
|
- Stage 2. (Structure building) Involves constructing a "tree" of sort (materialized as a tape) to navigate through the data. Strings and numbers are parsed at this stage.
|
||||||
|
|
||||||
|
|
||||||
|
The role of stage 1 is to identify pseudo-structural characters as quickly as possible. A character is pseudo-structural if and only if:
|
||||||
|
|
||||||
|
1. Not enclosed in quotes, AND
|
||||||
|
2. Is a non-whitespace character, AND
|
||||||
|
3. Its preceding character is either:
|
||||||
|
(a) a structural character, OR
|
||||||
|
(b) whitespace OR
|
||||||
|
(c) the final quote in a string.
|
||||||
|
|
||||||
|
This helps as we redefine some new characters as pseudo-structural such as the characters 1, G, n in the following:
|
||||||
|
|
||||||
|
> { "foo" : 1.5, "bar" : 1.5 GEOFF_IS_A_DUMMY bla bla , "baz", null }
|
||||||
|
|
||||||
|
Stage 1 also does unicode validation.
|
||||||
|
|
||||||
|
Stage 2 handles all of the rest: number parsings, recognizing atoms like true, false, null, and so forth.
|
||||||
|
|
||||||
|
Developer mode
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Build system targets that are only useful for developers of the simdjson
|
||||||
|
library are behind the `SIMDJSON_DEVELOPER_MODE` option. Enabling this option
|
||||||
|
makes tests, examples, benchmarks and other developer targets available. Not
|
||||||
|
enabling this option means that you are a consumer of simdjson and thus you
|
||||||
|
only get the library targets and options.
|
||||||
|
|
||||||
|
Developer mode is forced to be on when the `CI` environment variable is set to
|
||||||
|
a value that CMake recognizes as "on", which is set to `true` in all of the CI
|
||||||
|
workflows used by simdjson.
|
||||||
|
|
||||||
Directory Structure and Source
|
Directory Structure and Source
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
simdjson's source structure, from the top level, looks like this:
|
simdjson's source structure, from the top level, looks like this:
|
||||||
|
|
||||||
* **Makefile:** The main Makefile for Linux. This is not the same as CMakeLists.txt.
|
* **CMakeLists.txt:** The main build system.
|
||||||
* **CMakeLists.txt:** A Makefile generator for non-default cases and options.
|
|
||||||
* **include:** User-facing declarations and inline definitions (most user-facing functions are inlined).
|
* **include:** User-facing declarations and inline definitions (most user-facing functions are inlined).
|
||||||
* simdjson.h: A "master include" that includes files from include/simdjson/. This is equivalent to
|
* simdjson.h: A "main include" that includes files from include/simdjson/. This is equivalent to
|
||||||
the distributed simdjson.h.
|
the distributed simdjson.h.
|
||||||
* simdjson/*.h: Declarations for public simdjson classes and functions.
|
* simdjson/*.h: Declarations for public simdjson classes and functions.
|
||||||
* simdjson/inline/*.h: Definitions for public simdjson classes and functions.
|
* simdjson/*-inl.h: Definitions for public simdjson classes and functions.
|
||||||
* **src:** The source files for non-inlined functionality (e.g. the architecture-specific parser
|
* **src:** The source files for non-inlined functionality (e.g. the architecture-specific parser
|
||||||
implementations).
|
implementations).
|
||||||
* simdjson.cpp: A "master source" that includes all implementation files from src/. This is
|
* simdjson.cpp: A "main source" that includes all implementation files from src/. This is
|
||||||
equivalent to the distributed simdjson.cpp.
|
equivalent to the distributed simdjson.cpp.
|
||||||
* arm64/|fallback/|haswell/|westmere/: Architecture-specific implementations. All functions are
|
* arm64/|fallback/|haswell/|ppc64/|westmere/: Architecture-specific implementations. All functions are
|
||||||
Each architecture defines its own namespace, e.g. simdjson::haswell.
|
Each architecture defines its own namespace, e.g. simdjson::haswell.
|
||||||
* generic/: Generic implementations of the simdjson parser. These files may be included and
|
* generic/: Generic implementations of the simdjson parser. These files may be included and
|
||||||
compiled multiple times, from whichever architectures use them. They assume they are already
|
compiled multiple times, from whichever architectures use them. They assume they are already
|
||||||
enclosed in a namespace, e.g.:
|
enclosed in a namespace, e.g.:
|
||||||
```c++
|
```c++
|
||||||
namespace simdjson::haswell {
|
namespace simdjson {
|
||||||
#include "generic/stage1_find_marks.h"
|
namespace haswell {
|
||||||
|
#include "generic/stage1/json_structural_indexer.h"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -35,329 +77,219 @@ Other important files and directories:
|
|||||||
* **.drone.yml:** Definitions for Drone CI.
|
* **.drone.yml:** Definitions for Drone CI.
|
||||||
* **.appveyor.yml:** Definitions for Appveyor CI (Windows).
|
* **.appveyor.yml:** Definitions for Appveyor CI (Windows).
|
||||||
* **.circleci:** Definitions for Circle CI.
|
* **.circleci:** Definitions for Circle CI.
|
||||||
* **amalgamation.sh:** Generates singleheader/simdjson.h and singleheader/simdjson.cpp for release.
|
* **.github/workflows:** Definitions for GitHub Actions (CI).
|
||||||
|
* **singleheader:** Contains generated `simdjson.h` and `simdjson.cpp` that we release. The files `singleheader/simdjson.h` and `singleheader/simdjson.cpp` should never be edited by hand.
|
||||||
|
* **singleheader/amalgamate.py:** Generates `singleheader/simdjson.h` and `singleheader/simdjson.cpp` for release (python script).
|
||||||
* **benchmark:** This is where we do benchmarking. Benchmarking is core to every change we make; the
|
* **benchmark:** This is where we do benchmarking. Benchmarking is core to every change we make; the
|
||||||
cardinal rule is don't regress performance without knowing exactly why, and what you're trading
|
cardinal rule is don't regress performance without knowing exactly why, and what you're trading
|
||||||
for it. If you're not sure what else to do to check your performance, this is always a good start:
|
for it. Many of our benchmarks are microbenchmarks. We are effectively doing controlled scientific experiments for the purpose of understanding what affects our performance. So we simplify as much as possible. We try to avoid irrelevant factors such as page faults, interrupts, unnnecessary system calls. We recommend checking the performance as follows:
|
||||||
```bash
|
```bash
|
||||||
make parse && ./parse jsonexamples/twitter.json
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
|
||||||
|
cmake --build . --config Release
|
||||||
|
benchmark/dom/parse ../jsonexamples/twitter.json
|
||||||
```
|
```
|
||||||
|
The last line becomes `./benchmark/Release/parse.exe ../jsonexample/twitter.json` under Windows. You may also use Google Benchmark:
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
|
||||||
|
cmake --build . --target bench_parse_call --config Release
|
||||||
|
./benchmark/bench_parse_call
|
||||||
|
```
|
||||||
|
The last line becomes `./benchmark/Release/bench_parse_call.exe` under Windows. Under Windows, you can also build with the clang compiler by adding `-T ClangCL` to the call to `cmake ..`: `cmake -T ClangCL ..`.
|
||||||
|
* **fuzz:** The source for fuzz testing. This lets us explore important edge and middle cases
|
||||||
* **fuzz:** The source for fuzz testing. This lets us explore important edge and middle cases
|
* **fuzz:** The source for fuzz testing. This lets us explore important edge and middle cases
|
||||||
automatically, and is run in CI.
|
automatically, and is run in CI.
|
||||||
* **jsonchecker:** A set of JSON files used to check different functionality of the parser.
|
* **jsonchecker:** A set of JSON files used to check different functionality of the parser.
|
||||||
* **pass*.json:** Files that should pass validation.
|
* **pass*.json:** Files that should pass validation.
|
||||||
* **fail*.json:** Files that should fail validation.
|
* **fail*.json:** Files that should fail validation.
|
||||||
|
* **jsonchecker/minefield/y_*.json:** Files that should pass validation.
|
||||||
|
* **jsonchecker/minefield/n_*.json:** Files that should fail validation.
|
||||||
* **jsonexamples:** A wide spread of useful, real-world JSON files with different characteristics
|
* **jsonexamples:** A wide spread of useful, real-world JSON files with different characteristics
|
||||||
and sizes.
|
and sizes.
|
||||||
* **singleheader:** Contains generated simdjson.h and simdjson.cpp that we release.
|
|
||||||
* **test:** The tests are here. basictests.cpp and errortests.cpp are the primary ones.
|
* **test:** The tests are here. basictests.cpp and errortests.cpp are the primary ones.
|
||||||
* **tools:** Source for executables that can be distributed with simdjson
|
* **tools:** Source for executables that can be distributed with simdjson. Some examples:
|
||||||
|
* `json2json mydoc.json` parses the document, constructs a model and then dumps back the result to standard output.
|
||||||
|
* `json2json -d mydoc.json` parses the document, constructs a model and then dumps model (as a tape) to standard output. The tape format is described in the accompanying file `tape.md`.
|
||||||
|
* `minify mydoc.json` minifies the JSON document, outputting the result to standard output. Minifying means to remove the unneeded white space characters.
|
||||||
|
*`jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
|
||||||
|
|
||||||
|
|
||||||
> **Don't modify the files in singleheader/ directly; these are automatically generated.**
|
> **Don't modify the files in singleheader/ directly; these are automatically generated.**
|
||||||
>
|
|
||||||
> While we distribute those files on release, we *maintain* the files under include/ and src/.
|
|
||||||
|
|
||||||
While simdjson distributes just two files from the singleheader/ directory, we *maintain* the code in
|
While simdjson distributes just two files from the singleheader/ directory, we *maintain* the code in
|
||||||
multiple files under include/ and src/. include/simdjson.h and src/simdjson.cpp are the "spine" for
|
multiple files under include/ and src/. The files include/simdjson.h and src/simdjson.cpp are the "spine" for
|
||||||
these, and you can include
|
these, and you can include them as if they were the corresponding singleheader/ files.
|
||||||
|
|
||||||
Regenerating Single Headers From Master
|
|
||||||
|
|
||||||
|
Runtime Dispatching
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
A key feature of simdjson is the ability to compile different processing kernels, optimized for specific instruction sets, and to select
|
||||||
|
the most appropriate kernel at runtime. This ensures that users get the very best performance while still enabling simdjson to run everywhere.
|
||||||
|
This technique is frequently called runtime dispatching. The simdjson achieves runtime dispatching entirely in C++: we do not assume
|
||||||
|
that the user is building the code using CMake, for example.
|
||||||
|
|
||||||
|
To make runtime dispatching work, it is critical that the code be compiled for the lowest supported processor. In particular, you should
|
||||||
|
not use flags such as -mavx2, /arch:AVX2 and so forth while compiling simdjson. When you do so, you allow the compiler to use advanced
|
||||||
|
instructions. In turn, these advanced instructions present in the code may cause a runtime failure if the runtime processor does not
|
||||||
|
support them. Even a simple loop, compiled with these flags, might generate binary code that only run on advanced processors.
|
||||||
|
|
||||||
|
So we compile simdjson for a generic processor. Our users should do the same if they want simdjson's runtime dispatch to work. It is important
|
||||||
|
to understand that if runtime dispatching does not work, then simdjson will cause crashes on older processors. Of course, if a user chooses
|
||||||
|
to compile their code for a specific instruction set (e.g., AVX2), they are responsible for the failures if they later run their code
|
||||||
|
on a processor that does not support AVX2. Yet, if we were to entice these users to do so, we would share the blame: thus we carefully instruct
|
||||||
|
users to compile their code in a generic way without doing anything to enable advanced instructions.
|
||||||
|
|
||||||
|
|
||||||
|
We only use runtime dispatching on x64 (AMD/Intel) platforms, at the moment. On ARM processors, we would need a standard way to query, at runtime,
|
||||||
|
the processor for its supported features. We do not know how to do so on ARM systems in general. Thankfully it is not yet a concern: 64-bit ARM
|
||||||
|
processors are fairly uniform as far as the instruction sets they support.
|
||||||
|
|
||||||
|
|
||||||
|
In all cases, simdjson uses advanced instructions by relying on "intrinsic functions": we do not write assembly code. The intrinsic functions
|
||||||
|
are special functions that the compiler might recognize and translate into fast code. To make runtime dispatching work, we rely on the fact that
|
||||||
|
the header providing these instructions
|
||||||
|
(intrin.h under Visual Studio, x86intrin.h elsewhere) defines all of the intrinsic functions, including those that are not supported
|
||||||
|
processor.
|
||||||
|
|
||||||
|
At this point, we are require to use one of two main strategies.
|
||||||
|
|
||||||
|
1. On POSIX systems, the main compilers (LLVM clang, GNU gcc) allow us to use any intrinsic function after including the header, but they fail to inline the resulting instruction if the target processor does not support them. Because we compile for a generic processor, we would not be able to use most intrinsic functions. Thankfully, more recent versions of these compilers allow us to flag a region of code with a specific target, so that we can compile only some of the code with support for advanced instructions. Thus in our C++, one might notice macros like `TARGET_HASWELL`. It is then our responsibility, at runtime, to only run the regions of code (that we call kernels) matching the properties of the runtime processor. The benefit of this approach is that the compiler not only let us use intrinsic functions, but it can also optimize the rest of the code in the kernel with advanced instructions we enabled.
|
||||||
|
|
||||||
|
2. Under Visual Studio, the problem is somewhat simpler. Visual Studio will not only provide the intrinsic functions, but it will also allow us to use them. They will compile just fine. It is at runtime that they may cause a crash. So we do not need to mark regions of code for compilation toward advanced processors (e.g., with `TARGET_HASWELL` macros). The downside of the Visual Studio approach is that the compiler is not allowed to use advanced instructions others than those we specify. In principle, this means that Visual Studio has weaker optimization opportunities.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
We also handle the special case where a user is compiling using LLVM clang under Windows, [using the Visual Studio toolchain](https://devblogs.microsoft.com/cppblog/clang-llvm-support-in-visual-studio/). If you compile with LLVM clang under Visual Studio, then the header files (intrin.h or x86intrin.h) no longer provides the intrinsic functions that are unsupported by the processor. This appears to be deliberate on the part of the LLVM engineers. With a few lines of code, we handle this scenario just like LLVM clang under a POSIX system, but forcing the inclusion of the specific headers, and rolling our own intrinsic function as needed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Regenerating Single-Header Files
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
simdjson.h and simdjson.cpp are not always up to date in master. To ensure you have the latest copy,
|
The simdjson.h and simdjson.cpp files in the singleheader directory are not always up-to-date with the rest of the code; they are only ever
|
||||||
you can regenerate them by running this at the top level:
|
systematically regenerated on releases. To ensure you have the latest code, you can regenerate them by running this at the top level:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make amalgamate
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
|
||||||
|
cmake --build . # needed, because currently dependencies do not work fully for the amalgamate target
|
||||||
|
cmake --build . --target amalgamate
|
||||||
```
|
```
|
||||||
|
|
||||||
The amalgamator is at `amalgamation.sh` at the top level. It generates singleheader/simdjson.h by
|
You need to have python3 installed on your system.
|
||||||
|
|
||||||
|
The amalgamator script `amalgamate.py` generates singleheader/simdjson.h by
|
||||||
reading through include/simdjson.h, copy/pasting each header file into the amalgamated file at the
|
reading through include/simdjson.h, copy/pasting each header file into the amalgamated file at the
|
||||||
point it gets included (but only once per header). singleheader/simdjson.cpp is generated from
|
point it gets included (but only once per header). singleheader/simdjson.cpp is generated from
|
||||||
src/simdjson.cpp the same way, except files under generic/ may be included and copy/pasted multiple
|
src/simdjson.cpp the same way, except files under generic/ may be included and copy/pasted multiple
|
||||||
times.
|
times.
|
||||||
|
|
||||||
### Usage (old-school Makefile on platforms like Linux or macOS)
|
### Usage (CMake on 64-bit platforms like Linux, FreeBSD or macOS)
|
||||||
|
|
||||||
Requirements: recent clang or gcc, and make. We recommend at least GNU GCC/G++ 7 or LLVM clang 6. A 64-bit system like Linux or macOS is expected.
|
Requirements: In addition to git, we require a recent version of CMake as well as bash.
|
||||||
|
|
||||||
To test:
|
|
||||||
|
|
||||||
```
|
|
||||||
make
|
|
||||||
make test
|
|
||||||
```
|
|
||||||
|
|
||||||
To run benchmarks:
|
|
||||||
|
|
||||||
```
|
|
||||||
make parse
|
|
||||||
./parse jsonexamples/twitter.json
|
|
||||||
```
|
|
||||||
|
|
||||||
Under Linux, the `parse` command gives a detailed analysis of the performance counters.
|
|
||||||
|
|
||||||
To run comparative benchmarks (with other parsers):
|
|
||||||
|
|
||||||
```
|
|
||||||
make benchmark
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usage (CMake on 64-bit platforms like Linux or macOS)
|
|
||||||
|
|
||||||
Requirements: We require a recent version of cmake. On macOS, the easiest way to install cmake might be to use [brew](https://brew.sh) and then type
|
|
||||||
|
|
||||||
|
1. On macOS, the easiest way to install cmake might be to use [brew](https://brew.sh) and then type
|
||||||
```
|
```
|
||||||
brew install cmake
|
brew install cmake
|
||||||
```
|
```
|
||||||
|
2. Under Linux, you might be able to install CMake as follows:
|
||||||
There is an [equivalent brew on Linux which works the same way as well](https://linuxbrew.sh).
|
|
||||||
|
|
||||||
You need a recent compiler like clang or gcc. We recommend at least GNU GCC/G++ 7 or LLVM clang 6. For example, you can install a recent compiler with brew:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
brew install gcc@8
|
apt-get update -qq
|
||||||
|
apt-get install -y cmake
|
||||||
|
```
|
||||||
|
3. On FreeBSD, you might be able to install bash and CMake as follows:
|
||||||
|
```
|
||||||
|
pkg update -f
|
||||||
|
pkg install bash
|
||||||
|
pkg install cmake
|
||||||
```
|
```
|
||||||
|
|
||||||
Optional: You need to tell cmake which compiler you wish to use by setting the CC and CXX variables. Under bash, you can do so with commands such as `export CC=gcc-7` and `export CXX=g++-7`.
|
You need a recent compiler like clang or gcc. We recommend at least GNU GCC/G++ 7 or LLVM clang 6.
|
||||||
|
|
||||||
|
|
||||||
Building: While in the project repository, do the following:
|
Building: While in the project repository, do the following:
|
||||||
|
|
||||||
```
|
```
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake ..
|
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
|
||||||
make
|
cmake --build .
|
||||||
make test
|
ctest
|
||||||
```
|
```
|
||||||
|
|
||||||
CMake will build a library. By default, it builds a shared library (e.g., libsimdjson.so on Linux).
|
CMake will build a library. By default, it builds a static library (e.g., libsimdjson.a on Linux).
|
||||||
|
|
||||||
You can build a static library:
|
You can build a shared library:
|
||||||
|
|
||||||
```
|
```
|
||||||
mkdir buildstatic
|
mkdir buildshared
|
||||||
cd buildstatic
|
cd buildshared
|
||||||
cmake -DSIMDJSON_BUILD_STATIC=ON ..
|
cmake -D BUILD_SHARED_LIBS=ON -D SIMDJSON_DEVELOPER_MODE=ON ..
|
||||||
make
|
cmake --build .
|
||||||
make test
|
ctest
|
||||||
```
|
```
|
||||||
|
|
||||||
In some cases, you may want to specify your compiler, especially if the default compiler on your system is too old. You may proceed as follows:
|
In some cases, you may want to specify your compiler, especially if the default compiler on your system is too old. You need to tell cmake which compiler you wish to use by setting the CC and CXX variables. Under bash, you can do so with commands such as `export CC=gcc-7` and `export CXX=g++-7`. You can also do it as part of the `cmake` command: `cmake -DCMAKE_CXX_COMPILER=g++ ..`. You may proceed as follows:
|
||||||
|
|
||||||
```
|
```
|
||||||
brew install gcc@8
|
brew install gcc@8
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
export CXX=g++-8 CC=gcc-8
|
export CXX=g++-8 CC=gcc-8
|
||||||
cmake ..
|
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
|
||||||
make
|
cmake --build .
|
||||||
make test
|
ctest
|
||||||
```
|
```
|
||||||
|
|
||||||
### Usage (CMake on 64-bit Windows using Visual Studio)
|
If your compiler does not default on C++11 support or better you may get failing tests. If so, you may be able to exclude the failing tests by replacing `ctest` with `ctest -E "^quickstart$"`.
|
||||||
|
|
||||||
We assume you have a common 64-bit Windows PC with at least Visual Studio 2017 and an x64 processor with AVX2 support (2013 Intel Haswell or later) or SSE 4.2 + CLMUL (2010 Westmere or later).
|
Note that the name of directory (`build`) is arbitrary, you can name it as you want (e.g., `buildgcc`) and you can have as many different such directories as you would like (one per configuration).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Usage (CMake on 64-bit Windows using Visual Studio 2019)
|
||||||
|
|
||||||
|
We assume you have a common 64-bit Windows PC with at least Visual Studio 2019.
|
||||||
|
|
||||||
- Grab the simdjson code from GitHub, e.g., by cloning it using [GitHub Desktop](https://desktop.github.com/).
|
- Grab the simdjson code from GitHub, e.g., by cloning it using [GitHub Desktop](https://desktop.github.com/).
|
||||||
- Install [CMake](https://cmake.org/download/). When you install it, make sure to ask that `cmake` be made available from the command line. Please choose a recent version of cmake.
|
- Install [CMake](https://cmake.org/download/). When you install it, make sure to ask that `cmake` be made available from the command line. Please choose a recent version of cmake.
|
||||||
- Create a subdirectory within simdjson, such as `VisualStudio`.
|
- Create a subdirectory within simdjson, such as `build`.
|
||||||
- Using a shell, go to this newly created directory.
|
- Using a shell, go to this newly created directory. You can start a shell directly from GitHub Desktop (Repository > Open in Command Prompt).
|
||||||
- Type `cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..` in the shell while in the `VisualStudio` repository. (Alternatively, if you want to build a DLL, you may use the command line `cmake -DCMAKE_GENERATOR_PLATFORM=x64 -DSIMDJSON_BUILD_STATIC=OFF ..`.)
|
- Type `cmake ..` in the shell while in the `build` repository.
|
||||||
- This last command (`cmake ...`) created a Visual Studio solution file in the newly created directory (e.g., `simdjson.sln`). Open this file in Visual Studio. You should now be able to build the project and run the tests. For example, in the `Solution Explorer` window (available from the `View` menu), right-click `ALL_BUILD` and select `Build`. To test the code, still in the `Solution Explorer` window, select `RUN_TESTS` and select `Build`.
|
- This last command (`cmake ...`) created a Visual Studio solution file in the newly created directory (e.g., `simdjson.sln`). Open this file in Visual Studio. You should now be able to build the project and run the tests. For example, in the `Solution Explorer` window (available from the `View` menu), right-click `ALL_BUILD` and select `Build`. To test the code, still in the `Solution Explorer` window, select `RUN_TESTS` and select `Build`.
|
||||||
|
|
||||||
### Usage (Using `vcpkg` on 64-bit Windows, Linux and macOS)
|
|
||||||
|
|
||||||
[vcpkg](https://github.com/Microsoft/vcpkg) users on Windows, Linux and macOS can download and install `simdjson` with one single command from their favorite shell.
|
Though having Visual Studio installed is necessary, one can build simdjson using only cmake commands:
|
||||||
|
|
||||||
On 64-bit Linux and macOS:
|
- `mkdir build`
|
||||||
|
- `cd build`
|
||||||
```
|
- `cmake ..`
|
||||||
$ ./vcpkg install simdjson
|
- `cmake --build . -config Release`
|
||||||
```
|
|
||||||
|
|
||||||
will build and install `simdjson` as a static library.
|
|
||||||
|
|
||||||
On Windows (64-bit):
|
|
||||||
|
|
||||||
```
|
|
||||||
.\vcpkg.exe install simdjson:x64-windows
|
|
||||||
```
|
|
||||||
|
|
||||||
will build and install `simdjson` as a shared library.
|
|
||||||
|
|
||||||
```
|
|
||||||
.\vcpkg.exe install simdjson:x64-windows-static
|
|
||||||
```
|
|
||||||
|
|
||||||
will build and install `simdjson` as a static library.
|
|
||||||
|
|
||||||
These commands will also print out instructions on how to use the library from MSBuild or CMake-based projects.
|
|
||||||
|
|
||||||
If you find the version of `simdjson` shipped with `vcpkg` is out-of-date, feel free to report it to
|
|
||||||
`vcpkg` community either by submitting an issue or by creating a PR.
|
|
||||||
|
|
||||||
### Usage (Docker)
|
|
||||||
|
|
||||||
One can run tests and benchmarks using docker. It especially makes sense under Linux. Privileged
|
|
||||||
access may be needed to get performance counters.
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone https://github.com/simdjson/simdjson.git
|
|
||||||
cd simdjson
|
|
||||||
docker build -t simdjson .
|
|
||||||
docker run --privileged -t simdjson
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture and Design Notes
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
- 64-bit platforms like Linux or macOS, as well as Windows through Visual Studio 2017 or later.
|
|
||||||
- Any 64-bit processor:
|
|
||||||
- AVX2 (i.e., Intel processors starting with the Haswell microarchitecture released 2013 and AMD
|
|
||||||
processors starting with the Zen microarchitecture released 2017),
|
|
||||||
- SSE 4.2 and CLMUL (i.e., Intel processors going back to Westmere released in 2010 or AMD
|
|
||||||
processors starting with the Jaguar used in the PS4 and XBox One),
|
|
||||||
- 64-bit ARM processor (ARMv8-A NEON): this covers a wide range of mobile processors, including
|
|
||||||
all Apple processors currently available for sale, going as far back as the iPhone 5s (2013).
|
|
||||||
- Any 64-bit processor (simdjson has a fallback generic 64-bit implementation that is still super
|
|
||||||
fast).
|
|
||||||
- A recent C++ compiler (e.g., GNU GCC or LLVM CLANG or Visual Studio 2017), we assume C++17. GNU
|
|
||||||
GCC 7 or better or LLVM's clang 6 or better.
|
|
||||||
- Some benchmark scripts assume bash and other common utilities, but they are optional.
|
|
||||||
|
|
||||||
### Scope
|
|
||||||
|
|
||||||
We provide a fast parser, that fully validates an input according to various specifications.
|
|
||||||
The parser builds a useful immutable (read-only) DOM (document-object model) which can be later accessed.
|
|
||||||
|
|
||||||
To simplify the engineering, we make some assumptions.
|
|
||||||
|
|
||||||
- We support UTF-8 (and thus ASCII), nothing else (no Latin, no UTF-16). We do not believe this is a
|
|
||||||
genuine limitation, because we do not think there is any serious application that needs to process
|
|
||||||
JSON data without an ASCII or UTF-8 encoding. If the UTF-8 contains a leading BOM, it should be
|
|
||||||
omitted: the user is responsible for detecting and skipping the BOM; UTF-8 BOMs are discouraged.
|
|
||||||
- All strings in the JSON document may have up to 4294967295 bytes in UTF-8 (4GB). To enforce this
|
|
||||||
constraint, we refuse to parse a document that contains more than 4294967295 bytes (4GB). This
|
|
||||||
should accommodate most JSON documents.
|
|
||||||
- As allowed by the specification, we allow repeated keys within an object (other parsers like
|
|
||||||
sajson do the same).
|
|
||||||
- [The simdjson library is fast for JSON documents spanning a few bytes up to many megabytes](https://github.com/lemire/simdjson/issues/312).
|
|
||||||
|
|
||||||
_We do not aim to provide a general-purpose JSON library._ A library like RapidJSON offers much more
|
|
||||||
than just parsing, it helps you generate JSON and offers various other convenient functions. We
|
|
||||||
merely parse the document. This may change in the future.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- The input string is unmodified. (Parsers like sajson and RapidJSON use the input string as a buffer.)
|
|
||||||
- We parse integers and floating-point numbers as separate types which allows us to support large signed 64-bit integers in [-9223372036854775808,9223372036854775808), like a Java `long` or a C/C++ `long long` and large unsigned integers up to the value 18446744073709551615. Among the parsers that differentiate between integers and floating-point numbers, not all support 64-bit integers. (For example, sajson rejects JSON files with integers larger than or equal to 2147483648. RapidJSON will parse a file containing an overly long integer like 18446744073709551616 as a floating-point number.) When we cannot represent exactly an integer as a signed or unsigned 64-bit value, we reject the JSON document.
|
|
||||||
- We support the full range of 64-bit floating-point numbers (binary64). The values range from ` std::numeric_limits<double>::lowest()` to `std::numeric_limits<double>::max()`, so from -1.7976e308 all the way to 1.7975e308. Extreme values (less or equal to -1e308, greater or equal to 1e308) are rejected: we refuse to parse the input document.
|
|
||||||
- We test for accurate float parsing with a perfect accuracy (ULP 0). Many parsers offer only approximate floating parsing. For example, RapidJSON also offers the option of accurate float parsing (`kParseFullPrecisionFlag`) but it comes at a significant performance penalty compared to the default settings. By default, RapidJSON tolerates an error of 3 ULP.
|
|
||||||
- We do full UTF-8 validation as part of the parsing. (Parsers like fastjson, gason and dropbox json11 do not do UTF-8 validation. The sajson parser does incomplete UTF-8 validation, accepting code point
|
|
||||||
sequences like 0xb1 0x87.)
|
|
||||||
- We fully validate the numbers. (Parsers like gason and ultranjson will accept `[0e+]` as valid JSON.)
|
|
||||||
- We validate string content for unescaped characters. (Parsers like fastjson and ultrajson accept unescaped line breaks and tabs in strings.)
|
|
||||||
- We fully validate the white-space characters outside of the strings. Parsers like RapidJSON will accept JSON documents with null characters outside of strings.
|
|
||||||
|
|
||||||
### Architecture
|
|
||||||
|
|
||||||
The parser works in two stages:
|
|
||||||
|
|
||||||
- Stage 1. (Find marks) Identifies quickly structure elements, strings, and so forth. We validate UTF-8 encoding at that stage.
|
|
||||||
- Stage 2. (Structure building) Involves constructing a "tree" of sort (materialized as a tape) to navigate through the data. Strings and numbers are parsed at this stage.
|
|
||||||
|
|
||||||
### Remarks on JSON parsing
|
|
||||||
|
|
||||||
- The JSON spec defines what a JSON parser is:
|
|
||||||
> A JSON parser transforms a JSON text into another representation. A JSON parser MUST accept all texts that conform to the JSON grammar. A JSON parser MAY accept non-JSON forms or extensions. An implementation may set limits on the size of texts that it accepts. An implementation may set limits on the maximum depth of nesting. An implementation may set limits on the range and precision of numbers. An implementation may set limits on the length and character contents of strings.
|
|
||||||
|
|
||||||
* JSON is not JavaScript:
|
|
||||||
|
|
||||||
> All JSON is Javascript but NOT all Javascript is JSON. So {property:1} is invalid because property does not have double quotes around it. {'property':1} is also invalid, because it's single quoted while the only thing that can placate the JSON specification is double quoting. JSON is even fussy enough that {"property":.1} is invalid too, because you should have of course written {"property":0.1}. Also, don't even think about having comments or semicolons, you guessed it: they're invalid. (credit:https://github.com/elzr/vim-json)
|
|
||||||
|
|
||||||
* The structural characters are:
|
|
||||||
|
|
||||||
begin-array = [ left square bracket
|
|
||||||
begin-object = { left curly bracket
|
|
||||||
end-array = ] right square bracket
|
|
||||||
end-object = } right curly bracket
|
|
||||||
name-separator = : colon
|
|
||||||
value-separator = , comma
|
|
||||||
|
|
||||||
### Pseudo-structural elements
|
|
||||||
|
|
||||||
A character is pseudo-structural if and only if:
|
|
||||||
|
|
||||||
1. Not enclosed in quotes, AND
|
|
||||||
2. Is a non-whitespace character, AND
|
|
||||||
3. Its preceding character is either:
|
|
||||||
(a) a structural character, OR
|
|
||||||
(b) whitespace.
|
|
||||||
|
|
||||||
This helps as we redefine some new characters as pseudo-structural such as the characters 1, G, n in the following:
|
|
||||||
|
|
||||||
> { "foo" : 1.5, "bar" : 1.5 GEOFF_IS_A_DUMMY bla bla , "baz", null }
|
|
||||||
|
|
||||||
## About the Project
|
|
||||||
|
|
||||||
### Bindings and Ports of simdjson
|
|
||||||
|
|
||||||
We distinguish between "bindings" (which just wrap the C++ code) and a port to another programming language (which reimplements everything).
|
|
||||||
|
|
||||||
|
|
||||||
- [ZippyJSON](https://github.com/michaeleisel/zippyjson): Swift bindings for the simdjson project.
|
Furthermore, if you have installed LLVM clang on Windows, for example as a component of Visual Studio 2019, you can configure and build simdjson using LLVM clang on Windows using cmake:
|
||||||
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
|
|
||||||
- [simdjson-rs](https://github.com/Licenser/simdjson-rs): Rust port.
|
|
||||||
- [simdjson-rust](https://github.com/SunDoge/simdjson-rust): Rust wrapper (bindings).
|
|
||||||
- [SimdJsonSharp](https://github.com/EgorBo/SimdJsonSharp): C# version for .NET Core (bindings and full port).
|
|
||||||
- [simdjson_nodejs](https://github.com/luizperes/simdjson_nodejs): Node.js bindings for the simdjson project.
|
|
||||||
- [simdjson_php](https://github.com/crazyxman/simdjson_php): PHP bindings for the simdjson project.
|
|
||||||
- [simdjson_ruby](https://github.com/saka1/simdjson_ruby): Ruby bindings for the simdjson project.
|
|
||||||
- [simdjson-go](https://github.com/minio/simdjson-go): Go port using Golang assembly.
|
|
||||||
- [rcppsimdjson](https://github.com/eddelbuettel/rcppsimdjson): R bindings.
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
|
|
||||||
- `json2json mydoc.json` parses the document, constructs a model and then dumps back the result to standard output.
|
- `mkdir build`
|
||||||
- `json2json -d mydoc.json` parses the document, constructs a model and then dumps model (as a tape) to standard output. The tape format is described in the accompanying file `tape.md`.
|
- `cd build`
|
||||||
- `minify mydoc.json` minifies the JSON document, outputting the result to standard output. Minifying means to remove the unneeded white space characters.
|
- `cmake -T ClangCL ..`
|
||||||
- `jsonpointer mydoc.json <jsonpath> <jsonpath> ... <jsonpath>` parses the document, constructs a model and then processes a series of [JSON Pointer paths](https://tools.ietf.org/html/rfc6901). The result is itself a JSON document.
|
- `cmake --build . -config Release`
|
||||||
|
|
||||||
### In-depth comparisons
|
|
||||||
|
|
||||||
If you want to see how a wide range of parsers validate a given JSON file:
|
|
||||||
|
|
||||||
```
|
|
||||||
make allparserscheckfile
|
|
||||||
./allparserscheckfile myfile.json
|
|
||||||
```
|
|
||||||
|
|
||||||
For performance comparisons:
|
|
||||||
|
|
||||||
```
|
|
||||||
make parsingcompetition
|
|
||||||
./parsingcompetition myfile.json
|
|
||||||
```
|
|
||||||
|
|
||||||
For broader comparisons:
|
|
||||||
|
|
||||||
```
|
|
||||||
make allparsingcompetition
|
|
||||||
./allparsingcompetition myfile.json
|
|
||||||
```
|
|
||||||
|
|
||||||
Both the `parsingcompetition` and `allparsingcompetition` tools take a `-t` flag which produces
|
|
||||||
a table-oriented output that can be conveniently parsed by other tools.
|
|
||||||
|
|
||||||
### Various References
|
### Various References
|
||||||
|
|
||||||
- [Google double-conv](https://github.com/google/double-conversion/)
|
|
||||||
- [How to implement atoi using SIMD?](https://stackoverflow.com/questions/35127060/how-to-implement-atoi-using-simd)
|
- [How to implement atoi using SIMD?](https://stackoverflow.com/questions/35127060/how-to-implement-atoi-using-simd)
|
||||||
- [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.php)
|
- [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.php)
|
||||||
- https://tools.ietf.org/html/rfc7159
|
- https://tools.ietf.org/html/rfc7159
|
||||||
- The Mison implementation in rust https://github.com/pikkr/pikkr
|
|
||||||
- http://rapidjson.org/md_doc_sax.html
|
- http://rapidjson.org/md_doc_sax.html
|
||||||
- https://github.com/Geal/parser_benchmarks/tree/master/json
|
- https://github.com/Geal/parser_benchmarks/tree/master/json
|
||||||
- Gron: A command line tool that makes JSON greppable https://news.ycombinator.com/item?id=16727665
|
- Gron: A command line tool that makes JSON greppable https://news.ycombinator.com/item?id=16727665
|
||||||
@@ -371,31 +303,3 @@ Inspiring links:
|
|||||||
- https://auth0.com/blog/beating-json-performance-with-protobuf/
|
- https://auth0.com/blog/beating-json-performance-with-protobuf/
|
||||||
- https://gist.github.com/shijuvar/25ad7de9505232c87034b8359543404a
|
- https://gist.github.com/shijuvar/25ad7de9505232c87034b8359543404a
|
||||||
- https://github.com/frankmcsherry/blog/blob/master/posts/2018-02-11.md
|
- https://github.com/frankmcsherry/blog/blob/master/posts/2018-02-11.md
|
||||||
|
|
||||||
Validating UTF-8 takes no more than 0.7 cycles per byte:
|
|
||||||
|
|
||||||
- https://github.com/lemire/fastvalidate-utf-8 https://lemire.me/blog/2018/05/16/validating-utf-8-strings-using-as-little-as-0-7-cycles-per-byte/
|
|
||||||
|
|
||||||
### Academic References
|
|
||||||
|
|
||||||
- T.Mühlbauer, W.Rödiger, R.Seilbeck, A.Reiser, A.Kemper, and T.Neumann. Instant loading for main memory databases. PVLDB, 6(14):1702–1713, 2013. (SIMD-based CSV parsing)
|
|
||||||
- Mytkowicz, Todd, Madanlal Musuvathi, and Wolfram Schulte. "Data-parallel finite-state machines." ACM SIGARCH Computer Architecture News. Vol. 42. No. 1. ACM, 2014.
|
|
||||||
- Lu, Yifan, et al. "Tree structured data processing on GPUs." Cloud Computing, Data Science & Engineering-Confluence, 2017 7th International Conference on. IEEE, 2017.
|
|
||||||
- Sidhu, Reetinder. "High throughput, tree automata based XML processing using FPGAs." Field-Programmable Technology (FPT), 2013 International Conference on. IEEE, 2013.
|
|
||||||
- Dai, Zefu, Nick Ni, and Jianwen Zhu. "A 1 cycle-per-byte XML parsing accelerator." Proceedings of the 18th annual ACM/SIGDA international symposium on Field programmable gate arrays. ACM, 2010.
|
|
||||||
- Lin, Dan, et al. "Parabix: Boosting the efficiency of text processing on commodity processors." High Performance Computer Architecture (HPCA), 2012 IEEE 18th International Symposium on. IEEE, 2012. http://parabix.costar.sfu.ca/export/1783/docs/HPCA2012/final_ieee/final.pdf
|
|
||||||
- Deshmukh, V. M., and G. R. Bamnote. "An empirical evaluation of optimization parameters in XML parsing for performance enhancement." Computer, Communication and Control (IC4), 2015 International Conference on. IEEE, 2015.
|
|
||||||
- Moussalli, Roger, et al. "Efficient XML Path Filtering Using GPUs." ADMS@ VLDB. 2011.
|
|
||||||
- Jianliang, Ma, et al. "Parallel speculative dom-based XML parser." High Performance Computing and Communication & 2012 IEEE 9th International Conference on Embedded Software and Systems (HPCC-ICESS), 2012 IEEE 14th International Conference on. IEEE, 2012.
|
|
||||||
- Li, Y., Katsipoulakis, N.R., Chandramouli, B., Goldstein, J. and Kossmann, D., 2017. Mison: a fast JSON parser for data analytics. Proceedings of the VLDB Endowment, 10(10), pp.1118-1129. http://www.vldb.org/pvldb/vol10/p1118-li.pdf
|
|
||||||
- Cameron, Robert D., et al. "Parallel scanning with bitstream addition: An xml case study." European Conference on Parallel Processing. Springer, Berlin, Heidelberg, 2011.
|
|
||||||
- Cameron, Robert D., Kenneth S. Herdy, and Dan Lin. "High performance XML parsing using parallel bit stream technology." Proceedings of the 2008 conference of the center for advanced studies on collaborative research: meeting of minds. ACM, 2008.
|
|
||||||
- Shah, Bhavik, et al. "A data parallel algorithm for XML DOM parsing." International XML Database Symposium. Springer, Berlin, Heidelberg, 2009.
|
|
||||||
- Cameron, Robert D., and Dan Lin. "Architectural support for SWAR text processing with parallel bit streams: the inductive doubling principle." ACM Sigplan Notices. Vol. 44. No. 3. ACM, 2009.
|
|
||||||
- Amagasa, Toshiyuki, Mana Seino, and Hiroyuki Kitagawa. "Energy-Efficient XML Stream Processing through Element-Skipping Parsing." Database and Expert Systems Applications (DEXA), 2013 24th International Workshop on. IEEE, 2013.
|
|
||||||
- Medforth, Nigel Woodland. "icXML: Accelerating Xerces-C 3.1. 1 using the Parabix Framework." (2013).
|
|
||||||
- Zhang, Qiang Scott. Embedding Parallel Bit Stream Technology Into Expat. Diss. Simon Fraser University, 2010.
|
|
||||||
- Cameron, Robert D., et al. "Fast Regular Expression Matching with Bit-parallel Data Streams."
|
|
||||||
- Lin, Dan. Bits filter: a high-performance multiple string pattern matching algorithm for malware detection. Diss. School of Computing Science-Simon Fraser University, 2010.
|
|
||||||
- Yang, Shiyang. Validation of XML Document Based on Parallel Bit Stream Technology. Diss. Applied Sciences: School of Computing Science, 2013.
|
|
||||||
- N. Nakasato, "Implementation of a parallel tree method on a GPU", Journal of Computational Science, vol. 3, no. 3, pp. 132-141, 2012.
|
|
||||||
|
|||||||
@@ -1,301 +0,0 @@
|
|||||||
REFERENCE_VERSION = master
|
|
||||||
|
|
||||||
.SUFFIXES:
|
|
||||||
#
|
|
||||||
.SUFFIXES: .cpp .o .c .h
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: clean cleandist
|
|
||||||
COREDEPSINCLUDE = -Idependencies/json/single_include -Idependencies/rapidjson/include -Idependencies/sajson/include -Idependencies/cJSON -Idependencies/jsmn
|
|
||||||
EXTRADEPSINCLUDE = -Idependencies/jsoncppdist -Idependencies/json11 -Idependencies/fastjson/src -Idependencies/fastjson/include -Idependencies/gason/src -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src
|
|
||||||
# users can provide their own additional flags with make EXTRAFLAGS=something
|
|
||||||
architecture:=$(shell arch)
|
|
||||||
|
|
||||||
####
|
|
||||||
# If you want to specify your own target architecture,
|
|
||||||
# then define ARCHFLAGS. Otherwise, we set good default.
|
|
||||||
# E.g., type ' ARCHFLAGS="-march=westmere" make parse '
|
|
||||||
###
|
|
||||||
|
|
||||||
CXXFLAGS = $(ARCHFLAGS) -std=c++17 -pthread -Wall -Wextra -Wshadow -Ibenchmark/linux
|
|
||||||
CFLAGS = $(ARCHFLAGS) -Idependencies/ujson4c/3rdparty -Idependencies/ujson4c/src $(EXTRAFLAGS)
|
|
||||||
|
|
||||||
# This is a convenience flag
|
|
||||||
ifdef SANITIZEGOLD
|
|
||||||
SANITIZE = 1
|
|
||||||
LINKER = gold
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifdef LINKER
|
|
||||||
CXXFLAGS += -fuse-ld=$(LINKER)
|
|
||||||
CFLAGS += -fuse-ld=$(LINKER)
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
# SANITIZE *implies* DEBUG
|
|
||||||
ifeq ($(MEMSANITIZE),1)
|
|
||||||
CXXFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
|
|
||||||
CFLAGS += -g3 -O0 -fsanitize=memory -fno-omit-frame-pointer -fsanitize=undefined
|
|
||||||
else
|
|
||||||
ifeq ($(SANITIZE),1)
|
|
||||||
CXXFLAGS += -g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
|
|
||||||
CFLAGS += -g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined
|
|
||||||
else
|
|
||||||
ifeq ($(DEBUG),1)
|
|
||||||
CXXFLAGS += -g3 -O0
|
|
||||||
CFLAGS += -g3 -O0
|
|
||||||
else
|
|
||||||
# we opt for -O3 for regular builds
|
|
||||||
CXXFLAGS += -O3
|
|
||||||
CFLAGS += -O3
|
|
||||||
endif # ifeq ($(DEBUG),1)
|
|
||||||
endif # ifeq ($(SANITIZE),1)
|
|
||||||
endif # ifeq ($(MEMSANITIZE),1)
|
|
||||||
|
|
||||||
# Headers and sources
|
|
||||||
SRCHEADERS_GENERIC=src/generic/atomparsing.h src/generic/numberparsing.h src/generic/json_scanner.h src/generic/json_string_scanner.h src/generic/json_structural_indexer.h src/generic/json_minifier.h src/generic/buf_block_reader.h src/generic/stage2_build_tape.h src/generic/stringparsing.h src/generic/stage2_streaming_build_tape.h src/generic/utf8_fastvalidate_algorithm.h src/generic/utf8_lookup_algorithm.h src/generic/utf8_lookup2_algorithm.h src/generic/utf8_range_algorithm.h src/generic/utf8_zwegner_algorithm.h
|
|
||||||
SRCHEADERS_ARM64= src/arm64/bitmanipulation.h src/arm64/bitmask.h src/arm64/intrinsics.h src/arm64/numberparsing.h src/arm64/simd.h src/arm64/stage1_find_marks.h src/arm64/stage2_build_tape.h src/arm64/stringparsing.h
|
|
||||||
SRCHEADERS_HASWELL= src/haswell/bitmanipulation.h src/haswell/bitmask.h src/haswell/intrinsics.h src/haswell/numberparsing.h src/haswell/simd.h src/haswell/stage1_find_marks.h src/haswell/stage2_build_tape.h src/haswell/stringparsing.h
|
|
||||||
SRCHEADERS_FALLBACK= src/fallback/bitmanipulation.h src/fallback/implementation.h src/fallback/numberparsing.h src/fallback/stage1_find_marks.h src/fallback/stage2_build_tape.h src/fallback/stringparsing.h
|
|
||||||
SRCHEADERS_WESTMERE=src/westmere/bitmanipulation.h src/westmere/bitmask.h src/westmere/intrinsics.h src/westmere/numberparsing.h src/westmere/simd.h src/westmere/stage1_find_marks.h src/westmere/stage2_build_tape.h src/westmere/stringparsing.h
|
|
||||||
SRCHEADERS_SRC=src/isadetection.h src/jsoncharutils.h src/simdprune_tables.h src/implementation.cpp src/stage1_find_marks.cpp src/stage2_build_tape.cpp src/document_parser_callbacks.h
|
|
||||||
SRCHEADERS=$(SRCHEADERS_SRC) $(SRCHEADERS_GENERIC) $(SRCHEADERS_ARM64) $(SRCHEADERS_HASWELL) $(SRCHEADERS_WESTMERE) $(SRCHEADERS_FALLBACK)
|
|
||||||
|
|
||||||
INCLUDEHEADERS=include/simdjson.h include/simdjson/common_defs.h include/simdjson/internal/jsonformatutils.h include/simdjson/jsonioutil.h include/simdjson/jsonparser.h include/simdjson/padded_string.h include/simdjson/inline/padded_string.h include/simdjson/document.h include/simdjson/inline/document.h include/simdjson/parsedjson_iterator.h include/simdjson/inline/parsedjson_iterator.h include/simdjson/document_stream.h include/simdjson/inline/document_stream.h include/simdjson/implementation.h include/simdjson/parsedjson.h include/simdjson/portability.h include/simdjson/error.h include/simdjson/inline/error.h include/simdjson/simdjson.h include/simdjson/simdjson_version.h
|
|
||||||
|
|
||||||
ifeq ($(SIMDJSON_TEST_AMALGAMATED_HEADERS),1)
|
|
||||||
HEADERS=singleheader/simdjson.h
|
|
||||||
LIBFILES=singleheader/simdjson.cpp
|
|
||||||
CXXFLAGS += -Isingleheader
|
|
||||||
else
|
|
||||||
HEADERS=$(INCLUDEHEADERS) $(SRCHEADERS)
|
|
||||||
LIBFILES=src/simdjson.cpp
|
|
||||||
CXXFLAGS += -Isrc -Iinclude
|
|
||||||
endif
|
|
||||||
|
|
||||||
# We put EXTRAFLAGS after all other CXXFLAGS so they can override if necessary
|
|
||||||
CXXFLAGS += $(EXTRAFLAGS)
|
|
||||||
|
|
||||||
FEATURE_JSON_FILES=jsonexamples/generated/0-structurals-full.json jsonexamples/generated/0-structurals-miss.json jsonexamples/generated/0-structurals.json jsonexamples/generated/15-structurals-full.json jsonexamples/generated/15-structurals-miss.json jsonexamples/generated/15-structurals.json jsonexamples/generated/23-structurals-full.json jsonexamples/generated/23-structurals-miss.json jsonexamples/generated/23-structurals.json jsonexamples/generated/7-structurals-full.json jsonexamples/generated/7-structurals-miss.json jsonexamples/generated/7-structurals.json jsonexamples/generated/escape-full.json jsonexamples/generated/escape-miss.json jsonexamples/generated/escape.json jsonexamples/generated/utf-8-full.json jsonexamples/generated/utf-8-miss.json jsonexamples/generated/utf-8.json
|
|
||||||
|
|
||||||
RAPIDJSON_INCLUDE:=dependencies/rapidjson/include
|
|
||||||
SAJSON_INCLUDE:=dependencies/sajson/include
|
|
||||||
JSON11_INCLUDE:=dependencies/json11/json11.hpp
|
|
||||||
FASTJSON_INCLUDE:=dependencies/include/fastjson/fastjson.h
|
|
||||||
GASON_INCLUDE:=dependencies/gason/src/gason.h
|
|
||||||
UJSON4C_INCLUDE:=dependencies/ujson4c/src/ujdecode.c
|
|
||||||
CJSON_INCLUDE:=dependencies/cJSON/cJSON.h
|
|
||||||
JSMN_INCLUDE:=dependencies/jsmn/jsmn.h
|
|
||||||
JSON_INCLUDE:=dependencies/json/single_include/nlohmann/json.hpp
|
|
||||||
|
|
||||||
EXTRAOBJECTS=ujdecode.o
|
|
||||||
|
|
||||||
MAINEXECUTABLES=parse minify json2json jsonstats statisticalmodel jsonpointer get_corpus_benchmark
|
|
||||||
TESTEXECUTABLES=jsoncheck jsoncheck_westmere jsoncheck_fallback integer_tests numberparsingcheck stringparsingcheck pointercheck parse_many_test basictests errortests readme_examples readme_examples_noexceptions
|
|
||||||
COMPARISONEXECUTABLES=minifiercompetition parsingcompetition parseandstatcompetition distinctuseridcompetition allparserscheckfile allparsingcompetition
|
|
||||||
SUPPLEMENTARYEXECUTABLES=parse_noutf8validation parse_nonumberparsing parse_nostringparsing
|
|
||||||
|
|
||||||
all: $(MAINEXECUTABLES)
|
|
||||||
|
|
||||||
competition: $(COMPARISONEXECUTABLES)
|
|
||||||
|
|
||||||
.PHONY: benchmark test
|
|
||||||
|
|
||||||
benchmark:
|
|
||||||
bash ./scripts/parser.sh
|
|
||||||
bash ./scripts/parseandstat.sh
|
|
||||||
|
|
||||||
run_basictests: basictests
|
|
||||||
./basictests
|
|
||||||
|
|
||||||
run_errortests: errortests
|
|
||||||
./errortests
|
|
||||||
|
|
||||||
run_numberparsingcheck: numberparsingcheck
|
|
||||||
./numberparsingcheck
|
|
||||||
|
|
||||||
run_integer_tests: integer_tests
|
|
||||||
./integer_tests
|
|
||||||
|
|
||||||
run_stringparsingcheck: stringparsingcheck
|
|
||||||
./stringparsingcheck
|
|
||||||
|
|
||||||
run_jsoncheck: jsoncheck
|
|
||||||
./jsoncheck
|
|
||||||
|
|
||||||
run_parse_many_test: parse_many_test
|
|
||||||
./parse_many_test
|
|
||||||
|
|
||||||
run_jsoncheck_westmere: jsoncheck_westmere
|
|
||||||
./jsoncheck_westmere
|
|
||||||
|
|
||||||
run_jsoncheck_fallback: jsoncheck_fallback
|
|
||||||
./jsoncheck_fallback
|
|
||||||
|
|
||||||
run_pointercheck: pointercheck
|
|
||||||
./pointercheck
|
|
||||||
|
|
||||||
run_issue150_sh: allparserscheckfile
|
|
||||||
./scripts/issue150.sh
|
|
||||||
|
|
||||||
quickstart:
|
|
||||||
cd examples/quickstart && make quickstart
|
|
||||||
|
|
||||||
run_quickstart:
|
|
||||||
cd examples/quickstart && make test
|
|
||||||
|
|
||||||
run_testjson2json_sh: minify json2json
|
|
||||||
./scripts/testjson2json.sh
|
|
||||||
|
|
||||||
$(FEATURE_JSON_FILES): benchmark/genfeaturejson.rb
|
|
||||||
ruby ./benchmark/genfeaturejson.rb
|
|
||||||
|
|
||||||
run_benchfeatures: benchfeatures $(FEATURE_JSON_FILES)
|
|
||||||
./benchfeatures -n 1000
|
|
||||||
|
|
||||||
test: quicktests slowtests
|
|
||||||
@echo "It looks like the code is good!"
|
|
||||||
|
|
||||||
quiettest: quicktests slowtests
|
|
||||||
|
|
||||||
quicktests: run_basictests run_quickstart readme_examples readme_examples_noexceptions run_jsoncheck run_numberparsingcheck run_integer_tests run_stringparsingcheck run_jsoncheck run_parse_many_test run_pointercheck run_jsoncheck_westmere run_jsoncheck_fallback
|
|
||||||
|
|
||||||
slowtests: run_testjson2json_sh run_issue150_sh
|
|
||||||
|
|
||||||
amalgamate:
|
|
||||||
./amalgamation.sh
|
|
||||||
|
|
||||||
singleheader/simdjson.h singleheader/simdjson.cpp singleheader/amalgamation_demo.cpp: amalgamation.sh src/simdjson.cpp $(SRCHEADERS) $(INCLUDEHEADERS)
|
|
||||||
./amalgamation.sh
|
|
||||||
|
|
||||||
singleheader/demo: singleheader/simdjson.h singleheader/simdjson.cpp singleheader/amalgamation_demo.cpp
|
|
||||||
$(CXX) $(CXXFLAGS) -o singleheader/demo singleheader/amalgamation_demo.cpp -Isingleheader
|
|
||||||
|
|
||||||
submodules:
|
|
||||||
-git submodule update --init --recursive
|
|
||||||
-touch submodules
|
|
||||||
|
|
||||||
$(JSON_INCLUDE) $(SAJSON_INCLUDE) $(RAPIDJSON_INCLUDE) $(JSON11_INCLUDE) $(FASTJSON_INCLUDE) $(GASON_INCLUDE) $(UJSON4C_INCLUDE) $(CJSON_INCLUDE) $(JSMN_INCLUDE) : submodules
|
|
||||||
|
|
||||||
parse: benchmark/parse.cpp benchmark/event_counter.h benchmark/benchmarker.h $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o parse benchmark/parse.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
get_corpus_benchmark: benchmark/get_corpus_benchmark.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o get_corpus_benchmark benchmark/get_corpus_benchmark.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
parse_stream: benchmark/parse_stream.cpp benchmark/event_counter.h benchmark/benchmarker.h $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o parse_stream benchmark/parse_stream.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
benchfeatures: benchmark/benchfeatures.cpp benchmark/event_counter.h benchmark/benchmarker.h $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o benchfeatures benchmark/benchfeatures.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
perfdiff: benchmark/perfdiff.cpp
|
|
||||||
$(CXX) $(CXXFLAGS) -o perfdiff benchmark/perfdiff.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
checkperf:
|
|
||||||
bash ./scripts/checkperf.sh $(REFERENCE_VERSION)
|
|
||||||
|
|
||||||
statisticalmodel: benchmark/statisticalmodel.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o statisticalmodel benchmark/statisticalmodel.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
parse_noutf8validation: benchmark/parse.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o parse_noutf8validation -DSIMDJSON_SKIPUTF8VALIDATION benchmark/parse.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
parse_nonumberparsing: benchmark/parse.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o parse_nonumberparsing -DSIMDJSON_SKIPNUMBERPARSING benchmark/parse.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
parse_nostringparsing: benchmark/parse.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o parse_nostringparsing -DSIMDJSON_SKIPSTRINGPARSING benchmark/parse.cpp $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
jsoncheck:tests/jsoncheck.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o jsoncheck tests/jsoncheck.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
parse_many_test:tests/parse_many_test.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o parse_many_test tests/parse_many_test.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
jsoncheck_westmere:tests/jsoncheck.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o jsoncheck_westmere tests/jsoncheck.cpp -I. $(LIBFILES) $(LIBFLAGS) -DSIMDJSON_IMPLEMENTATION_HASWELL=0
|
|
||||||
|
|
||||||
jsoncheck_fallback:tests/jsoncheck.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o jsoncheck_fallback tests/jsoncheck.cpp -I. $(LIBFILES) $(LIBFLAGS) -DSIMDJSON_IMPLEMENTATION_HASWELL=0 -DSIMDJSON_IMPLEMENTATION_WESTMERE=0 -DSIMDJSON_IMPLEMENTATION_ARM64=0
|
|
||||||
|
|
||||||
basictests:tests/basictests.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o basictests tests/basictests.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
errortests:tests/errortests.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o errortests tests/errortests.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
readme_examples: tests/readme_examples.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o readme_examples tests/readme_examples.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
readme_examples_noexceptions: tests/readme_examples_noexceptions.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o readme_examples_noexceptions tests/readme_examples_noexceptions.cpp -I. $(LIBFILES) $(LIBFLAGS) -fno-exceptions
|
|
||||||
|
|
||||||
numberparsingcheck: tests/numberparsingcheck.cpp $(HEADERS) src/simdjson.cpp
|
|
||||||
$(CXX) $(CXXFLAGS) -o numberparsingcheck tests/numberparsingcheck.cpp -I. $(LIBFLAGS) -DJSON_TEST_NUMBERS
|
|
||||||
|
|
||||||
integer_tests:tests/integer_tests.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o integer_tests tests/integer_tests.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
stringparsingcheck: tests/stringparsingcheck.cpp $(HEADERS) src/simdjson.cpp
|
|
||||||
$(CXX) $(CXXFLAGS) -o stringparsingcheck tests/stringparsingcheck.cpp -I. $(LIBFLAGS) -DJSON_TEST_STRINGS
|
|
||||||
|
|
||||||
pointercheck:tests/pointercheck.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o pointercheck tests/pointercheck.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
minifiercompetition: benchmark/minifiercompetition.cpp submodules $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o minifiercompetition benchmark/minifiercompetition.cpp -I. $(LIBFILES) $(LIBFLAGS) $(COREDEPSINCLUDE)
|
|
||||||
|
|
||||||
minify: tools/minify.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o minify tools/minify.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
json2json: tools/json2json.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o json2json $ tools/json2json.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
jsonpointer: tools/jsonpointer.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o jsonpointer $ tools/jsonpointer.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
jsonstats: tools/jsonstats.cpp $(HEADERS) $(LIBFILES)
|
|
||||||
$(CXX) $(CXXFLAGS) -o jsonstats $ tools/jsonstats.cpp -I. $(LIBFILES) $(LIBFLAGS)
|
|
||||||
|
|
||||||
ujdecode.o: $(UJSON4C_INCLUDE)
|
|
||||||
$(CC) $(CFLAGS) -c dependencies/ujson4c/src/ujdecode.c
|
|
||||||
|
|
||||||
parseandstatcompetition: benchmark/parseandstatcompetition.cpp $(HEADERS) $(LIBFILES) submodules
|
|
||||||
$(CXX) $(CXXFLAGS) -o parseandstatcompetition benchmark/parseandstatcompetition.cpp -I. $(LIBFILES) $(LIBFLAGS) $(COREDEPSINCLUDE)
|
|
||||||
|
|
||||||
distinctuseridcompetition: benchmark/distinctuseridcompetition.cpp $(HEADERS) $(LIBFILES) submodules
|
|
||||||
$(CXX) $(CXXFLAGS) -o distinctuseridcompetition benchmark/distinctuseridcompetition.cpp -I. $(LIBFILES) $(LIBFLAGS) $(COREDEPSINCLUDE)
|
|
||||||
|
|
||||||
parsingcompetition: benchmark/parsingcompetition.cpp $(HEADERS) $(LIBFILES) submodules
|
|
||||||
@echo "In case of build error due to missing files, try 'make clean'"
|
|
||||||
$(CXX) $(CXXFLAGS) -o parsingcompetition benchmark/parsingcompetition.cpp -I. $(LIBFILES) $(LIBFLAGS) $(COREDEPSINCLUDE)
|
|
||||||
|
|
||||||
allparsingcompetition: benchmark/parsingcompetition.cpp $(HEADERS) $(LIBFILES) $(EXTRAOBJECTS) submodules
|
|
||||||
$(CXX) $(CXXFLAGS) -o allparsingcompetition benchmark/parsingcompetition.cpp $(EXTRAOBJECTS) -I. $(LIBFILES) $(LIBFLAGS) $(COREDEPSINCLUDE) $(EXTRADEPSINCLUDE) -DALLPARSER
|
|
||||||
|
|
||||||
|
|
||||||
allparserscheckfile: tests/allparserscheckfile.cpp $(HEADERS) $(LIBFILES) $(EXTRAOBJECTS) submodules
|
|
||||||
$(CXX) $(CXXFLAGS) -o allparserscheckfile tests/allparserscheckfile.cpp $(EXTRAOBJECTS) -I. $(LIBFILES) $(LIBFLAGS) $(COREDEPSINCLUDE) $(EXTRADEPSINCLUDE)
|
|
||||||
|
|
||||||
.PHONY: clean cppcheck cleandist
|
|
||||||
|
|
||||||
cppcheck:
|
|
||||||
cppcheck --enable=all src/*.cpp benchmarks/*.cpp tests/*.cpp -Iinclude -I. -Ibenchmark/linux
|
|
||||||
|
|
||||||
everything: $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES) quickstart
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f submodules $(EXTRAOBJECTS) $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
|
|
||||||
cd examples/quickstart && make clean
|
|
||||||
|
|
||||||
cleandist:
|
|
||||||
rm -f submodules $(EXTRAOBJECTS) $(MAINEXECUTABLES) $(EXTRA_EXECUTABLES) $(TESTEXECUTABLES) $(COMPARISONEXECUTABLES) $(SUPPLEMENTARYEXECUTABLES)
|
|
||||||
|
|
||||||
doc/api: Doxyfile $(HEADERS)
|
|
||||||
doxygen
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
[](https://cloud.drone.io/simdjson/simdjson)
|
|
||||||
[](https://circleci.com/gh/simdjson/simdjson)
|
/badge.svg)
|
||||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&q=proj%3Asimdjson&can=2)
|
[/badge.svg)](https://simdjson.org/plots.html)
|
||||||
[](https://ci.appveyor.com/project/lemire/simdjson-jmmti/branch/master)
|

|
||||||
[![][license img]][license]
|

|
||||||
|
[![][license img]][license] [](https://simdjson.org/api/2.0.0/index.html)
|
||||||
|
|
||||||
simdjson : Parsing gigabytes of JSON per second
|
simdjson : Parsing gigabytes of JSON per second
|
||||||
===============================================
|
===============================================
|
||||||
@@ -10,13 +11,15 @@ simdjson : Parsing gigabytes of JSON per second
|
|||||||
<img src="images/logo.png" width="10%" style="float: right">
|
<img src="images/logo.png" width="10%" style="float: right">
|
||||||
JSON is everywhere on the Internet. Servers spend a *lot* of time parsing it. We need a fresh
|
JSON is everywhere on the Internet. Servers spend a *lot* of time parsing it. We need a fresh
|
||||||
approach. The simdjson library uses commonly available SIMD instructions and microparallel algorithms
|
approach. The simdjson library uses commonly available SIMD instructions and microparallel algorithms
|
||||||
to parse JSON 2.5x faster than anything else out there.
|
to parse JSON 4x faster than RapidJSON and 25x faster than JSON for Modern C++.
|
||||||
|
|
||||||
* **Fast:** Over 2.5x faster than other production-grade JSON parsers.
|
* **Fast:** Over 4x faster than commonly used production-grade JSON parsers.
|
||||||
* **Easy:** First-class, easy to use API.
|
* **Record Breaking Features:** Minify JSON at 6 GB/s, validate UTF-8 at 13 GB/s, NDJSON at 3.5 GB/s.
|
||||||
|
* **Easy:** First-class, easy to use and carefully documented APIs.
|
||||||
* **Strict:** Full JSON and UTF-8 validation, lossless parsing. Performance with no compromises.
|
* **Strict:** Full JSON and UTF-8 validation, lossless parsing. Performance with no compromises.
|
||||||
* **Automatic:** Selects a CPU-tailored parser at runtime. No configuration needed.
|
* **Automatic:** Selects a CPU-tailored parser at runtime. No configuration needed.
|
||||||
* **Reliable:** From memory allocation to error handling, simdjson's design avoids surprises.
|
* **Reliable:** From memory allocation to error handling, simdjson's design avoids surprises.
|
||||||
|
* **Peer Reviewed:** Our research appears in venues like VLDB Journal, Software: Practice and Experience.
|
||||||
|
|
||||||
This library is part of the [Awesome Modern C++](https://awesomecpp.com) list.
|
This library is part of the [Awesome Modern C++](https://awesomecpp.com) list.
|
||||||
|
|
||||||
@@ -36,25 +39,32 @@ Table of Contents
|
|||||||
Quick Start
|
Quick Start
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
||||||
The simdjson library is easily consumable with a single .h and .cpp file.
|
The simdjson library is easily consumable with a single .h and .cpp file.
|
||||||
|
|
||||||
0. Prerequisites: `g++` (version 7 or better) or `clang++` (version 6 or better), and a 64-bit system.
|
0. Prerequisites: `g++` (version 7 or better) or `clang++` (version 6 or better), and a 64-bit
|
||||||
1. Pull [simdjson.h](singleheader/simdjson.h) and [simdjson.cpp](singleheader/simdjson.cpp) into a directory, along with the sample file [twitter.json](jsonexamples/twitter.json).
|
system with a command-line shell (e.g., Linux, macOS, freeBSD). We also support programming
|
||||||
|
environments like Visual Studio and Xcode, but different steps are needed.
|
||||||
|
1. Pull [simdjson.h](singleheader/simdjson.h) and [simdjson.cpp](singleheader/simdjson.cpp) into a
|
||||||
|
directory, along with the sample file [twitter.json](jsonexamples/twitter.json).
|
||||||
|
|
||||||
```
|
```
|
||||||
wget https://raw.githubusercontent.com/simdjson/simdjson/master/singleheader/simdjson.h https://raw.githubusercontent.com/simdjson/simdjson/master/singleheader/simdjson.cpp https://raw.githubusercontent.com/simdjson/simdjson/master/jsonexamples/twitter.json
|
wget https://raw.githubusercontent.com/simdjson/simdjson/master/singleheader/simdjson.h https://raw.githubusercontent.com/simdjson/simdjson/master/singleheader/simdjson.cpp https://raw.githubusercontent.com/simdjson/simdjson/master/jsonexamples/twitter.json
|
||||||
```
|
```
|
||||||
2. Create `quickstart.cpp`:
|
2. Create `quickstart.cpp`:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
#include "simdjson.h"
|
#include <iostream>
|
||||||
int main(void) {
|
#include "simdjson.h"
|
||||||
simdjson::dom::parser parser;
|
using namespace simdjson;
|
||||||
simdjson::dom::element tweets = parser.load("twitter.json");
|
int main(void) {
|
||||||
std::cout << tweets["search_metadata"]["count"] << " results." << std::endl;
|
ondemand::parser parser;
|
||||||
}
|
padded_string json = padded_string::load("twitter.json");
|
||||||
|
ondemand::document tweets = parser.iterate(json);
|
||||||
|
std::cout << uint64_t(tweets["search_metadata"]["count"]) << " results." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
3. `c++ -o quickstart quickstart.cpp simdjson.cpp -std=c++17`
|
3. `c++ -o quickstart quickstart.cpp simdjson.cpp`
|
||||||
4. `./quickstart`
|
4. `./quickstart`
|
||||||
```
|
```
|
||||||
100 results.
|
100 results.
|
||||||
@@ -69,65 +79,44 @@ Usage documentation is available:
|
|||||||
* [Performance](doc/performance.md) shows some more advanced scenarios and how to tune for them.
|
* [Performance](doc/performance.md) shows some more advanced scenarios and how to tune for them.
|
||||||
* [Implementation Selection](doc/implementation-selection.md) describes runtime CPU detection and
|
* [Implementation Selection](doc/implementation-selection.md) describes runtime CPU detection and
|
||||||
how you can work with it.
|
how you can work with it.
|
||||||
* [API](https://simdjson.org/api/0.3.0/annotated.html) contains the automatically generated API documentation.
|
* [API](https://simdjson.org/api/1.0.0/annotated.html) contains the automatically generated API documentation.
|
||||||
|
|
||||||
Performance results
|
Performance results
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
The simdjson library uses three-quarters less instructions than state-of-the-art parser RapidJSON and
|
The simdjson library uses three-quarters less instructions than state-of-the-art parser [RapidJSON](https://rapidjson.org). To our knowledge, simdjson is the first fully-validating JSON parser
|
||||||
fifty percent less than sajson. To our knowledge, simdjson is the first fully-validating JSON parser
|
to run at [gigabytes per second](https://en.wikipedia.org/wiki/Gigabyte) (GB/s) on commodity processors. It can parse millions of JSON documents per second on a single core.
|
||||||
to run at gigabytes per second on commodity processors. It can parse millions of JSON documents
|
|
||||||
per second on a single core.
|
|
||||||
|
|
||||||
The following figure represents parsing speed in GB/s for parsing various files
|
The following figure represents parsing speed in GB/s for parsing various files
|
||||||
on an Intel Skylake processor (3.4 GHz) using the GNU GCC 9 compiler (with the -O3 flag).
|
on an Intel Skylake processor (3.4 GHz) using the GNU GCC 10 compiler (with the -O3 flag).
|
||||||
We compare against the best and fastest C++ libraries.
|
We compare against the best and fastest C++ libraries on benchmarks that load and process the data.
|
||||||
The simdjson library offers full unicode (UTF-8) validation and exact
|
The simdjson library offers full unicode ([UTF-8](https://en.wikipedia.org/wiki/UTF-8)) validation and exact
|
||||||
number parsing. The RapidJSON library is tested in two modes: fast and
|
number parsing.
|
||||||
exact number parsing. The sajson library offers fast (but not exact)
|
|
||||||
number parsing and partial unicode validation. In this data set, the file
|
|
||||||
sizes range from 65KB (github_events) all the way to 3.3GB (gsoc-2018).
|
|
||||||
Many files are mostly made of numbers: canada, mesh.pretty, mesh, random
|
|
||||||
and numbers: in such instances, we see lower JSON parsing speeds due to the
|
|
||||||
high cost of number parsing. The simdjson library uses exact number parsing which
|
|
||||||
is particular taxing.
|
|
||||||
|
|
||||||
<img src="doc/gbps.png" width="90%">
|
|
||||||
|
|
||||||
On a Skylake processor, the parsing speeds (in GB/s) of various processors on the twitter.json file are as follows, using again GNU GCC 9.1 (with the -O3 flag). The popular JSON for Modern C++ library is particularly slow: it obviously trades parsing speed for other desirable features.
|
|
||||||
|
|
||||||
| parser | GB/s |
|
|
||||||
| ------------------------------------- | ---- |
|
|
||||||
| simdjson | 2.5 |
|
|
||||||
| RapidJSON UTF8-validation | 0.29 |
|
|
||||||
| RapidJSON UTF8-valid., exact numbers | 0.28 |
|
|
||||||
| RapidJSON insitu, UTF8-validation | 0.41 |
|
|
||||||
| RapidJSON insitu, UTF8-valid., exact | 0.39 |
|
|
||||||
| sajson (insitu, dynamic) | 0.62 |
|
|
||||||
| sajson (insitu, static) | 0.88 |
|
|
||||||
| dropbox | 0.13 |
|
|
||||||
| fastjson | 0.27 |
|
|
||||||
| gason | 0.59 |
|
|
||||||
| ultrajson | 0.34 |
|
|
||||||
| jsmn | 0.25 |
|
|
||||||
| cJSON | 0.31 |
|
|
||||||
| JSON for Modern C++ (nlohmann/json) | 0.11 |
|
|
||||||
|
|
||||||
|
<img src="doc/rome.png" width="60%">
|
||||||
|
|
||||||
The simdjson library offers high speed whether it processes tiny files (e.g., 300 bytes)
|
The simdjson library offers high speed whether it processes tiny files (e.g., 300 bytes)
|
||||||
or larger files (e.g., 3MB). The following plot presents parsing
|
or larger files (e.g., 3MB). The following plot presents parsing
|
||||||
speed for [synthetic files over various sizes generated with a script](https://github.com/simdjson/simdjson_experiments_vldb2019/blob/master/experiments/growing/gen.py) on a 3.4 GHz Skylake processor (GNU GCC 9, -O3).
|
speed for [synthetic files over various sizes generated with a script](https://github.com/simdjson/simdjson_experiments_vldb2019/blob/master/experiments/growing/gen.py) on a 3.4 GHz Skylake processor (GNU GCC 9, -O3).
|
||||||
<img src="doc/growing.png" width="90%">
|
|
||||||
|
|
||||||
|
<img src="doc/growing.png" width="60%">
|
||||||
|
|
||||||
[All our experiments are reproducible](https://github.com/simdjson/simdjson_experiments_vldb2019).
|
[All our experiments are reproducible](https://github.com/simdjson/simdjson_experiments_vldb2019).
|
||||||
|
|
||||||
|
|
||||||
|
For NDJSON files, we can exceed 3 GB/s with [our multithreaded parsing functions](https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Real-world usage
|
Real-world usage
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
- [Microsoft FishStore](https://github.com/microsoft/FishStore)
|
- [ClickHouse](https://github.com/ClickHouse/ClickHouse)
|
||||||
- [Yandex ClickHouse](https://github.com/yandex/ClickHouse)
|
|
||||||
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
|
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
|
||||||
|
- [Shopify HeapProfiler](https://github.com/Shopify/heap-profiler)
|
||||||
|
- [StarRocks](https://github.com/StarRocks/starrocks)
|
||||||
|
- [Microsoft FishStore](https://github.com/microsoft/FishStore)
|
||||||
|
|
||||||
|
|
||||||
If you are planning to use simdjson in a product, please work from one of our releases.
|
If you are planning to use simdjson in a product, please work from one of our releases.
|
||||||
|
|
||||||
@@ -137,15 +126,22 @@ Bindings and Ports of simdjson
|
|||||||
We distinguish between "bindings" (which just wrap the C++ code) and a port to another programming language (which reimplements everything).
|
We distinguish between "bindings" (which just wrap the C++ code) and a port to another programming language (which reimplements everything).
|
||||||
|
|
||||||
- [ZippyJSON](https://github.com/michaeleisel/zippyjson): Swift bindings for the simdjson project.
|
- [ZippyJSON](https://github.com/michaeleisel/zippyjson): Swift bindings for the simdjson project.
|
||||||
|
- [libpy_simdjson](https://github.com/gerrymanoim/libpy_simdjson/): high-speed Python bindings for simdjson using [libpy](https://github.com/quantopian/libpy).
|
||||||
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
|
- [pysimdjson](https://github.com/TkTech/pysimdjson): Python bindings for the simdjson project.
|
||||||
|
- [cysimdjson](https://github.com/TeskaLabs/cysimdjson): high-speed Python bindings for the simdjson project.
|
||||||
- [simdjson-rs](https://github.com/simd-lite): Rust port.
|
- [simdjson-rs](https://github.com/simd-lite): Rust port.
|
||||||
- [simdjson-rust](https://github.com/SunDoge/simdjson-rust): Rust wrapper (bindings).
|
- [simdjson-rust](https://github.com/SunDoge/simdjson-rust): Rust wrapper (bindings).
|
||||||
- [SimdJsonSharp](https://github.com/EgorBo/SimdJsonSharp): C# version for .NET Core (bindings and full port).
|
- [SimdJsonSharp](https://github.com/EgorBo/SimdJsonSharp): C# version for .NET Core (bindings and full port).
|
||||||
- [simdjson_nodejs](https://github.com/luizperes/simdjson_nodejs): Node.js bindings for the simdjson project.
|
- [simdjson_nodejs](https://github.com/luizperes/simdjson_nodejs): Node.js bindings for the simdjson project.
|
||||||
- [simdjson_php](https://github.com/crazyxman/simdjson_php): PHP bindings for the simdjson project.
|
- [simdjson_php](https://github.com/crazyxman/simdjson_php): PHP bindings for the simdjson project.
|
||||||
- [simdjson_ruby](https://github.com/saka1/simdjson_ruby): Ruby bindings for the simdjson project.
|
- [simdjson_ruby](https://github.com/saka1/simdjson_ruby): Ruby bindings for the simdjson project.
|
||||||
|
- [fast_jsonparser](https://github.com/anilmaurya/fast_jsonparser): Ruby bindings for the simdjson project.
|
||||||
- [simdjson-go](https://github.com/minio/simdjson-go): Go port using Golang assembly.
|
- [simdjson-go](https://github.com/minio/simdjson-go): Go port using Golang assembly.
|
||||||
- [rcppsimdjson](https://github.com/eddelbuettel/rcppsimdjson): R bindings.
|
- [rcppsimdjson](https://github.com/eddelbuettel/rcppsimdjson): R bindings.
|
||||||
|
- [simdjson_erlang](https://github.com/ChomperT/simdjson_erlang): erlang bindings.
|
||||||
|
- [lua-simdjson](https://github.com/FourierTransformer/lua-simdjson): lua bindings.
|
||||||
|
- [hermes-json](https://hackage.haskell.org/package/hermes-json): haskell bindings.
|
||||||
|
- [simdjzon](https://github.com/travisstaloch/simdjzon): zig port.
|
||||||
|
|
||||||
About simdjson
|
About simdjson
|
||||||
--------------
|
--------------
|
||||||
@@ -155,19 +151,24 @@ instructions, reducing branch misprediction, and reducing data dependency to tak
|
|||||||
CPU's multiple execution cores.
|
CPU's multiple execution cores.
|
||||||
|
|
||||||
Some people [enjoy reading our paper](https://arxiv.org/abs/1902.08318): A description of the design
|
Some people [enjoy reading our paper](https://arxiv.org/abs/1902.08318): A description of the design
|
||||||
and implementation of simdjson is in our research article: Geoff Langdale, Daniel
|
and implementation of simdjson is in our research article:
|
||||||
Lemire, [Parsing Gigabytes of JSON per Second](https://arxiv.org/abs/1902.08318), VLDB Journal 28 (6), 2019.
|
- Geoff Langdale, Daniel Lemire, [Parsing Gigabytes of JSON per Second](https://arxiv.org/abs/1902.08318), VLDB Journal 28 (6), 2019.
|
||||||
|
|
||||||
|
We have an in-depth paper focused on the UTF-8 validation:
|
||||||
|
|
||||||
|
- John Keiser, Daniel Lemire, [Validating UTF-8 In Less Than One Instruction Per Byte](https://arxiv.org/abs/2010.03090), Software: Practice & Experience 51 (5), 2021.
|
||||||
|
|
||||||
We also have an informal [blog post providing some background and context](https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/).
|
We also have an informal [blog post providing some background and context](https://branchfree.org/2019/02/25/paper-parsing-gigabytes-of-json-per-second/).
|
||||||
|
|
||||||
For the video inclined, <br />
|
For the video inclined, <br />
|
||||||
[](http://www.youtube.com/watch?v=wlvKAT7SZIQ)<br />
|
[](http://www.youtube.com/watch?v=wlvKAT7SZIQ)<br />
|
||||||
(it was the best voted talk, we're kinda proud of it).
|
(It was the best voted talk, we're kinda proud of it.)
|
||||||
|
|
||||||
Funding
|
Funding
|
||||||
-------
|
-------
|
||||||
|
|
||||||
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grant number RGPIN-2017-03910.
|
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grant
|
||||||
|
number RGPIN-2017-03910.
|
||||||
|
|
||||||
[license]: LICENSE
|
[license]: LICENSE
|
||||||
[license img]: https://img.shields.io/badge/License-Apache%202-blue.svg
|
[license img]: https://img.shields.io/badge/License-Apache%202-blue.svg
|
||||||
@@ -181,6 +182,12 @@ Head over to [CONTRIBUTING.md](CONTRIBUTING.md) for information on contributing
|
|||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
This code is made available under the Apache License 2.0.
|
This code is made available under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html).
|
||||||
|
|
||||||
Under Windows, we build some tools using the windows/dirent_portable.h file (which is outside our library code): it under the liberal (business-friendly) MIT license.
|
Under Windows, we build some tools using the windows/dirent_portable.h file (which is outside our library code): it under the liberal (business-friendly) MIT license.
|
||||||
|
|
||||||
|
For compilers that do not support [C++17](https://en.wikipedia.org/wiki/C%2B%2B17), we bundle the string-view library which is published under the [Boost license](http://www.boost.org/LICENSE_1_0.txt). Like the Apache license, the Boost license is a permissive license allowing commercial redistribution.
|
||||||
|
|
||||||
|
For efficient number serialization, we bundle Florian Loitsch's implementation of the Grisu2 algorithm for binary to decimal floating-point numbers. The implementation was slightly modified by JSON for Modern C++ library. Both Florian Loitsch's implementation and JSON for Modern C++ are provided under the MIT license.
|
||||||
|
|
||||||
|
For runtime dispatching, we use some code from the PyTorch project licensed under 3-clause BSD.
|
||||||
|
|||||||
+36
@@ -1,3 +1,39 @@
|
|||||||
|
# 0.5
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
|
||||||
|
Performance
|
||||||
|
* Faster and simpler UTF-8 validation with the lookup4 algorithm https://github.com/simdjson/simdjson/pull/993
|
||||||
|
* We improved the performance of simdjson under Visual Studio by about 25%. Users will still get better performance with clang-cl (+30%) but the gap has been reduced. https://github.com/simdjson/simdjson/pull/1031
|
||||||
|
|
||||||
|
Code usability
|
||||||
|
* In `parse_many`, when parsing streams of JSON documetns, we give to the users runtime control as to whether threads are used (via the parser.threaded attribute). https://github.com/simdjson/simdjson/issues/925
|
||||||
|
* Prefixed public macros to avoid name clashes with other libraries. https://github.com/simdjson/simdjson/issues/1035
|
||||||
|
* Better documentation regarding package managers (brew, MSYS2, conan, apt, vcpkg, FreeBSD package manager, etc.).
|
||||||
|
* Better documentation regarding CMake usage.
|
||||||
|
|
||||||
|
Standards
|
||||||
|
* We improved standard compliance with respect to both the JSON RFC 8259 and JSON Pointer RFC 6901. We added the at_pointer method to nodes for standard-compliant JSON Pointer queries. The legacy `at(std::string_view)` method remains but is deprecated since it is not standard-compliant as per RFC 6901.
|
||||||
|
* We removed computed GOTOs without sacrificing performance thus improving the C++ standard compliance (since computed GOTOs are compiler-specific extensions).
|
||||||
|
* Better support for C++20 https://github.com/simdjson/simdjson/pull/1050
|
||||||
|
|
||||||
|
# 0.4
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
|
||||||
|
- Test coverage has been greatly improved and we have resolved many static-analysis warnings on different systems.
|
||||||
|
- We added a fast (8GB/s) minifier that works directly on JSON strings.
|
||||||
|
- We added fast (10GB/s) UTF-8 validator that works directly on strings (any strings, including non-JSON).
|
||||||
|
- The array and object elements have a constant-time size() method.
|
||||||
|
- Performance improvements to the API (type(), get<>()).
|
||||||
|
- The parse_many function (ndjson) has been entirely reworked. It now uses a single secondary thread instead of several new threads.
|
||||||
|
- We have introduced a faster UTF-8 validation algorithm (lookup3) for all kernels (ARM, x64 SSE, x64 AVX).
|
||||||
|
- C++11 support for older compilers and systems.
|
||||||
|
- FreeBSD support (and tests).
|
||||||
|
- We support the clang front-end compiler (clangcl) under Visual Studio.
|
||||||
|
- It is now possible to target ARM platforms under Visual Studio.
|
||||||
|
- The simdjson library will never abort or print to standard output/error.
|
||||||
|
|
||||||
# 0.3
|
# 0.3
|
||||||
|
|
||||||
## Highlights
|
## Highlights
|
||||||
|
|||||||
-185
@@ -1,185 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
########################################################################
|
|
||||||
# Generates an "amalgamation build" for roaring. Inspired by similar
|
|
||||||
# script used by whefs.
|
|
||||||
########################################################################
|
|
||||||
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
|
|
||||||
|
|
||||||
echo "We are about to amalgamate all simdjson files into one source file. "
|
|
||||||
echo "See https://www.sqlite.org/amalgamation.html and https://en.wikipedia.org/wiki/Single_Compilation_Unit for rationale. "
|
|
||||||
|
|
||||||
AMAL_H="simdjson.h"
|
|
||||||
AMAL_C="simdjson.cpp"
|
|
||||||
|
|
||||||
SRCPATH="$SCRIPTPATH/src"
|
|
||||||
INCLUDEPATH="$SCRIPTPATH/include"
|
|
||||||
|
|
||||||
# this list excludes the "src/generic headers"
|
|
||||||
ALLCFILES="
|
|
||||||
simdjson.cpp
|
|
||||||
"
|
|
||||||
|
|
||||||
# order matters
|
|
||||||
ALLCHEADERS="
|
|
||||||
simdjson.h
|
|
||||||
"
|
|
||||||
|
|
||||||
found_includes=()
|
|
||||||
|
|
||||||
for file in ${ALLCFILES}; do
|
|
||||||
test -e "$SRCPATH/$file" && continue
|
|
||||||
echo "FATAL: source file [$SRCPATH/$file] not found."
|
|
||||||
exit 127
|
|
||||||
done
|
|
||||||
|
|
||||||
for file in ${ALLCHEADERS}; do
|
|
||||||
test -e "$INCLUDEPATH/$file" && continue
|
|
||||||
echo "FATAL: source file [$INCLUDEPATH/$file] not found."
|
|
||||||
exit 127
|
|
||||||
done
|
|
||||||
|
|
||||||
function doinclude()
|
|
||||||
{
|
|
||||||
file=$1
|
|
||||||
line="${@:2}"
|
|
||||||
if [ -f $INCLUDEPATH/$file ]; then
|
|
||||||
if [[ ! " ${found_includes[@]} " =~ " ${file} " ]]; then
|
|
||||||
found_includes+=("$file")
|
|
||||||
dofile $INCLUDEPATH/$file
|
|
||||||
fi;
|
|
||||||
elif [ -f $SRCPATH/$file ]; then
|
|
||||||
# generic includes are included multiple times
|
|
||||||
if [[ "${file}" == *'generic/'*'.h' ]]; then
|
|
||||||
dofile $SRCPATH/$file
|
|
||||||
elif [[ ! " ${found_includes[@]} " =~ " ${file} " ]]; then
|
|
||||||
found_includes+=("$file")
|
|
||||||
dofile $SRCPATH/$file
|
|
||||||
else
|
|
||||||
echo "/* $file already included: $line */"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# If we don't recognize it, just emit the #include
|
|
||||||
echo "$line"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function dofile()
|
|
||||||
{
|
|
||||||
# Last lines are always ignored. Files should end by an empty lines.
|
|
||||||
RELFILE=${1#"$SCRIPTPATH/"}
|
|
||||||
echo "/* begin file $RELFILE */"
|
|
||||||
# echo "#line 8 \"$1\"" ## redefining the line/file is not nearly as useful as it sounds for debugging. It breaks IDEs.
|
|
||||||
while IFS= read -r line || [ -n "$line" ];
|
|
||||||
do
|
|
||||||
if [[ "${line}" == '#include "'*'"'* ]]; then
|
|
||||||
file=$(echo $line| cut -d'"' -f 2)
|
|
||||||
|
|
||||||
if [[ "${file}" == '../'* ]]; then
|
|
||||||
file=$(echo $file| cut -d'/' -f 2-)
|
|
||||||
fi;
|
|
||||||
|
|
||||||
# we explicitly include simdjson headers, one time each (unless they are generic, in which case multiple times is fine)
|
|
||||||
doinclude $file $line
|
|
||||||
else
|
|
||||||
# Otherwise we simply copy the line
|
|
||||||
echo "$line"
|
|
||||||
fi
|
|
||||||
done < "$1"
|
|
||||||
echo "/* end file $RELFILE */"
|
|
||||||
}
|
|
||||||
timestamp=$(date)
|
|
||||||
echo "Creating ${AMAL_H}..."
|
|
||||||
echo "/* auto-generated on ${timestamp}. Do not edit! */" > "${AMAL_H}"
|
|
||||||
{
|
|
||||||
for h in ${ALLCHEADERS}; do
|
|
||||||
doinclude $h "ERROR $h not found"
|
|
||||||
done
|
|
||||||
} >> "${AMAL_H}"
|
|
||||||
|
|
||||||
|
|
||||||
echo "Creating ${AMAL_C}..."
|
|
||||||
echo "/* auto-generated on ${timestamp}. Do not edit! */" > "${AMAL_C}"
|
|
||||||
{
|
|
||||||
echo "#include \"${AMAL_H}\""
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "/* used for http://dmalloc.com/ Dmalloc - Debug Malloc Library */"
|
|
||||||
echo "#ifdef DMALLOC"
|
|
||||||
echo "#include \"dmalloc.h\""
|
|
||||||
echo "#endif"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
for file in ${ALLCFILES}; do
|
|
||||||
dofile "$SRCPATH/$file"
|
|
||||||
done
|
|
||||||
} >> "${AMAL_C}"
|
|
||||||
|
|
||||||
|
|
||||||
DEMOCPP="amalgamation_demo.cpp"
|
|
||||||
echo "Creating ${DEMOCPP}..."
|
|
||||||
echo "/* auto-generated on ${timestamp}. Do not edit! */" > "${DEMOCPP}"
|
|
||||||
cat <<< '
|
|
||||||
#include <iostream>
|
|
||||||
#include "simdjson.h"
|
|
||||||
#include "simdjson.cpp"
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
if(argc < 2) {
|
|
||||||
std::cerr << "Please specify at least one file name. " << std::endl;
|
|
||||||
}
|
|
||||||
const char * filename = argv[1];
|
|
||||||
simdjson::dom::parser parser;
|
|
||||||
auto [doc, error] = parser.load(filename); // do the parsing
|
|
||||||
if (error) {
|
|
||||||
std::cout << "parse failed" << std::endl;
|
|
||||||
std::cout << "error code: " << error << std::endl;
|
|
||||||
std::cout << error << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout << "parse valid" << std::endl;
|
|
||||||
}
|
|
||||||
if(argc == 2) {
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse_many
|
|
||||||
const char * filename2 = argv[2];
|
|
||||||
for (auto result : parser.load_many(filename2)) {
|
|
||||||
error = result.error();
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
std::cout << "parse_many failed" << std::endl;
|
|
||||||
std::cout << "error code: " << error << std::endl;
|
|
||||||
std::cout << error << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout << "parse_many valid" << std::endl;
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
' >> "${DEMOCPP}"
|
|
||||||
|
|
||||||
echo "Done with all files generation. "
|
|
||||||
|
|
||||||
echo "Files have been written to directory: $PWD "
|
|
||||||
ls -la ${AMAL_C} ${AMAL_H} ${DEMOCPP}
|
|
||||||
|
|
||||||
echo "Giving final instructions:"
|
|
||||||
|
|
||||||
|
|
||||||
CPPBIN=${DEMOCPP%%.*}
|
|
||||||
|
|
||||||
echo "Try :"
|
|
||||||
echo "c++ -O3 -std=c++17 -pthread -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json ../jsonexamples/amazon_cellphones.ndjson"
|
|
||||||
|
|
||||||
SINGLEHDR=$SCRIPTPATH/singleheader
|
|
||||||
echo "Copying files to $SCRIPTPATH/singleheader "
|
|
||||||
mkdir -p $SINGLEHDR
|
|
||||||
echo "c++ -O3 -std=c++17 -pthread -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json ../jsonexamples/amazon_cellphones.ndjson" > $SINGLEHDR/README.md
|
|
||||||
cp ${AMAL_C} ${AMAL_H} ${DEMOCPP} $SINGLEHDR
|
|
||||||
ls $SINGLEHDR
|
|
||||||
|
|
||||||
cd $SINGLEHDR && c++ -O3 -std=c++17 -pthread -o ${CPPBIN} ${DEMOCPP} && ./${CPPBIN} ../jsonexamples/twitter.json ../jsonexamples/amazon_cellphones.ndjson
|
|
||||||
|
|
||||||
lowercase(){
|
|
||||||
echo "$1" | tr 'A-Z' 'a-z'
|
|
||||||
}
|
|
||||||
|
|
||||||
OS=`lowercase \`uname\``
|
|
||||||
+28
-18
@@ -1,21 +1,31 @@
|
|||||||
target_include_directories(${SIMDJSON_LIB_NAME}
|
add_subdirectory(dom)
|
||||||
INTERFACE
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/benchmark>
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/benchmark/linux>
|
|
||||||
)
|
|
||||||
|
|
||||||
add_cpp_benchmark(parse)
|
|
||||||
add_cpp_benchmark(statisticalmodel)
|
|
||||||
add_cpp_benchmark(parse_stream)
|
|
||||||
add_cpp_benchmark(get_corpus_benchmark)
|
|
||||||
add_executable(perfdiff perfdiff.cpp)
|
|
||||||
|
|
||||||
# Google Benchmarks
|
include_directories( . linux )
|
||||||
if (SIMDJSON_GOOGLE_BENCHMARKS)
|
link_libraries(simdjson-windows-headers test-data)
|
||||||
add_cpp_benchmark(bench_parse_call)
|
link_libraries(simdjson)
|
||||||
target_link_libraries(bench_parse_call benchmark::benchmark)
|
|
||||||
|
add_executable(benchfeatures benchfeatures.cpp)
|
||||||
|
add_executable(get_corpus_benchmark get_corpus_benchmark.cpp)
|
||||||
|
|
||||||
|
if (TARGET benchmark::benchmark)
|
||||||
|
link_libraries(benchmark::benchmark)
|
||||||
|
add_executable(bench_parse_call bench_parse_call.cpp)
|
||||||
|
add_executable(bench_dom_api bench_dom_api.cpp)
|
||||||
|
if(SIMDJSON_EXCEPTIONS)
|
||||||
|
add_executable(bench_ondemand bench_ondemand.cpp)
|
||||||
|
if(TARGET yyjson)
|
||||||
|
target_link_libraries(bench_ondemand PRIVATE yyjson)
|
||||||
|
endif()
|
||||||
|
if(TARGET rapidjson)
|
||||||
|
target_link_libraries(bench_ondemand PRIVATE rapidjson)
|
||||||
|
endif()
|
||||||
|
if(TARGET sajson)
|
||||||
|
target_link_libraries(bench_ondemand PRIVATE sajson)
|
||||||
|
endif()
|
||||||
|
if(TARGET nlohmann_json)
|
||||||
|
target_link_libraries(bench_ondemand PRIVATE nlohmann_json)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
add_cpp_benchmark(bench_dom_api)
|
|
||||||
target_link_libraries(bench_dom_api benchmark::benchmark)
|
|
||||||
target_compile_definitions(bench_dom_api PRIVATE JSON_TEST_PATH="${PROJECT_SOURCE_DIR}/jsonexamples/twitter.json")
|
|
||||||
endif()
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# From the ROOT, run:
|
|
||||||
# docker build -t simdjsonbench -f benchmark/Dockerfile . && docker run --privileged -t simdjsonbench
|
|
||||||
FROM gcc:8.3
|
|
||||||
|
|
||||||
# # Build latest
|
|
||||||
# ENV latest_release=v0.2.1
|
|
||||||
# WORKDIR /usr/src/$latest_release/
|
|
||||||
# RUN git clone --depth 1 https://github.com/lemire/simdjson/ -b $latest_release .
|
|
||||||
# RUN make parse
|
|
||||||
|
|
||||||
# # Build master
|
|
||||||
# WORKDIR /usr/src/master/
|
|
||||||
# RUN git clone --depth 1 https://github.com/lemire/simdjson/ .
|
|
||||||
# RUN make parse
|
|
||||||
|
|
||||||
# Build the current source
|
|
||||||
COPY . /usr/src/current/
|
|
||||||
WORKDIR /usr/src/current/
|
|
||||||
RUN make checkperf
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "json_benchmark/file_runner.h"
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace amazon_cellphones {
|
||||||
|
|
||||||
|
const bool UNTHREADED = false;
|
||||||
|
const bool THREADED = true;
|
||||||
|
|
||||||
|
using namespace json_benchmark;
|
||||||
|
|
||||||
|
struct brand {
|
||||||
|
double cumulative_rating;
|
||||||
|
uint64_t reviews_count;
|
||||||
|
simdjson_inline bool operator==(const brand &other) const {
|
||||||
|
return cumulative_rating == other.cumulative_rating &&
|
||||||
|
reviews_count == other.reviews_count;
|
||||||
|
}
|
||||||
|
simdjson_inline bool operator!=(const brand &other) const { return !(*this == other); }
|
||||||
|
};
|
||||||
|
|
||||||
|
simdjson_unused static std::ostream &operator<<(std::ostream &o, const brand &b) {
|
||||||
|
o << "cumulative_rating: " << b.cumulative_rating << std::endl;
|
||||||
|
o << "reviews_count: " << b.reviews_count << std::endl;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename StringType>
|
||||||
|
simdjson_unused static std::ostream &operator<<(std::ostream &o, const std::pair<const StringType, brand> &p) {
|
||||||
|
o << "brand: " << p.first << std::endl;
|
||||||
|
o << p.second;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename I>
|
||||||
|
struct runner : public file_runner<I> {
|
||||||
|
std::map<typename I::StringType, brand> result{};
|
||||||
|
|
||||||
|
bool setup(benchmark::State &state) {
|
||||||
|
return this->load_json(state, AMAZON_CELLPHONES_NDJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool before_run(benchmark::State &state) {
|
||||||
|
if (!file_runner<I>::before_run(state)) { return false; }
|
||||||
|
result.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(benchmark::State &) {
|
||||||
|
return this->implementation.run(this->json, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R>
|
||||||
|
bool diff(benchmark::State &state, runner<R> &reference) {
|
||||||
|
return diff_results(state, result, reference.result, diff_flags::NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t items_per_iteration() {
|
||||||
|
return result.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<bool threaded>
|
||||||
|
struct simdjson_dom;
|
||||||
|
|
||||||
|
template<typename I> simdjson_inline static void amazon_cellphones(benchmark::State &state) {
|
||||||
|
run_json_benchmark<runner<I>, runner<simdjson_dom<UNTHREADED>>>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace amazon_cellphones
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "amazon_cellphones.h"
|
||||||
|
|
||||||
|
namespace amazon_cellphones {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
template<bool threaded>
|
||||||
|
struct simdjson_dom {
|
||||||
|
using StringType = std::string;
|
||||||
|
|
||||||
|
dom::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::map<StringType, brand> &result) {
|
||||||
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
|
parser.threaded = threaded;
|
||||||
|
#endif
|
||||||
|
auto stream = parser.parse_many(json);
|
||||||
|
auto i = stream.begin();
|
||||||
|
++i; // Skip first line
|
||||||
|
for (;i != stream.end(); ++i) {
|
||||||
|
auto doc = *i;
|
||||||
|
StringType copy(std::string_view(doc.at(1)));
|
||||||
|
auto x = result.find(copy);
|
||||||
|
if (x == result.end()) { // If key not found, add new key
|
||||||
|
result.emplace(copy, amazon_cellphones::brand{
|
||||||
|
double(doc.at(5)) * uint64_t(doc.at(7)),
|
||||||
|
uint64_t(doc.at(7))
|
||||||
|
});
|
||||||
|
} else { // Otherwise, update key data
|
||||||
|
x->second.cumulative_rating += double(doc.at(5)) * uint64_t(doc.at(7));
|
||||||
|
x->second.reviews_count += uint64_t(doc.at(7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(amazon_cellphones, simdjson_dom<UNTHREADED>)->UseManualTime();
|
||||||
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
|
BENCHMARK_TEMPLATE(amazon_cellphones, simdjson_dom<THREADED>)->UseManualTime();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace amazon_cellphones
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "amazon_cellphones.h"
|
||||||
|
|
||||||
|
namespace amazon_cellphones {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
template<bool threaded>
|
||||||
|
struct simdjson_ondemand {
|
||||||
|
using StringType = std::string;
|
||||||
|
|
||||||
|
ondemand::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::map<StringType, brand> &result) {
|
||||||
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
|
parser.threaded = threaded;
|
||||||
|
#endif
|
||||||
|
ondemand::document_stream stream = parser.iterate_many(json);
|
||||||
|
ondemand::document_stream::iterator i = stream.begin();
|
||||||
|
++i; // Skip first line
|
||||||
|
for (;i != stream.end(); ++i) {
|
||||||
|
auto doc = *i;
|
||||||
|
size_t index{0};
|
||||||
|
StringType copy;
|
||||||
|
double rating;
|
||||||
|
uint64_t reviews;
|
||||||
|
for ( auto value : doc ) {
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
copy = StringType(std::string_view(value));
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
rating = double(value);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
reviews = uint64_t(value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto x = result.find(copy);
|
||||||
|
if (x == result.end()) { // If key not found, add new key
|
||||||
|
result.emplace(copy, amazon_cellphones::brand{
|
||||||
|
rating * reviews,
|
||||||
|
reviews
|
||||||
|
});
|
||||||
|
} else { // Otherwise, update key data
|
||||||
|
x->second.cumulative_rating += rating * reviews;
|
||||||
|
x->second.reviews_count += reviews;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(amazon_cellphones, simdjson_ondemand<UNTHREADED>)->UseManualTime();
|
||||||
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
|
BENCHMARK_TEMPLATE(amazon_cellphones, simdjson_ondemand<THREADED>)->UseManualTime();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace amazon_cellphones
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
+541
-63
@@ -1,4 +1,5 @@
|
|||||||
#include <benchmark/benchmark.h>
|
#include <benchmark/benchmark.h>
|
||||||
|
#include <iostream>
|
||||||
#include "simdjson.h"
|
#include "simdjson.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@@ -6,32 +7,480 @@ using namespace simdjson;
|
|||||||
using namespace benchmark;
|
using namespace benchmark;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#ifndef JSON_TEST_PATH
|
|
||||||
#define JSON_TEST_PATH "jsonexamples/twitter.json"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const padded_string EMPTY_ARRAY("[]", 2);
|
const padded_string EMPTY_ARRAY("[]", 2);
|
||||||
|
|
||||||
|
static const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
|
||||||
|
static const char *NUMBERS_JSON = SIMDJSON_BENCHMARK_DATA_DIR "numbers.json";
|
||||||
|
|
||||||
|
static void recover_one_string(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
const std::string_view data = "\"one string\"";
|
||||||
|
padded_string docdata{data};
|
||||||
|
// we do not want mem. alloc. in the loop.
|
||||||
|
auto error = parser.allocate(docdata.size());
|
||||||
|
if(error) {
|
||||||
|
cout << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dom::element doc;
|
||||||
|
if ((error = parser.parse(docdata).get(doc))) {
|
||||||
|
cerr << "could not parse string" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::string_view v;
|
||||||
|
error = doc.get(v);
|
||||||
|
if (error) {
|
||||||
|
cerr << "could not get string" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(recover_one_string);
|
||||||
|
|
||||||
|
|
||||||
|
static void serialize_twitter(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
padded_string docdata;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(docdata);
|
||||||
|
if(error) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we do not want mem. alloc. in the loop.
|
||||||
|
if((error = parser.allocate(docdata.size()))) {
|
||||||
|
cout << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dom::element doc;
|
||||||
|
if ((error = parser.parse(docdata).get(doc))) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::string serial = simdjson::minify(doc);
|
||||||
|
bytes += serial.size();
|
||||||
|
benchmark::DoNotOptimize(serial);
|
||||||
|
}
|
||||||
|
// we validate the result
|
||||||
|
{
|
||||||
|
auto serial = simdjson::minify(doc);
|
||||||
|
dom::element doc2; // we parse the minified output
|
||||||
|
if ((error = parser.parse(serial).get(doc2))) { throw std::runtime_error("serialization error"); }
|
||||||
|
auto serial2 = simdjson::minify(doc2); // we minify a second time
|
||||||
|
if(serial != serial2) { throw std::runtime_error("serialization mismatch"); }
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(serialize_twitter)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
|
||||||
|
static void serialize_big_string_to_string(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
std::vector<char> content;
|
||||||
|
content.push_back('\"');
|
||||||
|
for(size_t i = 0 ; i < 100000; i ++) {
|
||||||
|
content.push_back('0' + char(i%10)); // we add what looks like a long list of digits
|
||||||
|
}
|
||||||
|
content.push_back('\"');
|
||||||
|
dom::element doc;
|
||||||
|
simdjson::error_code error;
|
||||||
|
if ((error = parser.parse(content.data(), content.size()).get(doc))) {
|
||||||
|
cerr << "could not parse big string" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
auto serial = simdjson::to_string(doc);
|
||||||
|
bytes += serial.size();
|
||||||
|
benchmark::DoNotOptimize(serial);
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(serialize_big_string_to_string)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
|
||||||
|
static void serialize_twitter_to_string(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
padded_string docdata;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(docdata);
|
||||||
|
if(error) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we do not want mem. alloc. in the loop.
|
||||||
|
if((error = parser.allocate(docdata.size()))) {
|
||||||
|
cout << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dom::element doc;
|
||||||
|
if ((error = parser.parse(docdata).get(doc))) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
auto serial = simdjson::to_string(doc);
|
||||||
|
bytes += serial.size();
|
||||||
|
benchmark::DoNotOptimize(serial);
|
||||||
|
}
|
||||||
|
// we validate the result
|
||||||
|
{
|
||||||
|
auto serial = simdjson::to_string(doc);
|
||||||
|
dom::element doc2; // we parse the stringify output
|
||||||
|
if ((error = parser.parse(serial).get(doc2))) { throw std::runtime_error("serialization error"); }
|
||||||
|
auto serial2 = simdjson::to_string(doc2); // we stringify again
|
||||||
|
if(serial != serial2) { throw std::runtime_error("serialization mismatch"); }
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(serialize_twitter_to_string)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
static void serialize_twitter_string_builder(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
padded_string docdata;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(docdata);
|
||||||
|
if(error) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we do not want mem. alloc. in the loop.
|
||||||
|
if((error = parser.allocate(docdata.size()))) {
|
||||||
|
cout << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dom::element doc;
|
||||||
|
if ((error = parser.parse(docdata).get(doc))) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t bytes = 0;
|
||||||
|
simdjson::internal::string_builder<> sb;// not part of our public API, for internal use
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
sb.clear();
|
||||||
|
sb.append(doc);
|
||||||
|
std::string_view serial = sb.str();
|
||||||
|
bytes += serial.size();
|
||||||
|
benchmark::DoNotOptimize(serial);
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(serialize_twitter_string_builder)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
|
||||||
|
static void numbers_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr;
|
||||||
|
simdjson::error_code error;
|
||||||
|
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
|
||||||
|
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::vector<double> container;
|
||||||
|
for (auto e : arr) {
|
||||||
|
double x;
|
||||||
|
if ((error = e.get(x))) { cerr << "found a node that is not an number: " << error << endl; break;}
|
||||||
|
container.push_back(x);
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_scan);
|
||||||
|
|
||||||
|
static void numbers_size_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr;
|
||||||
|
simdjson::error_code error;
|
||||||
|
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
|
||||||
|
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::vector<double> container;
|
||||||
|
container.resize(arr.size());
|
||||||
|
size_t pos = 0;
|
||||||
|
for (auto e : arr) {
|
||||||
|
double x;
|
||||||
|
if ((error = e.get(x))) { cerr << "found a node that is not an number: " << error << endl; break;}
|
||||||
|
container[pos++] = x;
|
||||||
|
}
|
||||||
|
if(pos != container.size()) { cerr << "bad count" << endl; }
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_size_scan);
|
||||||
|
|
||||||
|
|
||||||
|
static void numbers_type_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr;
|
||||||
|
simdjson::error_code error;
|
||||||
|
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
|
||||||
|
cerr << "could not read " << NUMBERS_JSON << " as an array" << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::vector<double> container;
|
||||||
|
for (auto e : arr) {
|
||||||
|
dom::element_type actual_type = e.type();
|
||||||
|
if(actual_type != dom::element_type::DOUBLE) {
|
||||||
|
cerr << "found a node that is not an number?" << endl; break;
|
||||||
|
}
|
||||||
|
double x;
|
||||||
|
error = e.get(x);
|
||||||
|
container.push_back(x);
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_type_scan);
|
||||||
|
|
||||||
|
static void numbers_type_size_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr;
|
||||||
|
simdjson::error_code error;
|
||||||
|
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
|
||||||
|
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::vector<double> container;
|
||||||
|
container.resize(arr.size());
|
||||||
|
size_t pos = 0;
|
||||||
|
for (auto e : arr) {
|
||||||
|
dom::element_type actual_type = e.type();
|
||||||
|
if(actual_type != dom::element_type::DOUBLE) {
|
||||||
|
cerr << "found a node that is not an number?" << endl; break;
|
||||||
|
}
|
||||||
|
double x;
|
||||||
|
error = e.get(x);
|
||||||
|
container[pos++] = x;
|
||||||
|
}
|
||||||
|
if(pos != container.size()) { cerr << "bad count" << endl; }
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_type_size_scan);
|
||||||
|
|
||||||
|
static void numbers_load_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr;
|
||||||
|
simdjson::error_code error;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
// this may hit the disk, but probably just once
|
||||||
|
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
|
||||||
|
cerr << "could not read " << NUMBERS_JSON << " as an array: " << error << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::vector<double> container;
|
||||||
|
for (auto e : arr) {
|
||||||
|
double x;
|
||||||
|
if ((error = e.get(x))) { cerr << "found a node that is not an number: " << error << endl; break;}
|
||||||
|
container.push_back(x);
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_load_scan);
|
||||||
|
|
||||||
|
static void numbers_load_size_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr;
|
||||||
|
simdjson::error_code error;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
// this may hit the disk, but probably just once
|
||||||
|
if ((error = parser.load(NUMBERS_JSON).get(arr))) {
|
||||||
|
cerr << "could not read " << NUMBERS_JSON << " as an array" << endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::vector<double> container;
|
||||||
|
container.resize(arr.size());
|
||||||
|
size_t pos = 0;
|
||||||
|
for (auto e : arr) {
|
||||||
|
double x;
|
||||||
|
if ((error = e.get(x))) { cerr << "found a node that is not an number?" << endl; break;}
|
||||||
|
container[pos++] = x;
|
||||||
|
}
|
||||||
|
if(pos != container.size()) { cerr << "bad count" << endl; }
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_load_size_scan);
|
||||||
|
|
||||||
|
|
||||||
#if SIMDJSON_EXCEPTIONS
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
static void numbers_exceptions_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr = parser.load(NUMBERS_JSON);
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::vector<double> container;
|
||||||
|
for (double x : arr) {
|
||||||
|
container.push_back(x);
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_exceptions_scan);
|
||||||
|
|
||||||
|
static void numbers_exceptions_size_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr = parser.load(NUMBERS_JSON);
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::vector<double> container;
|
||||||
|
container.resize(arr.size());
|
||||||
|
size_t pos = 0;
|
||||||
|
for (auto e : arr) {
|
||||||
|
container[pos++] = double(e);
|
||||||
|
}
|
||||||
|
if(pos != container.size()) { cerr << "bad count" << endl; }
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_exceptions_size_scan);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void numbers_type_exceptions_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr = parser.load(NUMBERS_JSON);
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::vector<double> container;
|
||||||
|
for (auto e : arr) {
|
||||||
|
dom::element_type actual_type = e.type();
|
||||||
|
if(actual_type != dom::element_type::DOUBLE) {
|
||||||
|
cerr << "found a node that is not an number?" << endl; break;
|
||||||
|
}
|
||||||
|
container.push_back(double(e));
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_type_exceptions_scan);
|
||||||
|
|
||||||
|
static void numbers_type_exceptions_size_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
dom::array arr = parser.load(NUMBERS_JSON);
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
std::vector<double> container;
|
||||||
|
container.resize(arr.size());
|
||||||
|
size_t pos = 0;
|
||||||
|
for (auto e : arr) {
|
||||||
|
dom::element_type actual_type = e.type();
|
||||||
|
if(actual_type != dom::element_type::DOUBLE) {
|
||||||
|
cerr << "found a node that is not an number?" << endl; break;
|
||||||
|
}
|
||||||
|
container[pos++] = double(e);
|
||||||
|
}
|
||||||
|
if(pos != container.size()) { cerr << "bad count" << endl; }
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_type_exceptions_size_scan);
|
||||||
|
|
||||||
|
static void numbers_exceptions_load_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
// this may hit the disk, but probably just once
|
||||||
|
dom::array arr = parser.load(NUMBERS_JSON);
|
||||||
|
std::vector<double> container;
|
||||||
|
for (double x : arr) {
|
||||||
|
container.push_back(x);
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_exceptions_load_scan);
|
||||||
|
|
||||||
|
static void numbers_exceptions_load_size_scan(State& state) {
|
||||||
|
// Prints the number of results in twitter.json
|
||||||
|
dom::parser parser;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
// this may hit the disk, but probably just once
|
||||||
|
dom::array arr = parser.load(NUMBERS_JSON);
|
||||||
|
std::vector<double> container;
|
||||||
|
container.resize(arr.size());
|
||||||
|
size_t pos = 0;
|
||||||
|
for (double x : arr) {
|
||||||
|
container[pos++] = x;
|
||||||
|
}
|
||||||
|
if(pos != container.size()) { cerr << "bad count" << endl; }
|
||||||
|
benchmark::DoNotOptimize(container.data());
|
||||||
|
benchmark::ClobberMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BENCHMARK(numbers_exceptions_load_size_scan);
|
||||||
|
|
||||||
|
|
||||||
static void twitter_count(State& state) {
|
static void twitter_count(State& state) {
|
||||||
// Prints the number of results in twitter.json
|
// Prints the number of results in twitter.json
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
dom::element doc = parser.load(JSON_TEST_PATH);
|
dom::element doc = parser.load(TWITTER_JSON);
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
uint64_t result_count = doc["search_metadata"]["count"];
|
uint64_t result_count = doc["search_metadata"]["count"];
|
||||||
if (result_count != 100) { return; }
|
if (result_count != 100) { return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(twitter_count);
|
BENCHMARK(twitter_count);
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||||
static void iterator_twitter_count(State& state) {
|
static void iterator_twitter_count(State& state) {
|
||||||
// Prints the number of results in twitter.json
|
// Prints the number of results in twitter.json
|
||||||
padded_string json = padded_string::load(JSON_TEST_PATH);
|
padded_string json = padded_string::load(TWITTER_JSON);
|
||||||
ParsedJson pj = build_parsed_json(json);
|
ParsedJson pj = build_parsed_json(json);
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
ParsedJson::Iterator iter(pj);
|
ParsedJson::Iterator iter(pj);
|
||||||
// uint64_t result_count = doc["search_metadata"]["count"];
|
// uint64_t result_count = doc["search_metadata"]["count"];
|
||||||
if (!iter.move_to_key("search_metadata")) { return; }
|
if (!iter.move_to_key("search_metadata")) { return; }
|
||||||
@@ -44,17 +493,18 @@ static void iterator_twitter_count(State& state) {
|
|||||||
}
|
}
|
||||||
BENCHMARK(iterator_twitter_count);
|
BENCHMARK(iterator_twitter_count);
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
static void twitter_default_profile(State& state) {
|
static void twitter_default_profile(State& state) {
|
||||||
// Count unique users with a default profile.
|
// Count unique users with a default profile.
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
dom::element doc = parser.load(JSON_TEST_PATH);
|
dom::element doc = parser.load(TWITTER_JSON);
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
set<string_view> default_users;
|
set<string_view> default_users;
|
||||||
for (dom::object tweet : doc["statuses"].get<dom::array>()) {
|
for (dom::object tweet : doc["statuses"]) {
|
||||||
dom::object user = tweet["user"];
|
dom::object user = tweet["user"];
|
||||||
if (user["default_profile"]) {
|
if (user["default_profile"]) {
|
||||||
default_users.insert(user["screen_name"]);
|
default_users.emplace(user["screen_name"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (default_users.size() != 86) { return; }
|
if (default_users.size() != 86) { return; }
|
||||||
@@ -65,15 +515,16 @@ BENCHMARK(twitter_default_profile);
|
|||||||
static void twitter_image_sizes(State& state) {
|
static void twitter_image_sizes(State& state) {
|
||||||
// Count unique image sizes
|
// Count unique image sizes
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
dom::element doc = parser.load(JSON_TEST_PATH);
|
dom::element doc = parser.load(TWITTER_JSON);
|
||||||
for (auto _ : state) {
|
simdjson::error_code error;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
set<tuple<uint64_t, uint64_t>> image_sizes;
|
set<tuple<uint64_t, uint64_t>> image_sizes;
|
||||||
for (dom::object tweet : doc["statuses"].get<dom::array>()) {
|
for (dom::object tweet : doc["statuses"]) {
|
||||||
auto [media, not_found] = tweet["entities"]["media"];
|
dom::array media;
|
||||||
if (!not_found) {
|
if (not (error = tweet["entities"]["media"].get(media))) {
|
||||||
for (dom::object image : media.get<dom::array>()) {
|
for (dom::object image : media) {
|
||||||
for (auto [key, size] : image["sizes"].get<dom::object>()) {
|
for (auto size : image["sizes"].get_object()) {
|
||||||
image_sizes.insert({ size["w"], size["h"] });
|
image_sizes.emplace(size.value["w"], size.value["h"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,10 +539,12 @@ BENCHMARK(twitter_image_sizes);
|
|||||||
static void error_code_twitter_count(State& state) noexcept {
|
static void error_code_twitter_count(State& state) noexcept {
|
||||||
// Prints the number of results in twitter.json
|
// Prints the number of results in twitter.json
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
dom::element doc = parser.load(JSON_TEST_PATH);
|
simdjson::error_code error;
|
||||||
for (auto _ : state) {
|
dom::element doc;
|
||||||
auto [value, error] = doc["search_metadata"]["count"].get<uint64_t>();
|
if ((error = parser.load(TWITTER_JSON).get(doc))) { return; }
|
||||||
if (error) { return; }
|
for (simdjson_unused auto _ : state) {
|
||||||
|
uint64_t value;
|
||||||
|
if ((error = doc["search_metadata"]["count"].get(value))) { return; }
|
||||||
if (value != 100) { return; }
|
if (value != 100) { return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,20 +553,22 @@ BENCHMARK(error_code_twitter_count);
|
|||||||
static void error_code_twitter_default_profile(State& state) noexcept {
|
static void error_code_twitter_default_profile(State& state) noexcept {
|
||||||
// Count unique users with a default profile.
|
// Count unique users with a default profile.
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
dom::element doc = parser.load(JSON_TEST_PATH);
|
simdjson::error_code error;
|
||||||
for (auto _ : state) {
|
dom::element doc;
|
||||||
|
if ((error = parser.load(TWITTER_JSON).get(doc))) { std::cerr << error << std::endl; return; }
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
set<string_view> default_users;
|
set<string_view> default_users;
|
||||||
|
|
||||||
auto [tweets, error] = doc["statuses"].get<dom::array>();
|
dom::array tweets;
|
||||||
if (error) { return; }
|
if ((error = doc["statuses"].get(tweets))) { return; }
|
||||||
for (dom::element tweet : tweets) {
|
for (dom::element tweet : tweets) {
|
||||||
auto [user, error2] = tweet["user"].get<dom::object>();
|
dom::object user;
|
||||||
if (error2) { return; }
|
if ((error = tweet["user"].get(user))) { return; }
|
||||||
auto [default_profile, error3] = user["default_profile"].get<bool>();
|
bool default_profile;
|
||||||
if (error3) { return; }
|
if ((error = user["default_profile"].get(default_profile))) { return; }
|
||||||
if (default_profile) {
|
if (default_profile) {
|
||||||
auto [screen_name, error4] = user["screen_name"].get<std::string_view>();
|
std::string_view screen_name;
|
||||||
if (error4) { return; }
|
if ((error = user["screen_name"].get(screen_name))) { return; }
|
||||||
default_users.insert(screen_name);
|
default_users.insert(screen_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,17 +578,22 @@ static void error_code_twitter_default_profile(State& state) noexcept {
|
|||||||
}
|
}
|
||||||
BENCHMARK(error_code_twitter_default_profile);
|
BENCHMARK(error_code_twitter_default_profile);
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||||
|
|
||||||
static void iterator_twitter_default_profile(State& state) {
|
static void iterator_twitter_default_profile(State& state) {
|
||||||
// Count unique users with a default profile.
|
// Count unique users with a default profile.
|
||||||
padded_string json = padded_string::load(JSON_TEST_PATH);
|
padded_string json;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(json);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
ParsedJson pj = build_parsed_json(json);
|
ParsedJson pj = build_parsed_json(json);
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
set<string_view> default_users;
|
set<string_view> default_users;
|
||||||
ParsedJson::Iterator iter(pj);
|
ParsedJson::Iterator iter(pj);
|
||||||
|
|
||||||
// for (dom::object tweet : doc["statuses"].get<dom::array>()) {
|
// for (dom::object tweet : doc["statuses"]) {
|
||||||
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
|
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
|
||||||
if (iter.down()) { // first status
|
if (iter.down()) { // first status
|
||||||
do {
|
do {
|
||||||
@@ -148,7 +608,7 @@ static void iterator_twitter_default_profile(State& state) {
|
|||||||
|
|
||||||
// default_users.insert(user["screen_name"]);
|
// default_users.insert(user["screen_name"]);
|
||||||
if (!(iter.move_to_key("screen_name") && iter.is_string())) { return; }
|
if (!(iter.move_to_key("screen_name") && iter.is_string())) { return; }
|
||||||
default_users.insert(string_view(iter.get_string(), iter.get_string_length()));
|
default_users.emplace(iter.get_string(), iter.get_string_length());
|
||||||
}
|
}
|
||||||
if (!iter.up()) { return; } // back to user
|
if (!iter.up()) { return; } // back to user
|
||||||
}
|
}
|
||||||
@@ -161,28 +621,32 @@ static void iterator_twitter_default_profile(State& state) {
|
|||||||
if (default_users.size() != 86) { return; }
|
if (default_users.size() != 86) { return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
BENCHMARK(iterator_twitter_default_profile);
|
BENCHMARK(iterator_twitter_default_profile);
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
static void error_code_twitter_image_sizes(State& state) noexcept {
|
static void error_code_twitter_image_sizes(State& state) noexcept {
|
||||||
// Count unique image sizes
|
// Count unique image sizes
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
dom::element doc = parser.load(JSON_TEST_PATH);
|
simdjson::error_code error;
|
||||||
for (auto _ : state) {
|
dom::element doc;
|
||||||
|
if ((error = parser.load(TWITTER_JSON).get(doc))) { std::cerr << error << std::endl; return; }
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
set<tuple<uint64_t, uint64_t>> image_sizes;
|
set<tuple<uint64_t, uint64_t>> image_sizes;
|
||||||
auto [statuses, error] = doc["statuses"].get<dom::array>();
|
dom::array statuses;
|
||||||
if (error) { return; }
|
if ((error = doc["statuses"].get(statuses))) { return; }
|
||||||
for (dom::element tweet : statuses) {
|
for (dom::element tweet : statuses) {
|
||||||
auto [images, not_found] = tweet["entities"]["media"].get<dom::array>();
|
dom::array images;
|
||||||
if (!not_found) {
|
if (not (error = tweet["entities"]["media"].get(images))) {
|
||||||
for (dom::element image : images) {
|
for (dom::element image : images) {
|
||||||
auto [sizes, error2] = image["sizes"].get<dom::object>();
|
dom::object sizes;
|
||||||
if (error2) { return; }
|
if ((error = image["sizes"].get(sizes))) { return; }
|
||||||
for (auto [key, size] : sizes) {
|
for (auto size : sizes) {
|
||||||
auto [width, error3] = size["w"].get<uint64_t>();
|
uint64_t width, height;
|
||||||
auto [height, error4] = size["h"].get<uint64_t>();
|
if ((error = size.value["w"].get(width))) { return; }
|
||||||
if (error3 || error4) { return; }
|
if ((error = size.value["h"].get(height))) { return; }
|
||||||
image_sizes.insert({ width, height });
|
image_sizes.emplace(width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,33 +656,38 @@ static void error_code_twitter_image_sizes(State& state) noexcept {
|
|||||||
}
|
}
|
||||||
BENCHMARK(error_code_twitter_image_sizes);
|
BENCHMARK(error_code_twitter_image_sizes);
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||||
static void iterator_twitter_image_sizes(State& state) {
|
static void iterator_twitter_image_sizes(State& state) {
|
||||||
// Count unique image sizes
|
// Count unique image sizes
|
||||||
padded_string json = padded_string::load(JSON_TEST_PATH);
|
padded_string json;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(json);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
ParsedJson pj = build_parsed_json(json);
|
ParsedJson pj = build_parsed_json(json);
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
set<tuple<uint64_t, uint64_t>> image_sizes;
|
set<tuple<uint64_t, uint64_t>> image_sizes;
|
||||||
ParsedJson::Iterator iter(pj);
|
ParsedJson::Iterator iter(pj);
|
||||||
|
|
||||||
// for (dom::object tweet : doc["statuses"].get<dom::array>()) {
|
// for (dom::object tweet : doc["statuses"]) {
|
||||||
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
|
if (!(iter.move_to_key("statuses") && iter.is_array())) { return; }
|
||||||
if (iter.down()) { // first status
|
if (iter.down()) { // first status
|
||||||
do {
|
do {
|
||||||
|
|
||||||
// auto [media, not_found] = tweet["entities"]["media"];
|
// dom::object media;
|
||||||
|
// not_found = tweet["entities"]["media"].get(media);
|
||||||
// if (!not_found) {
|
// if (!not_found) {
|
||||||
if (iter.move_to_key("entities")) {
|
if (iter.move_to_key("entities")) {
|
||||||
if (!iter.is_object()) { return; }
|
if (!iter.is_object()) { return; }
|
||||||
if (iter.move_to_key("media")) {
|
if (iter.move_to_key("media")) {
|
||||||
if (!iter.is_array()) { return; }
|
if (!iter.is_array()) { return; }
|
||||||
|
|
||||||
// for (dom::object image : media.get<dom::array>()) {
|
// for (dom::object image : media) {
|
||||||
if (iter.down()) { // first media
|
if (iter.down()) { // first media
|
||||||
do {
|
do {
|
||||||
|
|
||||||
// for (auto [key, size] : image["sizes"].get<dom::object>()) {
|
// for (auto [key, size] : dom::object(image["sizes"])) {
|
||||||
if (!(iter.move_to_key("sizes") && iter.is_object())) { return; }
|
if (!(iter.move_to_key("sizes") && iter.is_object())) { return; }
|
||||||
if (iter.down()) { // first size
|
if (iter.down()) { // first size
|
||||||
do {
|
do {
|
||||||
@@ -231,7 +700,7 @@ static void iterator_twitter_image_sizes(State& state) {
|
|||||||
if (!(iter.move_to_key("h")) && !iter.is_integer()) { return; }
|
if (!(iter.move_to_key("h")) && !iter.is_integer()) { return; }
|
||||||
uint64_t height = iter.get_integer();
|
uint64_t height = iter.get_integer();
|
||||||
if (!iter.up()) { return; } // back to size
|
if (!iter.up()) { return; } // back to size
|
||||||
image_sizes.insert({ width, height });
|
image_sizes.emplace(width, height);
|
||||||
|
|
||||||
} while (iter.next()); // next size
|
} while (iter.next()); // next size
|
||||||
if (!iter.up()) { return; } // back to sizes
|
if (!iter.up()) { return; } // back to sizes
|
||||||
@@ -243,7 +712,7 @@ static void iterator_twitter_image_sizes(State& state) {
|
|||||||
if (!iter.up()) { return; } // back to entities
|
if (!iter.up()) { return; } // back to entities
|
||||||
}
|
}
|
||||||
if (!iter.up()) { return; } // back to status
|
if (!iter.up()) { return; } // back to status
|
||||||
}
|
}
|
||||||
} while (iter.next()); // next status
|
} while (iter.next()); // next status
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,17 +721,26 @@ static void iterator_twitter_image_sizes(State& state) {
|
|||||||
}
|
}
|
||||||
BENCHMARK(iterator_twitter_image_sizes);
|
BENCHMARK(iterator_twitter_image_sizes);
|
||||||
|
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
static void print_json(State& state) noexcept {
|
static void print_json(State& state) noexcept {
|
||||||
// Prints the number of results in twitter.json
|
// Prints the number of results in twitter.json
|
||||||
padded_string json = get_corpus(JSON_TEST_PATH);
|
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
if (int error = json_parse(json, parser); error != SUCCESS) { cerr << error_message(error) << endl; return; }
|
|
||||||
for (auto _ : state) {
|
padded_string json;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(json);
|
||||||
|
if (error) { std::cerr << error << std::endl; return; }
|
||||||
|
|
||||||
|
int code = json_parse(json, parser);
|
||||||
|
if (code) { cerr << error_message(code) << endl; return; }
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
if (!parser.print_json(s)) { cerr << "print_json failed" << endl; return; }
|
if (!parser.print_json(s)) { cerr << "print_json failed" << endl; return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(print_json);
|
BENCHMARK(print_json);
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
BENCHMARK_MAIN();
|
BENCHMARK_MAIN();
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#include "simdjson.h"
|
||||||
|
|
||||||
|
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
#include "yyjson.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
#include "rapidjson/document.h"
|
||||||
|
#include "rapidjson/reader.h"
|
||||||
|
#include "rapidjson/stringbuffer.h"
|
||||||
|
#include "rapidjson/writer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_SAJSON
|
||||||
|
#include "sajson.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This has to be last, for reasons I don't yet understand
|
||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
|
||||||
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
#include "json2msgpack/simdjson_ondemand.h"
|
||||||
|
#include "json2msgpack/simdjson_dom.h"
|
||||||
|
#include "json2msgpack/yyjson.h"
|
||||||
|
#include "json2msgpack/rapidjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "json2msgpack/sajson.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "json2msgpack/nlohmann_json.h"
|
||||||
|
|
||||||
|
#include "partial_tweets/simdjson_ondemand.h"
|
||||||
|
#include "partial_tweets/simdjson_dom.h"
|
||||||
|
#include "partial_tweets/yyjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "partial_tweets/sajson.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "partial_tweets/rapidjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "partial_tweets/rapidjson_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "partial_tweets/nlohmann_json.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "partial_tweets/nlohmann_json_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
|
||||||
|
|
||||||
|
#include "distinct_user_id/simdjson_ondemand.h"
|
||||||
|
#include "distinct_user_id/simdjson_ondemand_json_pointer.h"
|
||||||
|
#include "distinct_user_id/simdjson_dom.h"
|
||||||
|
#include "distinct_user_id/simdjson_dom_json_pointer.h"
|
||||||
|
#include "distinct_user_id/yyjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "distinct_user_id/sajson.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "distinct_user_id/rapidjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "distinct_user_id/rapidjson_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "distinct_user_id/nlohmann_json.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "distinct_user_id/nlohmann_json_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
|
||||||
|
#include "find_tweet/simdjson_ondemand.h"
|
||||||
|
#include "find_tweet/simdjson_dom.h"
|
||||||
|
#include "find_tweet/yyjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "find_tweet/sajson.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "find_tweet/rapidjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "find_tweet/rapidjson_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "find_tweet/nlohmann_json.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "find_tweet/nlohmann_json_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
|
||||||
|
#include "top_tweet/simdjson_ondemand.h"
|
||||||
|
#include "top_tweet/simdjson_dom.h"
|
||||||
|
#include "top_tweet/yyjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "top_tweet/sajson.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "top_tweet/rapidjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "top_tweet/rapidjson_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "top_tweet/nlohmann_json.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "top_tweet/nlohmann_json_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
|
||||||
|
|
||||||
|
#include "kostya/simdjson_ondemand.h"
|
||||||
|
#include "kostya/simdjson_dom.h"
|
||||||
|
#include "kostya/yyjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "kostya/sajson.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "kostya/rapidjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "kostya/rapidjson_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "kostya/nlohmann_json.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "kostya/nlohmann_json_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
|
||||||
|
#include "large_random/simdjson_ondemand.h"
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
|
||||||
|
#include "large_random/simdjson_ondemand_unordered.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_UNORDERED
|
||||||
|
#include "large_random/simdjson_dom.h"
|
||||||
|
#include "large_random/yyjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "large_random/sajson.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_SAJSON
|
||||||
|
#include "large_random/rapidjson.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "large_random/rapidjson_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "large_random/nlohmann_json.h"
|
||||||
|
#if SIMDJSON_COMPETITION_SAX
|
||||||
|
#include "large_random/nlohmann_json_sax.h"
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAX
|
||||||
|
|
||||||
|
#include "amazon_cellphones/simdjson_dom.h"
|
||||||
|
#include "amazon_cellphones/simdjson_ondemand.h"
|
||||||
|
|
||||||
|
#include "large_amazon_cellphones/simdjson_dom.h"
|
||||||
|
#include "large_amazon_cellphones/simdjson_ondemand.h"
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
+194
-11
@@ -1,73 +1,256 @@
|
|||||||
#include <benchmark/benchmark.h>
|
#include <benchmark/benchmark.h>
|
||||||
|
#include <iostream>
|
||||||
#include "simdjson.h"
|
#include "simdjson.h"
|
||||||
using namespace simdjson;
|
using namespace simdjson;
|
||||||
using namespace benchmark;
|
using namespace benchmark;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
const padded_string EMPTY_ARRAY("[]", 2);
|
const padded_string EMPTY_ARRAY("[]", 2);
|
||||||
|
const char *TWITTER_JSON = SIMDJSON_BENCHMARK_DATA_DIR "twitter.json";
|
||||||
|
const char *GSOC_JSON = SIMDJSON_BENCHMARK_DATA_DIR "gsoc-2018.json";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void fast_minify_twitter(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
padded_string docdata;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(docdata);
|
||||||
|
if(error) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::unique_ptr<char[]> buffer{new char[docdata.size()]};
|
||||||
|
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
size_t new_length{}; // It will receive the minified length.
|
||||||
|
auto error = simdjson::minify(docdata.data(), docdata.size(), buffer.get(), new_length);
|
||||||
|
bytes += docdata.size();
|
||||||
|
benchmark::DoNotOptimize(error);
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(fast_minify_twitter)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void fast_minify_gsoc(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
padded_string docdata;
|
||||||
|
auto error = padded_string::load(GSOC_JSON).get(docdata);
|
||||||
|
if(error) {
|
||||||
|
cerr << "could not parse gsoc-2018.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::unique_ptr<char[]> buffer{new char[docdata.size()]};
|
||||||
|
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
size_t new_length{}; // It will receive the minified length.
|
||||||
|
auto error = simdjson::minify(docdata.data(), docdata.size(), buffer.get(), new_length);
|
||||||
|
bytes += docdata.size();
|
||||||
|
benchmark::DoNotOptimize(error);
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(fast_minify_gsoc)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
static void unicode_validate_twitter(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
padded_string docdata;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(docdata);
|
||||||
|
if(error) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we do not want mem. alloc. in the loop.
|
||||||
|
error = parser.allocate(docdata.size());
|
||||||
|
if(error) {
|
||||||
|
cout << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
bool is_ok = simdjson::validate_utf8(docdata.data(), docdata.size());
|
||||||
|
bytes += docdata.size();
|
||||||
|
benchmark::DoNotOptimize(is_ok);
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(unicode_validate_twitter)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
static void parse_twitter(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
padded_string docdata;
|
||||||
|
auto error = padded_string::load(TWITTER_JSON).get(docdata);
|
||||||
|
if(error) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we do not want mem. alloc. in the loop.
|
||||||
|
error = parser.allocate(docdata.size());
|
||||||
|
if(error) {
|
||||||
|
cout << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
dom::element doc;
|
||||||
|
bytes += docdata.size();
|
||||||
|
if ((error = parser.parse(docdata).get(doc))) {
|
||||||
|
cerr << "could not parse twitter.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(doc);
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(parse_twitter)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
|
||||||
|
static void parse_gsoc(State& state) {
|
||||||
|
dom::parser parser;
|
||||||
|
padded_string docdata;
|
||||||
|
auto error = padded_string::load(GSOC_JSON).get(docdata);
|
||||||
|
if(error) {
|
||||||
|
cerr << "could not parse gsoc-2018.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we do not want mem. alloc. in the loop.
|
||||||
|
error = parser.allocate(docdata.size());
|
||||||
|
if(error) {
|
||||||
|
cout << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t bytes = 0;
|
||||||
|
for (simdjson_unused auto _ : state) {
|
||||||
|
bytes += docdata.size();
|
||||||
|
dom::element doc;
|
||||||
|
if ((error = parser.parse(docdata).get(doc))) {
|
||||||
|
cerr << "could not parse gsoc-2018.json" << error << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
benchmark::DoNotOptimize(doc);
|
||||||
|
}
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
state.counters["Gigabytes"] = benchmark::Counter(
|
||||||
|
double(bytes), benchmark::Counter::kIsRate,
|
||||||
|
benchmark::Counter::OneK::kIs1000); // For GiB : kIs1024
|
||||||
|
state.counters["docs"] = Counter(double(state.iterations()), benchmark::Counter::kIsRate);
|
||||||
|
}
|
||||||
|
BENCHMARK(parse_gsoc)->Repetitions(10)->ComputeStatistics("max", [](const std::vector<double>& v) -> double {
|
||||||
|
return *(std::max_element(std::begin(v), std::end(v)));
|
||||||
|
})->DisplayAggregatesOnly(true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||||
static void json_parse(State& state) {
|
static void json_parse(State& state) {
|
||||||
ParsedJson pj;
|
ParsedJson pj;
|
||||||
if (!pj.allocate_capacity(EMPTY_ARRAY.length())) { return; }
|
if (!pj.allocate_capacity(EMPTY_ARRAY.length())) { return; }
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
auto error = json_parse(EMPTY_ARRAY, pj);
|
auto error = json_parse(EMPTY_ARRAY, pj);
|
||||||
if (error) { return; }
|
if (error) { return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
BENCHMARK(json_parse);
|
BENCHMARK(json_parse);
|
||||||
|
#endif // SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
|
|
||||||
static void parser_parse_error_code(State& state) {
|
static void parser_parse_error_code(State& state) {
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
|
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
auto [doc, error] = parser.parse(EMPTY_ARRAY);
|
auto error = parser.parse(EMPTY_ARRAY).error();
|
||||||
if (error) { return; }
|
if (error) { return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(parser_parse_error_code);
|
BENCHMARK(parser_parse_error_code);
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
static void parser_parse_exception(State& state) {
|
static void parser_parse_exception(State& state) {
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
|
if (parser.allocate(EMPTY_ARRAY.length())) { return; }
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
try {
|
try {
|
||||||
UNUSED dom::element doc = parser.parse(EMPTY_ARRAY);
|
simdjson_unused dom::element doc = parser.parse(EMPTY_ARRAY);
|
||||||
} catch(simdjson_error &j) {
|
} catch(simdjson_error &j) {
|
||||||
|
cout << j.what() << endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(parser_parse_exception);
|
BENCHMARK(parser_parse_exception);
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_DISABLE_DEPRECATED_API
|
||||||
SIMDJSON_PUSH_DISABLE_WARNINGS
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
SIMDJSON_DISABLE_DEPRECATED_WARNING
|
||||||
static void build_parsed_json(State& state) {
|
static void build_parsed_json(State& state) {
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
dom::parser parser = simdjson::build_parsed_json(EMPTY_ARRAY);
|
dom::parser parser = simdjson::build_parsed_json(EMPTY_ARRAY);
|
||||||
if (!parser.valid) { return; }
|
if (!parser.valid) { return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
BENCHMARK(build_parsed_json);
|
BENCHMARK(build_parsed_json);
|
||||||
|
#endif
|
||||||
|
|
||||||
static void document_parse_error_code(State& state) {
|
static void document_parse_error_code(State& state) {
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
auto [doc, error] = parser.parse(EMPTY_ARRAY);
|
auto error = parser.parse(EMPTY_ARRAY).error();
|
||||||
if (error) { return; }
|
if (error) { return; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(document_parse_error_code);
|
BENCHMARK(document_parse_error_code);
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
static void document_parse_exception(State& state) {
|
static void document_parse_exception(State& state) {
|
||||||
for (auto _ : state) {
|
for (simdjson_unused auto _ : state) {
|
||||||
try {
|
try {
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
UNUSED dom::element doc = parser.parse(EMPTY_ARRAY);
|
simdjson_unused dom::element doc = parser.parse(EMPTY_ARRAY);
|
||||||
} catch(simdjson_error &j) {
|
} catch(simdjson_error &j) {
|
||||||
|
cout << j.what() << endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BENCHMARK(document_parse_exception);
|
BENCHMARK(document_parse_exception);
|
||||||
|
|
||||||
BENCHMARK_MAIN();
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
|
|||||||
+120
-92
@@ -4,8 +4,8 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
#include <unistd.h>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
||||||
@@ -63,8 +63,8 @@ void print_usage(ostream& out) {
|
|||||||
out << "-s STAGE - Stop after the given stage." << endl;
|
out << "-s STAGE - Stop after the given stage." << endl;
|
||||||
out << " -s stage1 - Stop after find_structural_bits." << endl;
|
out << " -s stage1 - Stop after find_structural_bits." << endl;
|
||||||
out << " -s all - Run all stages." << endl;
|
out << " -s all - Run all stages." << endl;
|
||||||
out << "-a ARCH - Use the parser with the designated architecture (HASWELL, WESTMERE" << endl;
|
out << "-a ARCH - Use the parser with the designated architecture (HASWELL, WESTMERE," << endl;
|
||||||
out << " or ARM64). By default, detects best supported architecture." << endl;
|
out << " PPC64 or ARM64). By default, detects best supported architecture." << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void exit_usage(string message) {
|
void exit_usage(string message) {
|
||||||
@@ -75,7 +75,6 @@ void exit_usage(string message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct option_struct {
|
struct option_struct {
|
||||||
architecture arch = architecture::UNSUPPORTED;
|
|
||||||
bool stage1_only = false;
|
bool stage1_only = false;
|
||||||
|
|
||||||
int32_t iterations = 400;
|
int32_t iterations = 400;
|
||||||
@@ -84,48 +83,41 @@ struct option_struct {
|
|||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
|
|
||||||
option_struct(int argc, char **argv) {
|
option_struct(int argc, char **argv) {
|
||||||
#ifndef _MSC_VER
|
int c;
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "vtn:i:a:s:")) != -1) {
|
while ((c = getopt(argc, argv, "vtn:i:a:s:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'n':
|
case 'n':
|
||||||
iterations = atoi(optarg);
|
iterations = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
iteration_step = atoi(optarg);
|
iteration_step = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a': {
|
||||||
arch = parse_architecture(optarg);
|
auto impl = simdjson::get_available_implementations()[optarg];
|
||||||
if (arch == architecture::UNSUPPORTED) {
|
if(impl && impl->supported_by_runtime_system()) {
|
||||||
exit_usage(string("Unsupported option value -a ") + optarg + ": expected -a HASWELL, WESTMERE or ARM64");
|
simdjson::get_active_implementation() = impl;
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
if (!strcmp(optarg, "stage1")) {
|
|
||||||
stage1_only = true;
|
|
||||||
} else if (!strcmp(optarg, "all")) {
|
|
||||||
stage1_only = false;
|
|
||||||
} else {
|
} else {
|
||||||
exit_usage(string("Unsupported option value -s ") + optarg + ": expected -s stage1 or all");
|
std::cerr << "implementation " << optarg << " not found or not supported " << std::endl;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
exit_error("Unexpected argument " + c);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (!strcmp(optarg, "stage1")) {
|
||||||
|
stage1_only = true;
|
||||||
|
} else if (!strcmp(optarg, "all")) {
|
||||||
|
stage1_only = false;
|
||||||
|
} else {
|
||||||
|
exit_usage(string("Unsupported option value -s ") + optarg + ": expected -s stage1 or all");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
exit_error(string("Unexpected argument ") + std::string(1,static_cast<char>(c)));
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
int optind = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// If architecture is not specified, pick the best supported architecture by default
|
|
||||||
if (arch == architecture::UNSUPPORTED) {
|
|
||||||
arch = find_best_supported_architecture();
|
|
||||||
}
|
}
|
||||||
dom::parser::use_implementation(arch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
@@ -154,25 +146,25 @@ struct feature_benchmarker {
|
|||||||
benchmarker struct23;
|
benchmarker struct23;
|
||||||
benchmarker struct23_miss;
|
benchmarker struct23_miss;
|
||||||
|
|
||||||
feature_benchmarker(const simdjson::implementation &parser, event_collector& collector) :
|
feature_benchmarker(event_collector& collector) :
|
||||||
utf8 ("jsonexamples/generated/utf-8.json", parser, collector),
|
utf8 (SIMDJSON_BENCHMARK_DATA_DIR "generated/utf-8.json", collector),
|
||||||
utf8_miss ("jsonexamples/generated/utf-8-miss.json", parser, collector),
|
utf8_miss (SIMDJSON_BENCHMARK_DATA_DIR "generated/utf-8-miss.json", collector),
|
||||||
escape ("jsonexamples/generated/escape.json", parser, collector),
|
escape (SIMDJSON_BENCHMARK_DATA_DIR "generated/escape.json", collector),
|
||||||
escape_miss ("jsonexamples/generated/escape-miss.json", parser, collector),
|
escape_miss (SIMDJSON_BENCHMARK_DATA_DIR "generated/escape-miss.json", collector),
|
||||||
empty ("jsonexamples/generated/0-structurals.json", parser, collector),
|
empty (SIMDJSON_BENCHMARK_DATA_DIR "generated/0-structurals.json", collector),
|
||||||
empty_miss ("jsonexamples/generated/0-structurals-miss.json", parser, collector),
|
empty_miss (SIMDJSON_BENCHMARK_DATA_DIR "generated/0-structurals-miss.json", collector),
|
||||||
struct7 ("jsonexamples/generated/7-structurals.json", parser, collector),
|
struct7 (SIMDJSON_BENCHMARK_DATA_DIR "generated/7-structurals.json", collector),
|
||||||
struct7_miss ("jsonexamples/generated/7-structurals-miss.json", parser, collector),
|
struct7_miss (SIMDJSON_BENCHMARK_DATA_DIR "generated/7-structurals-miss.json", collector),
|
||||||
struct7_full ("jsonexamples/generated/7-structurals-full.json", parser, collector),
|
struct7_full (SIMDJSON_BENCHMARK_DATA_DIR "generated/7-structurals-full.json", collector),
|
||||||
struct15 ("jsonexamples/generated/15-structurals.json", parser, collector),
|
struct15 (SIMDJSON_BENCHMARK_DATA_DIR "generated/15-structurals.json", collector),
|
||||||
struct15_miss("jsonexamples/generated/15-structurals-miss.json", parser, collector),
|
struct15_miss(SIMDJSON_BENCHMARK_DATA_DIR "generated/15-structurals-miss.json", collector),
|
||||||
struct23 ("jsonexamples/generated/23-structurals.json", parser, collector),
|
struct23 (SIMDJSON_BENCHMARK_DATA_DIR "generated/23-structurals.json", collector),
|
||||||
struct23_miss("jsonexamples/generated/23-structurals-miss.json", parser, collector)
|
struct23_miss(SIMDJSON_BENCHMARK_DATA_DIR "generated/23-structurals-miss.json", collector)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
really_inline void run_iterations(size_t iterations, bool stage1_only=false) {
|
simdjson_inline void run_iterations(size_t iterations, bool stage1_only=false) {
|
||||||
struct7.run_iterations(iterations, stage1_only);
|
struct7.run_iterations(iterations, stage1_only);
|
||||||
struct7_miss.run_iterations(iterations, stage1_only);
|
struct7_miss.run_iterations(iterations, stage1_only);
|
||||||
struct7_full.run_iterations(iterations, stage1_only);
|
struct7_full.run_iterations(iterations, stage1_only);
|
||||||
@@ -189,7 +181,7 @@ struct feature_benchmarker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double cost_per_block(BenchmarkStage stage, const benchmarker& feature, size_t feature_blocks, const benchmarker& base) const {
|
double cost_per_block(BenchmarkStage stage, const benchmarker& feature, size_t feature_blocks, const benchmarker& base) const {
|
||||||
return (feature[stage].best.elapsed_ns() - base[stage].best.elapsed_ns()) / feature_blocks;
|
return (feature[stage].best.elapsed_ns() - base[stage].best.elapsed_ns()) / double(feature_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether we're recording cache miss and branch miss events
|
// Whether we're recording cache miss and branch miss events
|
||||||
@@ -199,7 +191,7 @@ struct feature_benchmarker {
|
|||||||
|
|
||||||
// Base cost of any block (including empty ones)
|
// Base cost of any block (including empty ones)
|
||||||
double base_cost(BenchmarkStage stage) const {
|
double base_cost(BenchmarkStage stage) const {
|
||||||
return (empty[stage].best.elapsed_ns() / empty.stats->blocks);
|
return (empty[stage].best.elapsed_ns() / double(empty.stats->blocks));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra cost of a 1-7 structural block over an empty block
|
// Extra cost of a 1-7 structural block over an empty block
|
||||||
@@ -212,10 +204,13 @@ struct feature_benchmarker {
|
|||||||
}
|
}
|
||||||
// Rate of 1-7-structural misses per 8-structural flip
|
// Rate of 1-7-structural misses per 8-structural flip
|
||||||
double struct1_7_miss_rate(BenchmarkStage stage) const {
|
double struct1_7_miss_rate(BenchmarkStage stage) const {
|
||||||
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
if (!has_events()) { return 1; }
|
if (!has_events()) { return 1; }
|
||||||
return double(struct7_miss[stage].best.branch_misses() - struct7[stage].best.branch_misses()) / struct7_miss.stats->blocks_with_1_structural_flipped;
|
return struct7_miss[stage].best.branch_misses() - struct7[stage].best.branch_misses() / double(struct7_miss.stats->blocks_with_1_structural_flipped);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra cost of an 8-15 structural block over a 1-7 structural block
|
// Extra cost of an 8-15 structural block over a 1-7 structural block
|
||||||
double struct8_15_cost(BenchmarkStage stage) const {
|
double struct8_15_cost(BenchmarkStage stage) const {
|
||||||
return cost_per_block(stage, struct15, struct15.stats->blocks_with_8_structurals, struct7);
|
return cost_per_block(stage, struct15, struct15.stats->blocks_with_8_structurals, struct7);
|
||||||
@@ -226,8 +221,12 @@ struct feature_benchmarker {
|
|||||||
}
|
}
|
||||||
// Rate of 8-15-structural misses per 8-structural flip
|
// Rate of 8-15-structural misses per 8-structural flip
|
||||||
double struct8_15_miss_rate(BenchmarkStage stage) const {
|
double struct8_15_miss_rate(BenchmarkStage stage) const {
|
||||||
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
if (!has_events()) { return 1; }
|
if (!has_events()) { return 1; }
|
||||||
return double(struct15_miss[stage].best.branch_misses() - struct15[stage].best.branch_misses()) / struct15_miss.stats->blocks_with_8_structurals_flipped;
|
return double(struct15_miss[stage].best.branch_misses() - struct15[stage].best.branch_misses()) / double(struct15_miss.stats->blocks_with_8_structurals_flipped);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra cost of a 16+-structural block over an 8-15 structural block (actual varies based on # of structurals!)
|
// Extra cost of a 16+-structural block over an 8-15 structural block (actual varies based on # of structurals!)
|
||||||
@@ -240,10 +239,15 @@ struct feature_benchmarker {
|
|||||||
}
|
}
|
||||||
// Rate of 16-structural misses per 16-structural flip
|
// Rate of 16-structural misses per 16-structural flip
|
||||||
double struct16_miss_rate(BenchmarkStage stage) const {
|
double struct16_miss_rate(BenchmarkStage stage) const {
|
||||||
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
if (!has_events()) { return 1; }
|
if (!has_events()) { return 1; }
|
||||||
return double(struct23_miss[stage].best.branch_misses() - struct23[stage].best.branch_misses()) / struct23_miss.stats->blocks_with_16_structurals_flipped;
|
return double(struct23_miss[stage].best.branch_misses() - struct23[stage].best.branch_misses()) / double(struct23_miss.stats->blocks_with_16_structurals_flipped);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Extra cost of having UTF-8 in a block
|
// Extra cost of having UTF-8 in a block
|
||||||
double utf8_cost(BenchmarkStage stage) const {
|
double utf8_cost(BenchmarkStage stage) const {
|
||||||
return cost_per_block(stage, utf8, utf8.stats->blocks_with_utf8, struct7_full);
|
return cost_per_block(stage, utf8, utf8.stats->blocks_with_utf8, struct7_full);
|
||||||
@@ -254,10 +258,13 @@ struct feature_benchmarker {
|
|||||||
}
|
}
|
||||||
// Rate of UTF-8 misses per UTF-8 flip
|
// Rate of UTF-8 misses per UTF-8 flip
|
||||||
double utf8_miss_rate(BenchmarkStage stage) const {
|
double utf8_miss_rate(BenchmarkStage stage) const {
|
||||||
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
if (!has_events()) { return 1; }
|
if (!has_events()) { return 1; }
|
||||||
return double(utf8_miss[stage].best.branch_misses() - utf8[stage].best.branch_misses()) / utf8_miss.stats->blocks_with_utf8_flipped;
|
return double(utf8_miss[stage].best.branch_misses() - utf8[stage].best.branch_misses()) / double(utf8_miss.stats->blocks_with_utf8_flipped);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extra cost of having escapes in a block
|
// Extra cost of having escapes in a block
|
||||||
double escape_cost(BenchmarkStage stage) const {
|
double escape_cost(BenchmarkStage stage) const {
|
||||||
return cost_per_block(stage, escape, escape.stats->blocks_with_escapes, struct7_full);
|
return cost_per_block(stage, escape, escape.stats->blocks_with_escapes, struct7_full);
|
||||||
@@ -268,47 +275,51 @@ struct feature_benchmarker {
|
|||||||
}
|
}
|
||||||
// Rate of escape misses per escape flip
|
// Rate of escape misses per escape flip
|
||||||
double escape_miss_rate(BenchmarkStage stage) const {
|
double escape_miss_rate(BenchmarkStage stage) const {
|
||||||
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
if (!has_events()) { return 1; }
|
if (!has_events()) { return 1; }
|
||||||
return double(escape_miss[stage].best.branch_misses() - escape[stage].best.branch_misses()) / escape_miss.stats->blocks_with_escapes_flipped;
|
return double(escape_miss[stage].best.branch_misses() - escape[stage].best.branch_misses()) / double(escape_miss.stats->blocks_with_escapes_flipped);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double calc_expected_feature_cost(BenchmarkStage stage, const benchmarker& file) const {
|
double calc_expected_feature_cost(BenchmarkStage stage, const benchmarker& file) const {
|
||||||
// Expected base ns/block (empty)
|
// Expected base ns/block (empty)
|
||||||
json_stats& stats = *file.stats;
|
json_stats& stats = *file.stats;
|
||||||
double expected = base_cost(stage) * stats.blocks;
|
double expected = base_cost(stage) * double(stats.blocks);
|
||||||
expected += struct1_7_cost(stage) * stats.blocks_with_1_structural;
|
expected += struct1_7_cost(stage) * double(stats.blocks_with_1_structural);
|
||||||
expected += utf8_cost(stage) * stats.blocks_with_utf8;
|
expected += utf8_cost(stage) * double(stats.blocks_with_utf8);
|
||||||
expected += escape_cost(stage) * stats.blocks_with_escapes;
|
expected += escape_cost(stage) * double(stats.blocks_with_escapes);
|
||||||
expected += struct8_15_cost(stage) * stats.blocks_with_8_structurals;
|
expected += struct8_15_cost(stage) * double(stats.blocks_with_8_structurals);
|
||||||
expected += struct16_cost(stage) * stats.blocks_with_16_structurals;
|
expected += struct16_cost(stage) * double(stats.blocks_with_16_structurals);
|
||||||
return expected / stats.blocks;
|
return expected / double(stats.blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
double calc_expected_miss_cost(BenchmarkStage stage, const benchmarker& file) const {
|
double calc_expected_miss_cost(BenchmarkStage stage, const benchmarker& file) const {
|
||||||
// Expected base ns/block (empty)
|
// Expected base ns/block (empty)
|
||||||
json_stats& stats = *file.stats;
|
json_stats& stats = *file.stats;
|
||||||
double expected = struct1_7_miss_cost(stage) * stats.blocks_with_1_structural_flipped * struct1_7_miss_rate(stage);
|
double expected = struct1_7_miss_cost(stage) * double(stats.blocks_with_1_structural_flipped) * struct1_7_miss_rate(stage);
|
||||||
expected += utf8_miss_cost(stage) * stats.blocks_with_utf8_flipped * utf8_miss_rate(stage);
|
expected += utf8_miss_cost(stage) * double(stats.blocks_with_utf8_flipped) * utf8_miss_rate(stage);
|
||||||
expected += escape_miss_cost(stage) * stats.blocks_with_escapes_flipped * escape_miss_rate(stage);
|
expected += escape_miss_cost(stage) * double(stats.blocks_with_escapes_flipped) * escape_miss_rate(stage);
|
||||||
expected += struct8_15_miss_cost(stage) * stats.blocks_with_8_structurals_flipped * struct8_15_miss_rate(stage);
|
expected += struct8_15_miss_cost(stage) * double(stats.blocks_with_8_structurals_flipped) * struct8_15_miss_rate(stage);
|
||||||
expected += struct16_miss_cost(stage) * stats.blocks_with_16_structurals_flipped * struct16_miss_rate(stage);
|
expected += struct16_miss_cost(stage) * double(stats.blocks_with_16_structurals_flipped) * struct16_miss_rate(stage);
|
||||||
return expected / stats.blocks;
|
return expected / double(stats.blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
double calc_expected_misses(BenchmarkStage stage, const benchmarker& file) const {
|
double calc_expected_misses(BenchmarkStage stage, const benchmarker& file) const {
|
||||||
json_stats& stats = *file.stats;
|
json_stats& stats = *file.stats;
|
||||||
double expected = stats.blocks_with_1_structural_flipped * struct1_7_miss_rate(stage);
|
double expected = double(stats.blocks_with_1_structural_flipped) * struct1_7_miss_rate(stage);
|
||||||
expected += stats.blocks_with_utf8_flipped * utf8_miss_rate(stage);
|
expected += double(stats.blocks_with_utf8_flipped) * utf8_miss_rate(stage);
|
||||||
expected += stats.blocks_with_escapes_flipped * escape_miss_rate(stage);
|
expected += double(stats.blocks_with_escapes_flipped) * escape_miss_rate(stage);
|
||||||
expected += stats.blocks_with_8_structurals_flipped * struct8_15_miss_rate(stage);
|
expected += double(stats.blocks_with_8_structurals_flipped) * struct8_15_miss_rate(stage);
|
||||||
expected += stats.blocks_with_16_structurals_flipped * struct16_miss_rate(stage);
|
expected += double(stats.blocks_with_16_structurals_flipped) * struct16_miss_rate(stage);
|
||||||
return expected;
|
return expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
double calc_expected(BenchmarkStage stage, const benchmarker& file) const {
|
double calc_expected(BenchmarkStage stage, const benchmarker& file) const {
|
||||||
return calc_expected_feature_cost(stage, file) + calc_expected_miss_cost(stage, file);
|
return calc_expected_feature_cost(stage, file) + calc_expected_miss_cost(stage, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(const option_struct& options) const {
|
void print(const option_struct& options) const {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Features in ns/block (64 bytes):\n");
|
printf("Features in ns/block (64 bytes):\n");
|
||||||
@@ -367,11 +378,11 @@ struct feature_benchmarker {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
void print_file_effectiveness(BenchmarkStage stage, const char* filename, const benchmarker& results, const feature_benchmarker& features) {
|
void print_file_effectiveness(BenchmarkStage stage, const char* filename, const benchmarker& results, const feature_benchmarker& features) {
|
||||||
double actual = results[stage].best.elapsed_ns() / results.stats->blocks;
|
double actual = results[stage].best.elapsed_ns() / double(results.stats->blocks);
|
||||||
double calc = features.calc_expected(stage, results);
|
double calc = features.calc_expected(stage, results);
|
||||||
uint64_t actual_misses = results[stage].best.branch_misses();
|
double calc_misses = features.calc_expected_misses(stage, results);
|
||||||
uint64_t calc_misses = uint64_t(features.calc_expected_misses(stage, results));
|
|
||||||
double calc_miss_cost = features.calc_expected_miss_cost(stage, results);
|
double calc_miss_cost = features.calc_expected_miss_cost(stage, results);
|
||||||
printf(" | %-8s ", benchmark_stage_name(stage));
|
printf(" | %-8s ", benchmark_stage_name(stage));
|
||||||
printf("| %-15s ", filename);
|
printf("| %-15s ", filename);
|
||||||
@@ -380,16 +391,33 @@ void print_file_effectiveness(BenchmarkStage stage, const char* filename, const
|
|||||||
printf("| %8.3g ", calc);
|
printf("| %8.3g ", calc);
|
||||||
printf("| %8.3g ", actual);
|
printf("| %8.3g ", actual);
|
||||||
printf("| %+8.3g ", actual - calc);
|
printf("| %+8.3g ", actual - calc);
|
||||||
printf("| %13lu ", calc_misses);
|
printf("| %13llu ", (long long unsigned)(calc_misses));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void print_file_effectiveness(BenchmarkStage stage, const char* filename, const benchmarker& results, const feature_benchmarker& features) {
|
||||||
|
double actual = results[stage].best.elapsed_ns() / double(results.stats->blocks);
|
||||||
|
double calc = features.calc_expected(stage, results);
|
||||||
|
double actual_misses = results[stage].best.branch_misses();
|
||||||
|
double calc_misses = features.calc_expected_misses(stage, results);
|
||||||
|
double calc_miss_cost = features.calc_expected_miss_cost(stage, results);
|
||||||
|
printf(" | %-8s ", benchmark_stage_name(stage));
|
||||||
|
printf("| %-15s ", filename);
|
||||||
|
printf("| %8.3g ", features.calc_expected_feature_cost(stage, results));
|
||||||
|
printf("| %8.3g ", calc_miss_cost);
|
||||||
|
printf("| %8.3g ", calc);
|
||||||
|
printf("| %8.3g ", actual);
|
||||||
|
printf("| %+8.3g ", actual - calc);
|
||||||
|
printf("| %13llu ", (long long unsigned)(calc_misses));
|
||||||
if (features.has_events()) {
|
if (features.has_events()) {
|
||||||
printf("| %13lu ", actual_misses);
|
printf("| %13llu ", (long long unsigned)(actual_misses));
|
||||||
printf("| %+13ld ", int64_t(actual_misses - calc_misses));
|
printf("| %+13lld ", (long long int)(actual_misses - calc_misses));
|
||||||
double miss_adjustment = calc_miss_cost * (double(int64_t(actual_misses - calc_misses)) / calc_misses);
|
double miss_adjustment = calc_miss_cost * (double(int64_t(actual_misses - calc_misses)) / calc_misses);
|
||||||
printf("| %8.3g ", calc_miss_cost + miss_adjustment);
|
printf("| %8.3g ", calc_miss_cost + miss_adjustment);
|
||||||
printf("| %+8.3g ", actual - (calc + miss_adjustment));
|
printf("| %+8.3g ", actual - (calc + miss_adjustment));
|
||||||
}
|
}
|
||||||
printf("|\n");
|
printf("|\n");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
// Read options
|
// Read options
|
||||||
@@ -405,9 +433,9 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
// Set up benchmarkers by reading all files
|
// Set up benchmarkers by reading all files
|
||||||
feature_benchmarker features(collector);
|
feature_benchmarker features(collector);
|
||||||
benchmarker gsoc_2018("jsonexamples/gsoc-2018.json", collector);
|
benchmarker gsoc_2018(SIMDJSON_BENCHMARK_DATA_DIR "gsoc-2018.json", collector);
|
||||||
benchmarker twitter("jsonexamples/twitter.json", collector);
|
benchmarker twitter(SIMDJSON_BENCHMARK_DATA_DIR "twitter.json", collector);
|
||||||
benchmarker random("jsonexamples/random.json", collector);
|
benchmarker random(SIMDJSON_BENCHMARK_DATA_DIR "random.json", collector);
|
||||||
|
|
||||||
// Run the benchmarks
|
// Run the benchmarks
|
||||||
progress_bar progress(options.iterations, 100);
|
progress_bar progress(options.iterations, 100);
|
||||||
|
|||||||
+75
-193
@@ -1,82 +1,7 @@
|
|||||||
#ifndef _BENCHMARK_H_
|
#ifndef _BENCHMARK_H_
|
||||||
#define _BENCHMARK_H_
|
#define _BENCHMARK_H_
|
||||||
#include <float.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <time.h>
|
|
||||||
#ifdef __x86_64__
|
|
||||||
|
|
||||||
const char *unitname = "cycles";
|
#include "event_counter.h"
|
||||||
|
|
||||||
#define RDTSC_START(cycles) \
|
|
||||||
do { \
|
|
||||||
uint32_t cyc_high, cyc_low; \
|
|
||||||
__asm volatile("cpuid\n" \
|
|
||||||
"rdtsc\n" \
|
|
||||||
"mov %%edx, %0\n" \
|
|
||||||
"mov %%eax, %1" \
|
|
||||||
: "=r"(cyc_high), "=r"(cyc_low) \
|
|
||||||
: \
|
|
||||||
: /* no read only */ \
|
|
||||||
"%rax", "%rbx", "%rcx", "%rdx" /* clobbers */ \
|
|
||||||
); \
|
|
||||||
(cycles) = ((uint64_t)cyc_high << 32) | cyc_low; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define RDTSC_STOP(cycles) \
|
|
||||||
do { \
|
|
||||||
uint32_t cyc_high, cyc_low; \
|
|
||||||
__asm volatile("rdtscp\n" \
|
|
||||||
"mov %%edx, %0\n" \
|
|
||||||
"mov %%eax, %1\n" \
|
|
||||||
"cpuid" \
|
|
||||||
: "=r"(cyc_high), "=r"(cyc_low) \
|
|
||||||
: /* no read only registers */ \
|
|
||||||
: "%rax", "%rbx", "%rcx", "%rdx" /* clobbers */ \
|
|
||||||
); \
|
|
||||||
(cycles) = ((uint64_t)cyc_high << 32) | cyc_low; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#else
|
|
||||||
const char *unitname = " (clock units) ";
|
|
||||||
|
|
||||||
#define RDTSC_START(cycles) \
|
|
||||||
do { \
|
|
||||||
cycles = clock(); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define RDTSC_STOP(cycles) \
|
|
||||||
do { \
|
|
||||||
cycles = clock(); \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static __attribute__((noinline)) uint64_t rdtsc_overhead_func(uint64_t dummy) {
|
|
||||||
return dummy;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t global_rdtsc_overhead = (uint64_t)UINT64_MAX;
|
|
||||||
|
|
||||||
#define RDTSC_SET_OVERHEAD(test, repeat) \
|
|
||||||
do { \
|
|
||||||
uint64_t cycles_start, cycles_final, cycles_diff; \
|
|
||||||
uint64_t min_diff = UINT64_MAX; \
|
|
||||||
for (int i = 0; i < repeat; i++) { \
|
|
||||||
__asm volatile("" ::: /* pretend to clobber */ "memory"); \
|
|
||||||
RDTSC_START(cycles_start); \
|
|
||||||
test; \
|
|
||||||
RDTSC_STOP(cycles_final); \
|
|
||||||
cycles_diff = (cycles_final - cycles_start); \
|
|
||||||
if (cycles_diff < min_diff) \
|
|
||||||
min_diff = cycles_diff; \
|
|
||||||
} \
|
|
||||||
global_rdtsc_overhead = min_diff; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
double diff(timespec start, timespec end) {
|
|
||||||
return ((end.tv_nsec + 1000000000 * end.tv_sec) -
|
|
||||||
(start.tv_nsec + 1000000000 * start.tv_sec)) /
|
|
||||||
1000000000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prints the best number of operations per cycle where
|
* Prints the best number of operations per cycle where
|
||||||
@@ -86,142 +11,99 @@ double diff(timespec start, timespec end) {
|
|||||||
*/
|
*/
|
||||||
#define BEST_TIME(name, test, expected, pre, repeat, size, verbose) \
|
#define BEST_TIME(name, test, expected, pre, repeat, size, verbose) \
|
||||||
do { \
|
do { \
|
||||||
if (global_rdtsc_overhead == UINT64_MAX) { \
|
|
||||||
RDTSC_SET_OVERHEAD(rdtsc_overhead_func(1), repeat); \
|
|
||||||
} \
|
|
||||||
if (verbose) \
|
if (verbose) \
|
||||||
printf("%-40s\t: ", name); \
|
std::printf("%-40s\t: ", name); \
|
||||||
else \
|
else \
|
||||||
printf("\"%-40s\"", name); \
|
std::printf("\"%-40s\"", name); \
|
||||||
fflush(NULL); \
|
fflush(NULL); \
|
||||||
uint64_t cycles_start, cycles_final, cycles_diff; \
|
event_collector collector; \
|
||||||
uint64_t min_diff = (uint64_t)-1; \
|
event_aggregate aggregate{}; \
|
||||||
double min_sumclockdiff = DBL_MAX; \
|
for (decltype(repeat) i = 0; i < repeat; i++) { \
|
||||||
uint64_t sum_diff = 0; \
|
|
||||||
double sumclockdiff = 0; \
|
|
||||||
struct timespec time1, time2; \
|
|
||||||
for (int i = 0; i < repeat; i++) { \
|
|
||||||
pre; \
|
pre; \
|
||||||
__asm volatile("" ::: /* pretend to clobber */ "memory"); \
|
std::atomic_thread_fence(std::memory_order_acquire); \
|
||||||
clock_gettime(CLOCK_REALTIME, &time1); \
|
collector.start(); \
|
||||||
RDTSC_START(cycles_start); \
|
|
||||||
if (test != expected) { \
|
if (test != expected) { \
|
||||||
fprintf(stderr, "not expected (%d , %d )", (int)test, (int)expected); \
|
std::fprintf(stderr, "not expected (%d , %d )", (int)test, \
|
||||||
|
(int)expected); \
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
RDTSC_STOP(cycles_final); \
|
std::atomic_thread_fence(std::memory_order_release); \
|
||||||
clock_gettime(CLOCK_REALTIME, &time2); \
|
event_count allocate_count = collector.end(); \
|
||||||
double thistiming = diff(time1, time2); \
|
aggregate << allocate_count; \
|
||||||
sumclockdiff += thistiming; \
|
|
||||||
if (thistiming < min_sumclockdiff) \
|
|
||||||
min_sumclockdiff = thistiming; \
|
|
||||||
cycles_diff = (cycles_final - cycles_start - global_rdtsc_overhead); \
|
|
||||||
if (cycles_diff < min_diff) \
|
|
||||||
min_diff = cycles_diff; \
|
|
||||||
sum_diff += cycles_diff; \
|
|
||||||
} \
|
} \
|
||||||
uint64_t S = size; \
|
if (collector.has_events()) { \
|
||||||
float cycle_per_op = (min_diff) / (double)S; \
|
std::printf("%7.3f", \
|
||||||
float avg_cycle_per_op = (sum_diff) / ((double)S * repeat); \
|
aggregate.best.cycles() / static_cast<double>(size)); \
|
||||||
double avg_gb_per_s = \
|
if (verbose) { \
|
||||||
((double)S * repeat) / ((sumclockdiff)*1000.0 * 1000.0 * 1000.0); \
|
std::printf(" cycles/byte "); \
|
||||||
double max_gb_per_s = \
|
} \
|
||||||
((double)S) / ((min_sumclockdiff)*1000.0 * 1000.0 * 1000.0); \
|
std::printf("\t"); \
|
||||||
if (verbose) \
|
std::printf("%7.3f", \
|
||||||
printf(" %7.3f %s per input byte (best) ", cycle_per_op, unitname); \
|
aggregate.best.instructions() / static_cast<double>(size)); \
|
||||||
if (verbose) \
|
if (verbose) { \
|
||||||
printf(" %7.3f %s (avg) ", avg_cycle_per_op, unitname); \
|
std::printf(" instructions/byte "); \
|
||||||
if (verbose) \
|
} \
|
||||||
printf(" %7.3f GB/s (error margin: %5.3f GB/s)", max_gb_per_s, \
|
std::printf("\t"); \
|
||||||
-avg_gb_per_s + max_gb_per_s); \
|
} \
|
||||||
if (verbose) \
|
double gb = static_cast<double>(size) / 1000000000.0; \
|
||||||
printf(" %13.0f documents/s (best)", 1.0/min_sumclockdiff); \
|
std::printf("%7.3f", gb / aggregate.best.elapsed_sec()); \
|
||||||
if (verbose) \
|
if (verbose) { \
|
||||||
printf(" %13.0f documents/s (avg)", 1.0/(sumclockdiff/repeat)); \
|
std::printf(" GB/s "); \
|
||||||
if (!verbose) \
|
} \
|
||||||
printf(" %20.3f %20.3f %20.3f %20.3f", cycle_per_op, \
|
std::printf("\t"); \
|
||||||
avg_cycle_per_op - cycle_per_op, max_gb_per_s, \
|
std::printf("%7.3f", 1.0 / aggregate.best.elapsed_sec()); \
|
||||||
-avg_gb_per_s + max_gb_per_s); \
|
if (verbose) { \
|
||||||
printf("\n"); \
|
std::printf(" documents/s "); \
|
||||||
fflush(NULL); \
|
} \
|
||||||
|
std::printf("\n"); \
|
||||||
|
std::fflush(NULL); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// like BEST_TIME, but no check
|
// like BEST_TIME, but no check
|
||||||
#define BEST_TIME_NOCHECK(name, test, pre, repeat, size, verbose) \
|
#define BEST_TIME_NOCHECK(name, test, pre, repeat, size, verbose) \
|
||||||
do { \
|
do { \
|
||||||
if (global_rdtsc_overhead == UINT64_MAX) { \
|
|
||||||
RDTSC_SET_OVERHEAD(rdtsc_overhead_func(1), repeat); \
|
|
||||||
} \
|
|
||||||
if (verbose) \
|
if (verbose) \
|
||||||
printf("%-40s\t: ", name); \
|
std::printf("%-40s\t: ", name); \
|
||||||
fflush(NULL); \
|
else \
|
||||||
uint64_t cycles_start, cycles_final, cycles_diff; \
|
std::printf("\"%-40s\"", name); \
|
||||||
uint64_t min_diff = (uint64_t)-1; \
|
std::fflush(NULL); \
|
||||||
uint64_t sum_diff = 0; \
|
event_collector collector; \
|
||||||
for (int i = 0; i < repeat; i++) { \
|
event_aggregate aggregate{}; \
|
||||||
|
for (decltype(repeat) i = 0; i < repeat; i++) { \
|
||||||
pre; \
|
pre; \
|
||||||
__asm volatile("" ::: /* pretend to clobber */ "memory"); \
|
std::atomic_thread_fence(std::memory_order_acquire); \
|
||||||
RDTSC_START(cycles_start); \
|
collector.start(); \
|
||||||
test; \
|
test; \
|
||||||
RDTSC_STOP(cycles_final); \
|
std::atomic_thread_fence(std::memory_order_release); \
|
||||||
cycles_diff = (cycles_final - cycles_start - global_rdtsc_overhead); \
|
event_count allocate_count = collector.end(); \
|
||||||
if (cycles_diff < min_diff) \
|
aggregate << allocate_count; \
|
||||||
min_diff = cycles_diff; \
|
|
||||||
sum_diff += cycles_diff; \
|
|
||||||
} \
|
} \
|
||||||
uint64_t S = size; \
|
if (collector.has_events()) { \
|
||||||
float cycle_per_op = (min_diff) / (double)S; \
|
std::printf("%7.3f", \
|
||||||
float avg_cycle_per_op = (sum_diff) / ((double)S * repeat); \
|
aggregate.best.cycles() / static_cast<double>(size)); \
|
||||||
if (verbose) \
|
if (verbose) { \
|
||||||
printf(" %.3f %s per input byte (best) ", cycle_per_op, unitname); \
|
std::printf(" cycles/byte "); \
|
||||||
if (verbose) \
|
|
||||||
printf(" %.3f %s per input byte (avg) ", avg_cycle_per_op, unitname); \
|
|
||||||
if (verbose) \
|
|
||||||
printf("\n"); \
|
|
||||||
if (!verbose) \
|
|
||||||
printf(" %.3f ", cycle_per_op); \
|
|
||||||
fflush(NULL); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// like BEST_TIME except that we run a function to check the result
|
|
||||||
#define BEST_TIME_CHECK(test, check, pre, repeat, size, verbose) \
|
|
||||||
do { \
|
|
||||||
if (global_rdtsc_overhead == UINT64_MAX) { \
|
|
||||||
RDTSC_SET_OVERHEAD(rdtsc_overhead_func(1), repeat); \
|
|
||||||
} \
|
|
||||||
if (verbose) \
|
|
||||||
printf("%-60s\t:\n", #test); \
|
|
||||||
fflush(NULL); \
|
|
||||||
uint64_t cycles_start, cycles_final, cycles_diff; \
|
|
||||||
uint64_t min_diff = (uint64_t)-1; \
|
|
||||||
uint64_t sum_diff = 0; \
|
|
||||||
for (int i = 0; i < repeat; i++) { \
|
|
||||||
pre; \
|
|
||||||
__asm volatile("" ::: /* pretend to clobber */ "memory"); \
|
|
||||||
RDTSC_START(cycles_start); \
|
|
||||||
test; \
|
|
||||||
RDTSC_STOP(cycles_final); \
|
|
||||||
if (!check) { \
|
|
||||||
printf("error"); \
|
|
||||||
break; \
|
|
||||||
} \
|
} \
|
||||||
cycles_diff = (cycles_final - cycles_start - global_rdtsc_overhead); \
|
std::printf("\t"); \
|
||||||
if (cycles_diff < min_diff) \
|
std::printf("%7.3f", \
|
||||||
min_diff = cycles_diff; \
|
aggregate.best.instructions() / static_cast<double>(size)); \
|
||||||
sum_diff += cycles_diff; \
|
if (verbose) { \
|
||||||
|
std::printf(" instructions/byte "); \
|
||||||
|
} \
|
||||||
|
std::printf("\t"); \
|
||||||
} \
|
} \
|
||||||
uint64_t S = size; \
|
double gb = static_cast<double>(size) / 1000000000.0; \
|
||||||
float cycle_per_op = (min_diff) / (double)S; \
|
std::printf("%7.3f", gb / aggregate.best.elapsed_sec()); \
|
||||||
float avg_cycle_per_op = (sum_diff) / ((double)S * repeat); \
|
if (verbose) { \
|
||||||
if (verbose) \
|
std::printf(" GB/s "); \
|
||||||
printf(" %.3f cycles per operation (best) ", cycle_per_op); \
|
} \
|
||||||
if (verbose) \
|
std::printf("\t"); \
|
||||||
printf("\t%.3f cycles per operation (avg) ", avg_cycle_per_op); \
|
std::printf("%7.3f", 1.0 / aggregate.best.elapsed_sec()); \
|
||||||
if (verbose) \
|
if (verbose) { \
|
||||||
printf("\n"); \
|
std::printf(" documents/s "); \
|
||||||
if (!verbose) \
|
} \
|
||||||
printf(" %.3f ", cycle_per_op); \
|
std::printf("\n"); \
|
||||||
fflush(NULL); \
|
std::fflush(NULL); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+106
-77
@@ -2,13 +2,14 @@
|
|||||||
#define __BENCHMARKER_H
|
#define __BENCHMARKER_H
|
||||||
|
|
||||||
#include "event_counter.h"
|
#include "event_counter.h"
|
||||||
|
#include "simdjson.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
#include <unistd.h>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -45,6 +46,8 @@ using std::vector;
|
|||||||
using std::ostream;
|
using std::ostream;
|
||||||
using std::ofstream;
|
using std::ofstream;
|
||||||
using std::exception;
|
using std::exception;
|
||||||
|
using std::min;
|
||||||
|
using std::max;
|
||||||
|
|
||||||
// Initialize "verbose" to go nowhere. We'll read options in main() and set to cout if verbose is true.
|
// Initialize "verbose" to go nowhere. We'll read options in main() and set to cout if verbose is true.
|
||||||
std::ofstream dev_null;
|
std::ofstream dev_null;
|
||||||
@@ -81,7 +84,7 @@ struct json_stats {
|
|||||||
bytes = json.size();
|
bytes = json.size();
|
||||||
blocks = bytes / BYTES_PER_BLOCK;
|
blocks = bytes / BYTES_PER_BLOCK;
|
||||||
if (bytes % BYTES_PER_BLOCK > 0) { blocks++; } // Account for remainder block
|
if (bytes % BYTES_PER_BLOCK > 0) { blocks++; } // Account for remainder block
|
||||||
structurals = parser.n_structural_indexes-1;
|
structurals = parser.implementation->n_structural_indexes-1;
|
||||||
|
|
||||||
// Calculate stats on blocks that will trigger utf-8 if statements / mispredictions
|
// Calculate stats on blocks that will trigger utf-8 if statements / mispredictions
|
||||||
bool last_block_has_utf8 = false;
|
bool last_block_has_utf8 = false;
|
||||||
@@ -138,7 +141,7 @@ struct json_stats {
|
|||||||
for (size_t block=0; block<blocks; block++) {
|
for (size_t block=0; block<blocks; block++) {
|
||||||
// Count structurals in the block
|
// Count structurals in the block
|
||||||
int block_structurals=0;
|
int block_structurals=0;
|
||||||
while (structural < parser.n_structural_indexes && parser.structural_indexes[structural] < (block+1)*BYTES_PER_BLOCK) {
|
while (structural < parser.implementation->n_structural_indexes && parser.implementation->structural_indexes[structural] < (block+1)*BYTES_PER_BLOCK) {
|
||||||
block_structurals++;
|
block_structurals++;
|
||||||
structural++;
|
structural++;
|
||||||
}
|
}
|
||||||
@@ -222,8 +225,19 @@ struct progress_bar {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The speed at which we can allocate memory is strictly system specific.
|
||||||
|
* It depends on the OS and the runtime library. It is subject to various
|
||||||
|
* system-specific knobs. It is not something that we can reasonably
|
||||||
|
* benchmark with crude timings.
|
||||||
|
* If someone wants to optimize how simdjson allocate memory, then it will
|
||||||
|
* almost surely require a distinct benchmarking tool. What is meant by
|
||||||
|
* "memory allocation" also requires a definition. Doing "new char[size]" can
|
||||||
|
* do many different things depending on the system.
|
||||||
|
*/
|
||||||
|
|
||||||
enum class BenchmarkStage {
|
enum class BenchmarkStage {
|
||||||
ALL,
|
ALL, // This excludes allocation
|
||||||
ALLOCATE,
|
ALLOCATE,
|
||||||
STAGE1,
|
STAGE1,
|
||||||
STAGE2
|
STAGE2
|
||||||
@@ -231,7 +245,7 @@ enum class BenchmarkStage {
|
|||||||
|
|
||||||
const char* benchmark_stage_name(BenchmarkStage stage) {
|
const char* benchmark_stage_name(BenchmarkStage stage) {
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case BenchmarkStage::ALL: return "All";
|
case BenchmarkStage::ALL: return "All (Without Allocation)";
|
||||||
case BenchmarkStage::ALLOCATE: return "Allocate";
|
case BenchmarkStage::ALLOCATE: return "Allocate";
|
||||||
case BenchmarkStage::STAGE1: return "Stage 1";
|
case BenchmarkStage::STAGE1: return "Stage 1";
|
||||||
case BenchmarkStage::STAGE2: return "Stage 2";
|
case BenchmarkStage::STAGE2: return "Stage 2";
|
||||||
@@ -241,7 +255,7 @@ const char* benchmark_stage_name(BenchmarkStage stage) {
|
|||||||
|
|
||||||
struct benchmarker {
|
struct benchmarker {
|
||||||
// JSON text from loading the file. Owns the memory.
|
// JSON text from loading the file. Owns the memory.
|
||||||
padded_string json;
|
padded_string json{};
|
||||||
// JSON filename
|
// JSON filename
|
||||||
const char *filename;
|
const char *filename;
|
||||||
// Event collector that can be turned on to measure cycles, missed branches, etc.
|
// Event collector that can be turned on to measure cycles, missed branches, etc.
|
||||||
@@ -250,22 +264,21 @@ struct benchmarker {
|
|||||||
// Statistics about the JSON file independent of its speed (amount of utf-8, structurals, etc.).
|
// Statistics about the JSON file independent of its speed (amount of utf-8, structurals, etc.).
|
||||||
// Loaded on first parse.
|
// Loaded on first parse.
|
||||||
json_stats* stats;
|
json_stats* stats;
|
||||||
// Speed and event summary for full parse (not including allocation)
|
// Speed and event summary for full parse (stage 1 and stage 2, but *excluding* allocation)
|
||||||
event_aggregate all_stages;
|
event_aggregate all_stages_without_allocation{};
|
||||||
// Speed and event summary for stage 1
|
// Speed and event summary for stage 1
|
||||||
event_aggregate stage1;
|
event_aggregate stage1{};
|
||||||
// Speed and event summary for stage 2
|
// Speed and event summary for stage 2
|
||||||
event_aggregate stage2;
|
event_aggregate stage2{};
|
||||||
// Speed and event summary for allocation
|
// Speed and event summary for allocation
|
||||||
event_aggregate allocate_stage;
|
event_aggregate allocate_stage{};
|
||||||
// Speed and event summary for the repeatly-parsing mode
|
// Speed and event summary for the repeatly-parsing mode
|
||||||
event_aggregate loop;
|
event_aggregate loop{};
|
||||||
|
|
||||||
benchmarker(const char *_filename, event_collector& _collector)
|
benchmarker(const char *_filename, event_collector& _collector)
|
||||||
: filename(_filename), collector(_collector), stats(NULL) {
|
: filename(_filename), collector(_collector), stats(NULL) {
|
||||||
verbose() << "[verbose] loading " << filename << endl;
|
verbose() << "[verbose] loading " << filename << endl;
|
||||||
simdjson::error_code error;
|
auto error = padded_string::load(filename).get(json);
|
||||||
padded_string::load(filename).tie(this->json, error);
|
|
||||||
if (error) {
|
if (error) {
|
||||||
exit_error(string("Could not load the file ") + filename);
|
exit_error(string("Could not load the file ") + filename);
|
||||||
}
|
}
|
||||||
@@ -278,33 +291,37 @@ struct benchmarker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
benchmarker(const benchmarker&) = delete;
|
||||||
|
benchmarker& operator=(const benchmarker&) = delete;
|
||||||
|
|
||||||
const event_aggregate& operator[](BenchmarkStage stage) const {
|
const event_aggregate& operator[](BenchmarkStage stage) const {
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case BenchmarkStage::ALL: return this->all_stages;
|
case BenchmarkStage::ALL: return this->all_stages_without_allocation;
|
||||||
case BenchmarkStage::STAGE1: return this->stage1;
|
case BenchmarkStage::STAGE1: return this->stage1;
|
||||||
case BenchmarkStage::STAGE2: return this->stage2;
|
case BenchmarkStage::STAGE2: return this->stage2;
|
||||||
case BenchmarkStage::ALLOCATE: return this->allocate_stage;
|
case BenchmarkStage::ALLOCATE: return this->allocate_stage;
|
||||||
default: exit_error("Unknown stage"); return this->all_stages;
|
default: exit_error("Unknown stage"); return this->all_stages_without_allocation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int iterations() const {
|
int iterations() const {
|
||||||
return all_stages.iterations;
|
return all_stages_without_allocation.iterations;
|
||||||
}
|
}
|
||||||
|
|
||||||
really_inline void run_iteration(bool stage1_only, bool hotbuffers=false) {
|
simdjson_inline void run_iteration(bool stage1_only, bool hotbuffers=false) {
|
||||||
// Allocate dom::parser
|
// Allocate dom::parser
|
||||||
collector.start();
|
collector.start();
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
error_code error = parser.allocate(json.size());
|
// We always allocate at least 64KB. Smaller allocations may actually be slower under some systems.
|
||||||
|
error_code error = parser.allocate(json.size() < 65536 ? 65536 : json.size());
|
||||||
if (error) {
|
if (error) {
|
||||||
exit_error(string("Unable to allocate_stage ") + to_string(json.size()) + " bytes for the JSON result: " + error_message(error));
|
exit_error(string("Unable to allocate_stage ") + to_string(json.size()) + " bytes for the JSON text: " + error_message(error));
|
||||||
}
|
}
|
||||||
event_count allocate_count = collector.end();
|
event_count allocate_count = collector.end();
|
||||||
allocate_stage << allocate_count;
|
allocate_stage << allocate_count;
|
||||||
// Run it once to get hot buffers
|
// Run it once to get hot buffers
|
||||||
if(hotbuffers) {
|
if(hotbuffers) {
|
||||||
auto result = parser.parse((const uint8_t *)json.data(), json.size());
|
auto result = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
|
||||||
if (result.error()) {
|
if (result.error()) {
|
||||||
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
|
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
|
||||||
}
|
}
|
||||||
@@ -314,7 +331,7 @@ struct benchmarker {
|
|||||||
|
|
||||||
// Stage 1 (find structurals)
|
// Stage 1 (find structurals)
|
||||||
collector.start();
|
collector.start();
|
||||||
error = active_implementation->stage1((const uint8_t *)json.data(), json.size(), parser, false);
|
error = parser.implementation->stage1(reinterpret_cast<const uint8_t *>(json.data()), json.size(), stage1_mode::regular);
|
||||||
event_count stage1_count = collector.end();
|
event_count stage1_count = collector.end();
|
||||||
stage1 << stage1_count;
|
stage1 << stage1_count;
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -324,22 +341,22 @@ struct benchmarker {
|
|||||||
// Stage 2 (unified machine) and the rest
|
// Stage 2 (unified machine) and the rest
|
||||||
|
|
||||||
if (stage1_only) {
|
if (stage1_only) {
|
||||||
all_stages << stage1_count;
|
all_stages_without_allocation << stage1_count;
|
||||||
} else {
|
} else {
|
||||||
event_count stage2_count;
|
event_count stage2_count;
|
||||||
collector.start();
|
collector.start();
|
||||||
error = active_implementation->stage2((const uint8_t *)json.data(), json.size(), parser);
|
error = parser.implementation->stage2(parser.doc);
|
||||||
if (error) {
|
if (error) {
|
||||||
exit_error(string("Failed to parse ") + filename + " during stage 2 parsing " + error_message(error));
|
exit_error(string("Failed to parse ") + filename + " during stage 2 parsing " + error_message(error));
|
||||||
}
|
}
|
||||||
stage2_count = collector.end();
|
stage2_count = collector.end();
|
||||||
stage2 << stage2_count;
|
stage2 << stage2_count;
|
||||||
all_stages << allocate_count + stage1_count + stage2_count;
|
all_stages_without_allocation << stage1_count + stage2_count;
|
||||||
}
|
}
|
||||||
// Calculate stats the first time we parse
|
// Calculate stats the first time we parse
|
||||||
if (stats == NULL) {
|
if (stats == NULL) {
|
||||||
if (stage1_only) { // we need stage 2 once
|
if (stage1_only) { // we need stage 2 once
|
||||||
error = active_implementation->stage2((const uint8_t *)json.data(), json.size(), parser);
|
error = parser.implementation->stage2(parser.doc);
|
||||||
if (error) {
|
if (error) {
|
||||||
printf("Warning: failed to parse during stage 2. Unable to acquire statistics.\n");
|
printf("Warning: failed to parse during stage 2. Unable to acquire statistics.\n");
|
||||||
}
|
}
|
||||||
@@ -350,7 +367,7 @@ struct benchmarker {
|
|||||||
|
|
||||||
void run_loop(size_t iterations) {
|
void run_loop(size_t iterations) {
|
||||||
dom::parser parser;
|
dom::parser parser;
|
||||||
auto firstresult = parser.parse((const uint8_t *)json.data(), json.size());
|
auto firstresult = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
|
||||||
if (firstresult.error()) {
|
if (firstresult.error()) {
|
||||||
exit_error(string("Failed to parse ") + filename + string(":") + error_message(firstresult.error()));
|
exit_error(string("Failed to parse ") + filename + string(":") + error_message(firstresult.error()));
|
||||||
}
|
}
|
||||||
@@ -358,7 +375,7 @@ struct benchmarker {
|
|||||||
collector.start();
|
collector.start();
|
||||||
// some users want something closer to "number of documents per second"
|
// some users want something closer to "number of documents per second"
|
||||||
for(size_t i = 0; i < iterations; i++) {
|
for(size_t i = 0; i < iterations; i++) {
|
||||||
auto result = parser.parse((const uint8_t *)json.data(), json.size());
|
auto result = parser.parse(reinterpret_cast<const uint8_t *>(json.data()), json.size());
|
||||||
if (result.error()) {
|
if (result.error()) {
|
||||||
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
|
exit_error(string("Failed to parse ") + filename + string(":") + error_message(result.error()));
|
||||||
}
|
}
|
||||||
@@ -367,62 +384,75 @@ struct benchmarker {
|
|||||||
loop << all_loop_count;
|
loop << all_loop_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
really_inline void run_iterations(size_t iterations, bool stage1_only, bool hotbuffers=false) {
|
simdjson_inline void run_iterations(size_t iterations, bool stage1_only, bool hotbuffers=false) {
|
||||||
for (size_t i = 0; i<iterations; i++) {
|
for (size_t i = 0; i<iterations; i++) {
|
||||||
run_iteration(stage1_only, hotbuffers);
|
run_iteration(stage1_only, hotbuffers);
|
||||||
}
|
}
|
||||||
run_loop(iterations);
|
run_loop(iterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void print_aggregate(const char* prefix, const T& stage) const {
|
void print_aggregate(const char* prefix, const T& stage) const {
|
||||||
printf("%s%-13s: %8.4f ns per block (%6.2f%%) - %8.4f ns per byte - %8.4f ns per structural - %8.3f GB/s\n",
|
printf("%s%-13s: %8.4f ns per block (%6.2f%%) - %8.4f ns per byte - %8.4f ns per structural - %8.4f GB/s\n",
|
||||||
prefix,
|
prefix,
|
||||||
"Speed",
|
"Speed",
|
||||||
stage.elapsed_ns() / stats->blocks, // per block
|
stage.elapsed_ns() / static_cast<double>(stats->blocks), // per block
|
||||||
100.0 * stage.elapsed_sec() / all_stages.elapsed_sec(), // %
|
percent(stage.elapsed_sec(), all_stages_without_allocation.elapsed_sec()), // %
|
||||||
stage.elapsed_ns() / stats->bytes, // per byte
|
stage.elapsed_ns() / static_cast<double>(stats->bytes), // per byte
|
||||||
stage.elapsed_ns() / stats->structurals, // per structural
|
stage.elapsed_ns() / static_cast<double>(stats->structurals), // per structural
|
||||||
(json.size() / 1000000000.0) / stage.elapsed_sec() // GB/s
|
(static_cast<double>(json.size()) / 1000000000.0) / stage.elapsed_sec() // GB/s
|
||||||
);
|
);
|
||||||
|
|
||||||
if (collector.has_events()) {
|
if (collector.has_events()) {
|
||||||
printf("%s%-13s: %8.4f per block (%6.2f%%) - %8.4f per byte - %8.4f per structural - %8.3f GHz est. frequency\n",
|
printf("%s%-13s: %8.4f per block (%6.2f%%) - %8.4f per byte - %8.4f per structural - %8.3f GHz est. frequency\n",
|
||||||
prefix,
|
prefix,
|
||||||
"Cycles",
|
"Cycles",
|
||||||
stage.cycles() / stats->blocks,
|
stage.cycles() / static_cast<double>(stats->blocks),
|
||||||
100.0 * stage.cycles() / all_stages.cycles(),
|
percent(stage.cycles(), all_stages_without_allocation.cycles()),
|
||||||
stage.cycles() / stats->bytes,
|
stage.cycles() / static_cast<double>(stats->bytes),
|
||||||
stage.cycles() / stats->structurals,
|
stage.cycles() / static_cast<double>(stats->structurals),
|
||||||
(stage.cycles() / stage.elapsed_sec()) / 1000000000.0
|
(stage.cycles() / stage.elapsed_sec()) / 1000000000.0
|
||||||
);
|
);
|
||||||
printf("%s%-13s: %8.4f per block (%6.2f%%) - %8.4f per byte - %8.4f per structural - %8.3f per cycle\n",
|
printf("%s%-13s: %8.4f per block (%6.2f%%) - %8.4f per byte - %8.4f per structural - %8.3f per cycle\n",
|
||||||
prefix,
|
prefix,
|
||||||
"Instructions",
|
"Instructions",
|
||||||
stage.instructions() / stats->blocks,
|
stage.instructions() / static_cast<double>(stats->blocks),
|
||||||
100.0 * stage.instructions() / all_stages.instructions(),
|
percent(stage.instructions(), all_stages_without_allocation.instructions()),
|
||||||
stage.instructions() / stats->bytes,
|
stage.instructions() / static_cast<double>(stats->bytes),
|
||||||
stage.instructions() / stats->structurals,
|
stage.instructions() / static_cast<double>(stats->structurals),
|
||||||
stage.instructions() / stage.cycles()
|
stage.instructions() / static_cast<double>(stage.cycles())
|
||||||
);
|
);
|
||||||
|
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
// NOTE: removed cycles/miss because it is a somewhat misleading stat
|
// NOTE: removed cycles/miss because it is a somewhat misleading stat
|
||||||
printf("%s%-13s: %7.0f branch misses (%6.2f%%) - %.0f cache misses (%6.2f%%) - %.2f cache references\n",
|
printf("%s%-13s: %7.0f branch misses (%6.2f%%) - %.0f cache misses (%6.2f%%) - %.2f cache references\n",
|
||||||
prefix,
|
prefix,
|
||||||
"Misses",
|
"Misses",
|
||||||
stage.branch_misses(),
|
stage.branch_misses(),
|
||||||
100.0 * stage.branch_misses() / all_stages.branch_misses(),
|
percent(stage.branch_misses(), all_stages_without_allocation.branch_misses()),
|
||||||
stage.cache_misses(),
|
stage.cache_misses(),
|
||||||
100.0 * stage.cache_misses() / all_stages.cache_misses(),
|
percent(stage.cache_misses(), all_stages_without_allocation.cache_misses()),
|
||||||
stage.cache_references()
|
stage.cache_references()
|
||||||
);
|
);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(bool tabbed_output, size_t iterations) const {
|
static double percent(size_t a, size_t b) {
|
||||||
|
return 100.0 * static_cast<double>(a) / static_cast<double>(b);
|
||||||
|
}
|
||||||
|
static double percent(double a, double b) {
|
||||||
|
return 100.0 * a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print(bool tabbed_output) const {
|
||||||
if (tabbed_output) {
|
if (tabbed_output) {
|
||||||
char* filename_copy = (char*)malloc(strlen(filename)+1);
|
char* filename_copy = reinterpret_cast<char*>(malloc(strlen(filename)+1));
|
||||||
|
SIMDJSON_PUSH_DISABLE_WARNINGS
|
||||||
|
SIMDJSON_DISABLE_DEPRECATED_WARNING // Validated CRT_SECURE safe here
|
||||||
strcpy(filename_copy, filename);
|
strcpy(filename_copy, filename);
|
||||||
|
SIMDJSON_POP_DISABLE_WARNINGS
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
char* base = ::basename(filename_copy);
|
char* base = ::basename(filename_copy);
|
||||||
#else
|
#else
|
||||||
@@ -432,21 +462,21 @@ struct benchmarker {
|
|||||||
base[strlen(base)-5] = '\0';
|
base[strlen(base)-5] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
double gb = json.size() / 1000000000.0;
|
double gb = static_cast<double>(json.size()) / 1000000000.0;
|
||||||
if (collector.has_events()) {
|
if (collector.has_events()) {
|
||||||
printf("\"%s\"\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n",
|
printf("\"%s\"\t%f\t%f\t%f\t%f\t%f\t%f\t%f\n",
|
||||||
base,
|
base,
|
||||||
allocate_stage.best.cycles() / json.size(),
|
allocate_stage.best.cycles() / static_cast<double>(json.size()),
|
||||||
stage1.best.cycles() / json.size(),
|
stage1.best.cycles() / static_cast<double>(json.size()),
|
||||||
stage2.best.cycles() / json.size(),
|
stage2.best.cycles() / static_cast<double>(json.size()),
|
||||||
all_stages.best.cycles() / json.size(),
|
all_stages_without_allocation.best.cycles() / static_cast<double>(json.size()),
|
||||||
gb / all_stages.best.elapsed_sec(),
|
gb / all_stages_without_allocation.best.elapsed_sec(),
|
||||||
gb / stage1.best.elapsed_sec(),
|
gb / stage1.best.elapsed_sec(),
|
||||||
gb / stage2.best.elapsed_sec());
|
gb / stage2.best.elapsed_sec());
|
||||||
} else {
|
} else {
|
||||||
printf("\"%s\"\t\t\t\t\t%f\t%f\t%f\n",
|
printf("\"%s\"\t\t\t\t\t%f\t%f\t%f\n",
|
||||||
base,
|
base,
|
||||||
gb / all_stages.best.elapsed_sec(),
|
gb / all_stages_without_allocation.best.elapsed_sec(),
|
||||||
gb / stage1.best.elapsed_sec(),
|
gb / stage1.best.elapsed_sec(),
|
||||||
gb / stage2.best.elapsed_sec());
|
gb / stage2.best.elapsed_sec());
|
||||||
}
|
}
|
||||||
@@ -455,28 +485,28 @@ struct benchmarker {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
printf("%s\n", filename);
|
printf("%s\n", filename);
|
||||||
printf("%s\n", string(strlen(filename), '=').c_str());
|
printf("%s\n", string(strlen(filename), '=').c_str());
|
||||||
printf("%9zu blocks - %10zu bytes - %5zu structurals (%5.1f %%)\n", stats->bytes / BYTES_PER_BLOCK, stats->bytes, stats->structurals, 100.0 * stats->structurals / stats->bytes);
|
printf("%9zu blocks - %10zu bytes - %5zu structurals (%5.1f %%)\n", stats->bytes / BYTES_PER_BLOCK, stats->bytes, stats->structurals, percent(stats->structurals, stats->bytes));
|
||||||
if (stats) {
|
if (stats) {
|
||||||
printf("special blocks with: utf8 %9zu (%5.1f %%) - escape %9zu (%5.1f %%) - 0 structurals %9zu (%5.1f %%) - 1+ structurals %9zu (%5.1f %%) - 8+ structurals %9zu (%5.1f %%) - 16+ structurals %9zu (%5.1f %%)\n",
|
printf("special blocks with: utf8 %9zu (%5.1f %%) - escape %9zu (%5.1f %%) - 0 structurals %9zu (%5.1f %%) - 1+ structurals %9zu (%5.1f %%) - 8+ structurals %9zu (%5.1f %%) - 16+ structurals %9zu (%5.1f %%)\n",
|
||||||
stats->blocks_with_utf8, 100.0 * stats->blocks_with_utf8 / stats->blocks,
|
stats->blocks_with_utf8, percent(stats->blocks_with_utf8, stats->blocks),
|
||||||
stats->blocks_with_escapes, 100.0 * stats->blocks_with_escapes / stats->blocks,
|
stats->blocks_with_escapes, percent(stats->blocks_with_escapes, stats->blocks),
|
||||||
stats->blocks_with_0_structurals, 100.0 * stats->blocks_with_0_structurals / stats->blocks,
|
stats->blocks_with_0_structurals, percent(stats->blocks_with_0_structurals, stats->blocks),
|
||||||
stats->blocks_with_1_structural, 100.0 * stats->blocks_with_1_structural / stats->blocks,
|
stats->blocks_with_1_structural, percent(stats->blocks_with_1_structural, stats->blocks),
|
||||||
stats->blocks_with_8_structurals, 100.0 * stats->blocks_with_8_structurals / stats->blocks,
|
stats->blocks_with_8_structurals, percent(stats->blocks_with_8_structurals, stats->blocks),
|
||||||
stats->blocks_with_16_structurals, 100.0 * stats->blocks_with_16_structurals / stats->blocks);
|
stats->blocks_with_16_structurals, percent(stats->blocks_with_16_structurals, stats->blocks));
|
||||||
printf("special block flips: utf8 %9zu (%5.1f %%) - escape %9zu (%5.1f %%) - 0 structurals %9zu (%5.1f %%) - 1+ structurals %9zu (%5.1f %%) - 8+ structurals %9zu (%5.1f %%) - 16+ structurals %9zu (%5.1f %%)\n",
|
printf("special block flips: utf8 %9zu (%5.1f %%) - escape %9zu (%5.1f %%) - 0 structurals %9zu (%5.1f %%) - 1+ structurals %9zu (%5.1f %%) - 8+ structurals %9zu (%5.1f %%) - 16+ structurals %9zu (%5.1f %%)\n",
|
||||||
stats->blocks_with_utf8_flipped, 100.0 * stats->blocks_with_utf8_flipped / stats->blocks,
|
stats->blocks_with_utf8_flipped, percent(stats->blocks_with_utf8_flipped, stats->blocks),
|
||||||
stats->blocks_with_escapes_flipped, 100.0 * stats->blocks_with_escapes_flipped / stats->blocks,
|
stats->blocks_with_escapes_flipped, percent(stats->blocks_with_escapes_flipped, stats->blocks),
|
||||||
stats->blocks_with_0_structurals_flipped, 100.0 * stats->blocks_with_0_structurals_flipped / stats->blocks,
|
stats->blocks_with_0_structurals_flipped, percent(stats->blocks_with_0_structurals_flipped, stats->blocks),
|
||||||
stats->blocks_with_1_structural_flipped, 100.0 * stats->blocks_with_1_structural_flipped / stats->blocks,
|
stats->blocks_with_1_structural_flipped, percent(stats->blocks_with_1_structural_flipped, stats->blocks),
|
||||||
stats->blocks_with_8_structurals_flipped, 100.0 * stats->blocks_with_8_structurals_flipped / stats->blocks,
|
stats->blocks_with_8_structurals_flipped, percent(stats->blocks_with_8_structurals_flipped, stats->blocks),
|
||||||
stats->blocks_with_16_structurals_flipped, 100.0 * stats->blocks_with_16_structurals_flipped / stats->blocks);
|
stats->blocks_with_16_structurals_flipped, percent(stats->blocks_with_16_structurals_flipped, stats->blocks));
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("All Stages\n");
|
printf("All Stages (excluding allocation)\n");
|
||||||
print_aggregate("| " , all_stages.best);
|
print_aggregate("| " , all_stages_without_allocation.best);
|
||||||
// frequently, allocation is a tiny fraction of the running time so we omit it
|
// frequently, allocation is a tiny fraction of the running time so we omit it
|
||||||
if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages.best.elapsed_sec()) {
|
if(allocate_stage.best.elapsed_sec() > 0.01 * all_stages_without_allocation.best.elapsed_sec()) {
|
||||||
printf("|- Allocation\n");
|
printf("|- Allocation\n");
|
||||||
print_aggregate("| ", allocate_stage.best);
|
print_aggregate("| ", allocate_stage.best);
|
||||||
}
|
}
|
||||||
@@ -487,17 +517,16 @@ struct benchmarker {
|
|||||||
if (collector.has_events()) {
|
if (collector.has_events()) {
|
||||||
double freq1 = (stage1.best.cycles() / stage1.best.elapsed_sec()) / 1000000000.0;
|
double freq1 = (stage1.best.cycles() / stage1.best.elapsed_sec()) / 1000000000.0;
|
||||||
double freq2 = (stage2.best.cycles() / stage2.best.elapsed_sec()) / 1000000000.0;
|
double freq2 = (stage2.best.cycles() / stage2.best.elapsed_sec()) / 1000000000.0;
|
||||||
double freqall = (all_stages.best.cycles() / all_stages.best.elapsed_sec()) / 1000000000.0;
|
double freqall = (all_stages_without_allocation.best.cycles() / all_stages_without_allocation.best.elapsed_sec()) / 1000000000.0;
|
||||||
double freqmin = std::min(freq1, freq2);
|
double freqmin = min(freq1, freq2);
|
||||||
double freqmax = std::max(freq1, freq2);
|
double freqmax = max(freq1, freq2);
|
||||||
if((freqall < 0.95 * freqmin) or (freqall > 1.05 * freqmax)) {
|
if((freqall < 0.95 * freqmin) or (freqall > 1.05 * freqmax)) {
|
||||||
printf("\nWarning: The processor frequency fluctuates in an expected way!!!\n"
|
printf("\nWarning: The processor frequency fluctuates in an expected way!!!\n"
|
||||||
"Expect the overall speed not to match stage 1 and stage 2 speeds.\n"
|
|
||||||
"Range for stage 1 and stage 2 : [%.3f GHz, %.3f GHz], overall: %.3f GHz.\n",
|
"Range for stage 1 and stage 2 : [%.3f GHz, %.3f GHz], overall: %.3f GHz.\n",
|
||||||
freqmin, freqmax, freqall);
|
freqmin, freqmax, freqall);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("\n%.1f documents parsed per second\n", iterations/loop.best.elapsed_sec());
|
printf("\n%.1f documents parsed per second (best)\n", 1.0/static_cast<double>(all_stages_without_allocation.best.elapsed_sec()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "json_benchmark/file_runner.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
using namespace json_benchmark;
|
||||||
|
|
||||||
|
template<typename I>
|
||||||
|
struct runner : public file_runner<I> {
|
||||||
|
std::vector<uint64_t> result{};
|
||||||
|
|
||||||
|
bool setup(benchmark::State &state) {
|
||||||
|
return this->load_json(state, TWITTER_JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool before_run(benchmark::State &state) {
|
||||||
|
if (!file_runner<I>::before_run(state)) { return false; }
|
||||||
|
result.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(benchmark::State &) {
|
||||||
|
return this->implementation.run(this->json, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool after_run(benchmark::State &state) {
|
||||||
|
if (!file_runner<I>::after_run(state)) { return false; }
|
||||||
|
std::sort(result.begin(), result.end());
|
||||||
|
auto last = std::unique(result.begin(), result.end());
|
||||||
|
result.erase(last, result.end());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R>
|
||||||
|
bool diff(benchmark::State &state, runner<R> &reference) {
|
||||||
|
return diff_results(state, result, reference.result, diff_flags::NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t items_per_iteration() {
|
||||||
|
return result.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct simdjson_dom;
|
||||||
|
|
||||||
|
template<typename I> simdjson_inline static void distinct_user_id(benchmark::State &state) {
|
||||||
|
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace distinct_user_id
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
struct nlohmann_json {
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
|
||||||
|
for (auto tweet : root["statuses"]) {
|
||||||
|
result.push_back(tweet["user"]["id"]);
|
||||||
|
if (tweet.contains("retweeted_status")) {
|
||||||
|
result.push_back(tweet["retweeted_status"]["user"]["id"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, nlohmann_json)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
struct nlohmann_json_sax {
|
||||||
|
struct Handler : json::json_sax_t
|
||||||
|
{
|
||||||
|
std::vector<uint64_t>& result;
|
||||||
|
bool user = false;
|
||||||
|
bool user_id = false;
|
||||||
|
Handler(std::vector<uint64_t> &r) : result(r) { }
|
||||||
|
|
||||||
|
bool key(string_t& val) override {
|
||||||
|
// Assume that valid user/id pairs appear only once in main array of user objects
|
||||||
|
if (user) { // If already found user object, find id key
|
||||||
|
if (val.compare("id") == 0) { user_id = true; }
|
||||||
|
}
|
||||||
|
else if (val.compare("user") == 0) { user = true; } // Otherwise, find user object
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool number_unsigned(number_unsigned_t val) override {
|
||||||
|
if (user_id) {
|
||||||
|
result.emplace_back(val);
|
||||||
|
user = false;
|
||||||
|
user_id = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Irrelevant events
|
||||||
|
bool null() override { return true; }
|
||||||
|
bool boolean(bool val) override { return true; }
|
||||||
|
bool number_float(number_float_t val, const string_t& s) override { return true; }
|
||||||
|
bool number_integer(number_integer_t val) override { return true; }
|
||||||
|
bool string(string_t& val) override { return true; }
|
||||||
|
bool start_object(std::size_t elements) override { return true; }
|
||||||
|
bool end_object() override { return true; }
|
||||||
|
bool start_array(std::size_t elements) override { return true; }
|
||||||
|
bool end_array() override { return true; }
|
||||||
|
bool binary(json::binary_t& val) override { return true; }
|
||||||
|
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { return false; }
|
||||||
|
}; // Handler
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
Handler handler(result);
|
||||||
|
json::sax_parse(json.data(), &handler);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}; // nlohmann_json_sax
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, nlohmann_json_sax)->UseManualTime();
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
struct rapidjson_base {
|
||||||
|
Document doc{};
|
||||||
|
|
||||||
|
bool run(Document &root, std::vector<uint64_t> &result) {
|
||||||
|
if (root.HasParseError()) { printf("parse error\n"); return false; }
|
||||||
|
if (!root.IsObject()) { printf("root is not an object\n"); return false; }
|
||||||
|
auto statuses = root.FindMember("statuses");
|
||||||
|
if (statuses == root.MemberEnd() || !statuses->value.IsArray()) { printf("statuses is not an array\n"); return false; }
|
||||||
|
for (auto &tweet : statuses->value.GetArray()) {
|
||||||
|
if (!tweet.IsObject()) { return false; }
|
||||||
|
auto user = tweet.FindMember("user");
|
||||||
|
if (user == tweet.MemberEnd() || !user->value.IsObject()) { printf("user is not an object\n"); return false; }
|
||||||
|
auto id = user->value.FindMember("id");
|
||||||
|
if (id == user->value.MemberEnd() || !id->value.IsUint64()) { printf("id is not an int\n"); return false; }
|
||||||
|
result.push_back(id->value.GetUint64());
|
||||||
|
|
||||||
|
auto retweet = tweet.FindMember("retweeted_status");
|
||||||
|
if (retweet != tweet.MemberEnd()) {
|
||||||
|
if (!retweet->value.IsObject()) { printf("retweet is not an object\n"); return false; }
|
||||||
|
user = retweet->value.FindMember("user");
|
||||||
|
if (user == retweet->value.MemberEnd() || !user->value.IsObject()) { printf("rewtweet.user is not an object\n"); return false; }
|
||||||
|
id = user->value.FindMember("id");
|
||||||
|
if (id == user->value.MemberEnd() || !id->value.IsUint64()) { printf("retweet.id is not an int\n"); return false; }
|
||||||
|
result.push_back(id->value.GetUint64());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rapidjson : rapidjson_base {
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag>(json.data()), result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, rapidjson)->UseManualTime();
|
||||||
|
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
struct rapidjson_insitu : rapidjson_base {
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag|kParseInsituFlag>(json.data()), result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, rapidjson_insitu)->UseManualTime();
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
} // namespace partial_tweets
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
#include <string.h>
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
struct rapidjson_sax {
|
||||||
|
struct Handler {
|
||||||
|
std::vector<uint64_t>& result;
|
||||||
|
bool user = false;
|
||||||
|
bool user_id = false;
|
||||||
|
Handler(std::vector<uint64_t> &r) : result(r) { }
|
||||||
|
|
||||||
|
bool Key(const char* key, SizeType length, bool copy) {
|
||||||
|
// Assume that valid user/id pairs appear only once in main array of user objects
|
||||||
|
if (user) { // If already found user object, find id key
|
||||||
|
if ((length == 2) && memcmp(key,"id",2) == 0) { user_id = true; }
|
||||||
|
}
|
||||||
|
else if ((length == 4) && memcmp(key,"user",4) == 0) { user = true; } // Otherwise, find user object
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool Uint(unsigned i) { // id values are treated as Uint (not Uint64) by the reader
|
||||||
|
if (user_id) { // Getting id if previous key was "id" for a user
|
||||||
|
result.emplace_back(i);
|
||||||
|
user_id = false;
|
||||||
|
user = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Irrelevant events
|
||||||
|
bool Null() { return true; }
|
||||||
|
bool Bool(bool b) { return true; }
|
||||||
|
bool Double(double d) { return true; }
|
||||||
|
bool Int(int i) { return true; }
|
||||||
|
bool Int64(int64_t i) { return true; }
|
||||||
|
bool Uint64(uint64_t i) { return true; }
|
||||||
|
bool RawNumber(const char* str, SizeType length, bool copy) { return true; }
|
||||||
|
bool String(const char* str, SizeType length, bool copy) { return true; }
|
||||||
|
bool StartObject() { return true; }
|
||||||
|
bool EndObject(SizeType memberCount) { return true; }
|
||||||
|
bool StartArray() { return true; }
|
||||||
|
bool EndArray(SizeType elementCount) { return true; }
|
||||||
|
}; // handler
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
Reader reader;
|
||||||
|
Handler handler(result);
|
||||||
|
InsituStringStream ss(json.data());
|
||||||
|
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag | kParseFullPrecisionFlag>(ss,handler);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // rapid_jason_sax
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, rapidjson_sax)->UseManualTime();
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_SAJSON
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
struct sajson {
|
||||||
|
size_t ast_buffer_size{0};
|
||||||
|
size_t *ast_buffer{nullptr};
|
||||||
|
~sajson() { free(ast_buffer); }
|
||||||
|
|
||||||
|
simdjson_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
|
||||||
|
auto val = obj.get_value_of_key({key.data(), key.length()});
|
||||||
|
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
|
||||||
|
return { val.as_cstring(), val.get_string_length() };
|
||||||
|
}
|
||||||
|
simdjson_inline uint64_t get_str_uint64(const ::sajson::value &obj, std::string_view key) {
|
||||||
|
// Since sajson only supports 53-bit numbers, and IDs in twitter.json can be > 53 bits, we read the corresponding id_str and parse that.
|
||||||
|
auto val = obj.get_value_of_key({key.data(), key.length()});
|
||||||
|
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field not a string"; }
|
||||||
|
auto str = val.as_cstring();
|
||||||
|
char *endptr;
|
||||||
|
uint64_t result = strtoull(str, &endptr, 10);
|
||||||
|
if (endptr != &str[val.get_string_length()]) { throw "field is a string, but not an integer string"; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
using namespace sajson;
|
||||||
|
if (!ast_buffer) {
|
||||||
|
ast_buffer_size = json.size();
|
||||||
|
ast_buffer = (size_t *)std::malloc(ast_buffer_size * sizeof(size_t));
|
||||||
|
}
|
||||||
|
auto doc = parse(
|
||||||
|
bounded_allocation(ast_buffer, ast_buffer_size),
|
||||||
|
mutable_string_view(json.size(), json.data())
|
||||||
|
);
|
||||||
|
if (!doc.is_valid()) { return false; }
|
||||||
|
|
||||||
|
auto root = doc.get_root();
|
||||||
|
if (root.get_type() != TYPE_OBJECT) { return false; }
|
||||||
|
auto statuses = root.get_value_of_key({"statuses", strlen("statuses")});
|
||||||
|
if (statuses.get_type() != TYPE_ARRAY) { return false; }
|
||||||
|
|
||||||
|
for (size_t i=0; i<statuses.get_length(); i++) {
|
||||||
|
auto tweet = statuses.get_array_element(i);
|
||||||
|
|
||||||
|
// get tweet.user.id
|
||||||
|
if (tweet.get_type() != TYPE_OBJECT) { return false; }
|
||||||
|
auto user = tweet.get_value_of_key({"user", strlen("user")});
|
||||||
|
if (user.get_type() != TYPE_OBJECT) { return false; }
|
||||||
|
result.push_back(get_str_uint64(user, "id_str"));
|
||||||
|
|
||||||
|
// get tweet.retweeted_status.user.id
|
||||||
|
auto retweet = tweet.get_value_of_key({"retweeted_status", strlen("retweeted_status")});
|
||||||
|
switch (retweet.get_type()) {
|
||||||
|
case TYPE_OBJECT: {
|
||||||
|
auto retweet_user = retweet.get_value_of_key({"user", strlen("user")});
|
||||||
|
if (retweet_user.get_type() != TYPE_OBJECT) { return false; }
|
||||||
|
result.push_back(get_str_uint64(retweet_user, "id_str"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TODO distinguish null and missing. null is bad. missing is fine.
|
||||||
|
case TYPE_NULL:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, sajson)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAJSON
|
||||||
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
struct simdjson_dom {
|
||||||
|
dom::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
// Walk the document, parsing as we go
|
||||||
|
auto doc = parser.parse(json);
|
||||||
|
for (dom::object tweet : doc["statuses"]) {
|
||||||
|
// We believe that all statuses have a matching
|
||||||
|
// user, and we are willing to throw when they do not.
|
||||||
|
result.push_back(tweet["user"]["id"]);
|
||||||
|
// Not all tweets have a "retweeted_status", but when they do
|
||||||
|
// we want to go and find the user within.
|
||||||
|
auto retweet = tweet["retweeted_status"];
|
||||||
|
if (retweet.error() != NO_SUCH_FIELD) {
|
||||||
|
result.push_back(retweet["user"]["id"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_dom)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
struct simdjson_dom_json_pointer {
|
||||||
|
dom::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
// Walk the document, parsing as we go
|
||||||
|
auto doc = parser.parse(json);
|
||||||
|
for (dom::object tweet : doc["statuses"]) {
|
||||||
|
// We believe that all statuses have a matching
|
||||||
|
// user, and we are willing to throw when they do not.
|
||||||
|
result.push_back(tweet.at_pointer("/user/id"));
|
||||||
|
// Not all tweets have a "retweeted_status", but when they do
|
||||||
|
// we want to go and find the user within.
|
||||||
|
auto retweet_id = tweet.at_pointer("/retweeted_status/user/id");
|
||||||
|
if (retweet_id.error() != NO_SUCH_FIELD) {
|
||||||
|
result.push_back(retweet_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_dom_json_pointer)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
struct simdjson_ondemand {
|
||||||
|
ondemand::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
// Walk the document, parsing as we go
|
||||||
|
auto doc = parser.iterate(json);
|
||||||
|
for (ondemand::object tweet : doc.find_field("statuses")) {
|
||||||
|
// We believe that all statuses have a matching
|
||||||
|
// user, and we are willing to throw when they do not.
|
||||||
|
result.push_back(tweet.find_field("user").find_field("id"));
|
||||||
|
// Not all tweets have a "retweeted_status", but when they do
|
||||||
|
// we want to go and find the user within.
|
||||||
|
auto retweet = tweet.find_field("retweeted_status");
|
||||||
|
if (!retweet.error()) {
|
||||||
|
result.push_back(retweet.find_field("user").find_field("id"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_ondemand)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
struct simdjson_ondemand_json_pointer {
|
||||||
|
ondemand::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
// Walk the document, parsing as we go
|
||||||
|
auto doc = parser.iterate(json);
|
||||||
|
for (ondemand::object tweet : doc.find_field("statuses")) {
|
||||||
|
// We believe that all statuses have a matching
|
||||||
|
// user, and we are willing to throw when they do not.
|
||||||
|
result.push_back(tweet.at_pointer("/user/id"));
|
||||||
|
// Not all tweets have a "retweeted_status", but when they do
|
||||||
|
// we want to go and find the user within.
|
||||||
|
auto retweet_id = tweet.at_pointer("/retweeted_status/user/id");
|
||||||
|
if (retweet_id.error() != NO_SUCH_FIELD) {
|
||||||
|
result.push_back(retweet_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, simdjson_ondemand_json_pointer)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
|
||||||
|
#include "distinct_user_id.h"
|
||||||
|
|
||||||
|
namespace distinct_user_id {
|
||||||
|
|
||||||
|
struct yyjson_base {
|
||||||
|
bool run(yyjson_doc *doc, std::vector<uint64_t> &result) {
|
||||||
|
if (!doc) { return false; }
|
||||||
|
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||||
|
if (!yyjson_is_obj(root)) { return false; }
|
||||||
|
yyjson_val *statuses = yyjson_obj_get(root, "statuses");
|
||||||
|
if (!yyjson_is_arr(statuses)) { return false; }
|
||||||
|
|
||||||
|
// Walk the document, parsing the tweets as we go
|
||||||
|
size_t tweet_idx, tweets_max;
|
||||||
|
yyjson_val *tweet;
|
||||||
|
yyjson_arr_foreach(statuses, tweet_idx, tweets_max, tweet) {
|
||||||
|
auto user = yyjson_obj_get(tweet, "user");
|
||||||
|
if (!yyjson_is_obj(user)) { return false; }
|
||||||
|
auto id = yyjson_obj_get(user, "id");
|
||||||
|
if (!yyjson_is_uint(id)) { return false; }
|
||||||
|
result.push_back(yyjson_get_uint(id));
|
||||||
|
|
||||||
|
// Not all tweets have a "retweeted_status", but when they do
|
||||||
|
// we want to go and find the user within.
|
||||||
|
auto retweet = yyjson_obj_get(tweet, "retweeted_status");
|
||||||
|
if (retweet) {
|
||||||
|
if (!yyjson_is_obj(retweet)) { return false; }
|
||||||
|
user = yyjson_obj_get(retweet, "user");
|
||||||
|
if (!yyjson_is_obj(user)) { return false; }
|
||||||
|
id = yyjson_obj_get(user, "id");
|
||||||
|
if (!yyjson_is_uint(id)) { return false; }
|
||||||
|
result.push_back(yyjson_get_sint(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct yyjson : yyjson_base {
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, yyjson)->UseManualTime();
|
||||||
|
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
struct yyjson_insitu : yyjson_base {
|
||||||
|
bool run(simdjson::padded_string &json, std::vector<uint64_t> &result) {
|
||||||
|
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(distinct_user_id, yyjson_insitu)->UseManualTime();
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
} // namespace distinct_user_id
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||||
@@ -1,392 +0,0 @@
|
|||||||
#include "simdjson.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "benchmark.h"
|
|
||||||
|
|
||||||
SIMDJSON_PUSH_DISABLE_ALL_WARNINGS
|
|
||||||
|
|
||||||
// #define RAPIDJSON_SSE2 // bad for performance
|
|
||||||
// #define RAPIDJSON_SSE42 // bad for performance
|
|
||||||
#include "rapidjson/document.h"
|
|
||||||
#include "rapidjson/reader.h"
|
|
||||||
#include "rapidjson/stringbuffer.h"
|
|
||||||
#include "rapidjson/writer.h"
|
|
||||||
|
|
||||||
#include "sajson.h"
|
|
||||||
|
|
||||||
SIMDJSON_POP_DISABLE_WARNINGS
|
|
||||||
|
|
||||||
using namespace rapidjson;
|
|
||||||
|
|
||||||
bool equals(const char *s1, const char *s2) { return strcmp(s1, s2) == 0; }
|
|
||||||
|
|
||||||
void remove_duplicates(std::vector<int64_t> &v) {
|
|
||||||
std::sort(v.begin(), v.end());
|
|
||||||
auto last = std::unique(v.begin(), v.end());
|
|
||||||
v.erase(last, v.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_vec(const std::vector<int64_t> &v) {
|
|
||||||
for (auto i : v) {
|
|
||||||
std::cout << i << " ";
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
|
|
||||||
// simdjson_recurse below come be implemented like so but it is slow:
|
|
||||||
/*void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element) {
|
|
||||||
if (element.is<simdjson::dom::array>()) {
|
|
||||||
auto [array, array_error] = element.get<simdjson::dom::array>();
|
|
||||||
for (auto child : array) {
|
|
||||||
if (child.is<simdjson::dom::array>() || child.is<simdjson::dom::object>()) {
|
|
||||||
simdjson_recurse(v, child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (element.is<simdjson::dom::object>()) {
|
|
||||||
auto [object, error] = element.get<simdjson::dom::object>();
|
|
||||||
int64_t id;
|
|
||||||
object["user"]["id"].get<int64_t>().tie(id,error);
|
|
||||||
if(!error) {
|
|
||||||
v.push_back(id);
|
|
||||||
}
|
|
||||||
for (auto [key, value] : object) {
|
|
||||||
if (value.is<simdjson::dom::array>() || value.is<simdjson::dom::object>()) {
|
|
||||||
simdjson_recurse(v, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
|
|
||||||
void simdjson_recurse(std::vector<int64_t> & v, simdjson::dom::element element) {
|
|
||||||
if (element.is<simdjson::dom::array>()) {
|
|
||||||
auto array = element.get<simdjson::dom::array>();
|
|
||||||
for (auto child : array) {
|
|
||||||
if (child.is<simdjson::dom::array>() || child.is<simdjson::dom::object>()) {
|
|
||||||
simdjson_recurse(v, child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (element.is<simdjson::dom::object>()) {
|
|
||||||
auto object = element.get<simdjson::dom::object>();
|
|
||||||
for (auto [key, value] : object) {
|
|
||||||
if((key.size() == 4) && (memcmp(key.data(), "user", 4) == 0)) {
|
|
||||||
// we are in an object under the key "user"
|
|
||||||
if(value.is<simdjson::dom::object>()) {
|
|
||||||
auto child_object = value.get<simdjson::dom::object>();
|
|
||||||
for (auto [child_key, child_value] : child_object) {
|
|
||||||
if((child_key.size() == 2) && (memcmp(child_key.data(), "id", 2) == 0)) {
|
|
||||||
if(child_value.is<int64_t>()) {
|
|
||||||
v.push_back(child_value.get<int64_t>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (child_value.is<simdjson::dom::array>() || child_value.is<simdjson::dom::object>()) {
|
|
||||||
simdjson_recurse(v, child_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (value.is<simdjson::dom::array>()) {
|
|
||||||
simdjson_recurse(v, value);
|
|
||||||
}
|
|
||||||
// end of: we are in an object under the key "user"
|
|
||||||
} else if (value.is<simdjson::dom::array>() || value.is<simdjson::dom::object>()) {
|
|
||||||
simdjson_recurse(v, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) std::vector<int64_t>
|
|
||||||
simdjson_just_dom(simdjson::dom::element doc) {
|
|
||||||
std::vector<int64_t> answer;
|
|
||||||
simdjson_recurse(answer, doc);
|
|
||||||
remove_duplicates(answer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) std::vector<int64_t>
|
|
||||||
simdjson_compute_stats(const simdjson::padded_string &p) {
|
|
||||||
std::vector<int64_t> answer;
|
|
||||||
simdjson::dom::parser parser;
|
|
||||||
simdjson::dom::element doc = parser.parse(p);
|
|
||||||
simdjson_recurse(answer, doc);
|
|
||||||
remove_duplicates(answer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) simdjson::error_code
|
|
||||||
simdjson_just_parse(const simdjson::padded_string &p) {
|
|
||||||
simdjson::dom::parser parser;
|
|
||||||
return parser.parse(p).error();
|
|
||||||
}
|
|
||||||
|
|
||||||
void sajson_traverse(std::vector<int64_t> &answer, const sajson::value &node) {
|
|
||||||
using namespace sajson;
|
|
||||||
switch (node.get_type()) {
|
|
||||||
case TYPE_ARRAY: {
|
|
||||||
auto length = node.get_length();
|
|
||||||
for (size_t i = 0; i < length; ++i) {
|
|
||||||
sajson_traverse(answer, node.get_array_element(i));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TYPE_OBJECT: {
|
|
||||||
auto length = node.get_length();
|
|
||||||
// sajson has O(log n) find_object_key, but we still visit each node anyhow
|
|
||||||
// because we need to visit all values.
|
|
||||||
for (auto i = 0u; i < length; ++i) {
|
|
||||||
auto key = node.get_object_key(i); // expected: sajson::string
|
|
||||||
bool found_user =
|
|
||||||
(key.length() == 4) && (memcmp(key.data(), "user", 4) == 0);
|
|
||||||
if (found_user) { // found a user!!!
|
|
||||||
auto user_value = node.get_object_value(i); // get the value
|
|
||||||
if (user_value.get_type() ==
|
|
||||||
TYPE_OBJECT) { // the value should be an object
|
|
||||||
// now we know that we only need one value
|
|
||||||
auto user_value_length = user_value.get_length();
|
|
||||||
auto right_index =
|
|
||||||
user_value.find_object_key(sajson::string("id", 2));
|
|
||||||
if (right_index < user_value_length) {
|
|
||||||
auto v = user_value.get_object_value(right_index);
|
|
||||||
if (v.get_type() == TYPE_INTEGER) { // check that it is an integer
|
|
||||||
answer.push_back(v.get_integer_value()); // record it!
|
|
||||||
} else if (v.get_type() == TYPE_DOUBLE) {
|
|
||||||
answer.push_back((int64_t)v.get_double_value()); // record it!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sajson_traverse(answer, node.get_object_value(i));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TYPE_NULL:
|
|
||||||
case TYPE_FALSE:
|
|
||||||
case TYPE_TRUE:
|
|
||||||
case TYPE_STRING:
|
|
||||||
case TYPE_DOUBLE:
|
|
||||||
case TYPE_INTEGER:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false && "unknown node type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) std::vector<int64_t>
|
|
||||||
sasjon_just_dom(sajson::document &d) {
|
|
||||||
std::vector<int64_t> answer;
|
|
||||||
sajson_traverse(answer, d.get_root());
|
|
||||||
remove_duplicates(answer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) std::vector<int64_t>
|
|
||||||
sasjon_compute_stats(const simdjson::padded_string &p) {
|
|
||||||
std::vector<int64_t> answer;
|
|
||||||
char *buffer = (char *)malloc(p.size());
|
|
||||||
memcpy(buffer, p.data(), p.size());
|
|
||||||
auto d = sajson::parse(sajson::dynamic_allocation(),
|
|
||||||
sajson::mutable_string_view(p.size(), buffer));
|
|
||||||
if (!d.is_valid()) {
|
|
||||||
free(buffer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
sajson_traverse(answer, d.get_root());
|
|
||||||
free(buffer);
|
|
||||||
remove_duplicates(answer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) bool
|
|
||||||
sasjon_just_parse(const simdjson::padded_string &p) {
|
|
||||||
char *buffer = (char *)malloc(p.size());
|
|
||||||
memcpy(buffer, p.data(), p.size());
|
|
||||||
auto d = sajson::parse(sajson::dynamic_allocation(),
|
|
||||||
sajson::mutable_string_view(p.size(), buffer));
|
|
||||||
bool answer = !d.is_valid();
|
|
||||||
free(buffer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void rapid_traverse(std::vector<int64_t> &answer, const rapidjson::Value &v) {
|
|
||||||
switch (v.GetType()) {
|
|
||||||
case kObjectType:
|
|
||||||
for (Value::ConstMemberIterator m = v.MemberBegin(); m != v.MemberEnd();
|
|
||||||
++m) {
|
|
||||||
bool found_user = (m->name.GetStringLength() == 4) &&
|
|
||||||
(memcmp(m->name.GetString(), "user", 4) == 0);
|
|
||||||
if (found_user) {
|
|
||||||
const rapidjson::Value &child = m->value;
|
|
||||||
if (child.GetType() == kObjectType) {
|
|
||||||
for (Value::ConstMemberIterator k = child.MemberBegin();
|
|
||||||
k != child.MemberEnd(); ++k) {
|
|
||||||
if (equals(k->name.GetString(), "id")) {
|
|
||||||
const rapidjson::Value &val = k->value;
|
|
||||||
if (val.GetType() == kNumberType) {
|
|
||||||
answer.push_back(val.GetInt64());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rapid_traverse(answer, m->value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kArrayType:
|
|
||||||
for (Value::ConstValueIterator i = v.Begin(); i != v.End();
|
|
||||||
++i) { // v.Size();
|
|
||||||
rapid_traverse(answer, *i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kNullType:
|
|
||||||
case kFalseType:
|
|
||||||
case kTrueType:
|
|
||||||
case kStringType:
|
|
||||||
case kNumberType:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) std::vector<int64_t>
|
|
||||||
rapid_just_dom(rapidjson::Document &d) {
|
|
||||||
std::vector<int64_t> answer;
|
|
||||||
rapid_traverse(answer, d);
|
|
||||||
remove_duplicates(answer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) std::vector<int64_t>
|
|
||||||
rapid_compute_stats(const simdjson::padded_string &p) {
|
|
||||||
std::vector<int64_t> answer;
|
|
||||||
char *buffer = (char *)malloc(p.size() + 1);
|
|
||||||
memcpy(buffer, p.data(), p.size());
|
|
||||||
buffer[p.size()] = '\0';
|
|
||||||
rapidjson::Document d;
|
|
||||||
d.ParseInsitu<kParseValidateEncodingFlag>(buffer);
|
|
||||||
if (d.HasParseError()) {
|
|
||||||
free(buffer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
rapid_traverse(answer, d);
|
|
||||||
free(buffer);
|
|
||||||
remove_duplicates(answer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((noinline)) bool
|
|
||||||
rapid_just_parse(const simdjson::padded_string &p) {
|
|
||||||
char *buffer = (char *)malloc(p.size() + 1);
|
|
||||||
memcpy(buffer, p.data(), p.size());
|
|
||||||
buffer[p.size()] = '\0';
|
|
||||||
rapidjson::Document d;
|
|
||||||
d.ParseInsitu<kParseValidateEncodingFlag>(buffer);
|
|
||||||
bool answer = d.HasParseError();
|
|
||||||
free(buffer);
|
|
||||||
return answer;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
bool verbose = false;
|
|
||||||
bool just_data = false;
|
|
||||||
|
|
||||||
int c;
|
|
||||||
while ((c = getopt(argc, argv, "vt")) != -1)
|
|
||||||
switch (c) {
|
|
||||||
case 't':
|
|
||||||
just_data = true;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbose = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
if (optind >= argc) {
|
|
||||||
std::cerr
|
|
||||||
<< "Using different parsers, we compute the content statistics of "
|
|
||||||
"JSON documents."
|
|
||||||
<< std::endl;
|
|
||||||
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
|
|
||||||
std::cerr << "Or " << argv[0] << " -v <jsonfile>" << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
const char *filename = argv[optind];
|
|
||||||
if (optind + 1 < argc) {
|
|
||||||
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
auto [p, error] = simdjson::padded_string::load(filename);
|
|
||||||
if (error) {
|
|
||||||
std::cerr << "Could not load the file " << filename << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
std::cout << "Input has ";
|
|
||||||
if (p.size() > 1024 * 1024)
|
|
||||||
std::cout << p.size() / (1024 * 1024) << " MB ";
|
|
||||||
else if (p.size() > 1024)
|
|
||||||
std::cout << p.size() / 1024 << " KB ";
|
|
||||||
else
|
|
||||||
std::cout << p.size() << " B ";
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
std::vector<int64_t> s1 = simdjson_compute_stats(p);
|
|
||||||
if (verbose) {
|
|
||||||
printf("simdjson: ");
|
|
||||||
print_vec(s1);
|
|
||||||
}
|
|
||||||
std::vector<int64_t> s2 = rapid_compute_stats(p);
|
|
||||||
if (verbose) {
|
|
||||||
printf("rapid: ");
|
|
||||||
print_vec(s2);
|
|
||||||
}
|
|
||||||
std::vector<int64_t> s3 = sasjon_compute_stats(p);
|
|
||||||
if (verbose) {
|
|
||||||
printf("sasjon: ");
|
|
||||||
print_vec(s3);
|
|
||||||
}
|
|
||||||
assert(s1 == s2);
|
|
||||||
assert(s1 == s3);
|
|
||||||
size_t size = s1.size();
|
|
||||||
|
|
||||||
int repeat = 500;
|
|
||||||
int volume = p.size();
|
|
||||||
if (just_data) {
|
|
||||||
printf(
|
|
||||||
"name cycles_per_byte cycles_per_byte_err gb_per_s gb_per_s_err \n");
|
|
||||||
}
|
|
||||||
BEST_TIME("simdjson ", simdjson_compute_stats(p).size(), size, , repeat,
|
|
||||||
volume, !just_data);
|
|
||||||
BEST_TIME("rapid ", rapid_compute_stats(p).size(), size, , repeat, volume,
|
|
||||||
!just_data);
|
|
||||||
BEST_TIME("sasjon ", sasjon_compute_stats(p).size(), size, , repeat, volume,
|
|
||||||
!just_data);
|
|
||||||
BEST_TIME("simdjson (just parse) ", simdjson_just_parse(p), simdjson::error_code::SUCCESS, , repeat,
|
|
||||||
volume, !just_data);
|
|
||||||
BEST_TIME("rapid (just parse) ", rapid_just_parse(p), false, , repeat,
|
|
||||||
volume, !just_data);
|
|
||||||
BEST_TIME("sasjon (just parse) ", sasjon_just_parse(p), false, , repeat,
|
|
||||||
volume, !just_data);
|
|
||||||
simdjson::dom::parser parser;
|
|
||||||
simdjson::dom::element doc = parser.parse(p);
|
|
||||||
BEST_TIME("simdjson (just dom) ", simdjson_just_dom(doc).size(), size,
|
|
||||||
, repeat, volume, !just_data);
|
|
||||||
char *buffer = (char *)malloc(p.size() + 1);
|
|
||||||
buffer[p.size()] = '\0';
|
|
||||||
memcpy(buffer, p.data(), p.size());
|
|
||||||
rapidjson::Document drapid;
|
|
||||||
drapid.ParseInsitu<kParseValidateEncodingFlag>(buffer);
|
|
||||||
BEST_TIME("rapid (just dom) ", rapid_just_dom(drapid).size(), size, , repeat,
|
|
||||||
volume, !just_data);
|
|
||||||
memcpy(buffer, p.data(), p.size());
|
|
||||||
auto dsasjon = sajson::parse(sajson::dynamic_allocation(),
|
|
||||||
sajson::mutable_string_view(p.size(), buffer));
|
|
||||||
BEST_TIME("sasjon (just dom) ", sasjon_just_dom(dsasjon).size(), size, ,
|
|
||||||
repeat, volume, !just_data);
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
include_directories( .. ../linux )
|
||||||
|
link_libraries(simdjson-windows-headers test-data)
|
||||||
|
link_libraries(simdjson)
|
||||||
|
|
||||||
|
add_executable(perfdiff perfdiff.cpp)
|
||||||
|
add_executable(parse parse.cpp)
|
||||||
|
add_executable(parse_stream parse_stream.cpp)
|
||||||
|
add_executable(statisticalmodel statisticalmodel.cpp)
|
||||||
|
|
||||||
|
add_executable(parse_noutf8validation parse.cpp)
|
||||||
|
target_compile_definitions(parse_noutf8validation PRIVATE SIMDJSON_SKIPUTF8VALIDATION)
|
||||||
|
add_executable(parse_nonumberparsing parse.cpp)
|
||||||
|
target_compile_definitions(parse_nonumberparsing PRIVATE SIMDJSON_SKIPNUMBERPARSING)
|
||||||
|
add_executable(parse_nostringparsing parse.cpp)
|
||||||
|
target_compile_definitions(parse_nostringparsing PRIVATE SIMDJSON_SKIPSTRINGPARSING)
|
||||||
|
include(checkperf.cmake)
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
# Relevant targets:
|
||||||
|
# checkperf-parse: builds the reference checkperf-parse, syncing reference repository if needed
|
||||||
|
# checkperf: builds the targets needed for checkperf (parse, perfdiff, checkperf-parse)
|
||||||
|
# update-checkperf-repo: updates the reference repository we're checking performance against
|
||||||
|
# checkperf-repo: initialize and sync reference repository (first time only)
|
||||||
|
# TEST checkperf: runs the actual checkperf test
|
||||||
|
|
||||||
|
option(SIMDJSON_ENABLE_DOM_CHECKPERF "Enable DOM performance comparison with main branch" OFF)
|
||||||
|
|
||||||
|
|
||||||
|
# Clone the repository if it's not there
|
||||||
|
find_package(Git QUIET)
|
||||||
|
if (SIMDJSON_ENABLE_DOM_CHECKPERF AND Git_FOUND AND (GIT_VERSION_STRING VERSION_GREATER "2.1.4") AND (NOT CMAKE_GENERATOR MATCHES Ninja) AND (NOT MSVC) ) # We use "-C" which requires a recent git
|
||||||
|
message(STATUS "Git is available and it is recent. We are enabling checkperf targets.")
|
||||||
|
# sync_git_repository(myrepo ...) creates two targets:
|
||||||
|
# myrepo - if the repo does not exist, creates and syncs it against the origin branch
|
||||||
|
# update_myrepo - will update the repo against the origin branch (and create if needed)
|
||||||
|
function(sync_git_repository name dir remote branch url)
|
||||||
|
# This conditionally creates the git repository
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${dir}/.git/config
|
||||||
|
COMMAND ${GIT_EXECUTABLE} init ${dir}
|
||||||
|
COMMAND ${GIT_EXECUTABLE} -C ${dir} remote add ${remote} ${url}
|
||||||
|
)
|
||||||
|
add_custom_target(init-${name} DEPENDS ${dir}/.git/config)
|
||||||
|
# This conditionally syncs the git repository, first time only
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${dir}/.git/FETCH_HEAD
|
||||||
|
COMMAND ${GIT_EXECUTABLE} remote set-url ${remote} ${url}
|
||||||
|
COMMAND ${GIT_EXECUTABLE} fetch --depth=1 ${remote} ${branch}
|
||||||
|
COMMAND ${GIT_EXECUTABLE} reset --hard ${remote}/${branch}
|
||||||
|
WORKING_DIRECTORY ${dir}
|
||||||
|
DEPENDS init-${name}
|
||||||
|
)
|
||||||
|
# This is the ${name} target, which will create and sync the repo first time only
|
||||||
|
add_custom_target(${name} DEPENDS ${dir}/.git/FETCH_HEAD)
|
||||||
|
# This is the update-${name} target, which will sync the repo (creating it if needed)
|
||||||
|
add_custom_target(
|
||||||
|
update-${name}
|
||||||
|
COMMAND ${GIT_EXECUTABLE} remote set-url ${remote} ${url}
|
||||||
|
COMMAND ${GIT_EXECUTABLE} fetch --depth=1 ${remote} ${branch}
|
||||||
|
COMMAND ${GIT_EXECUTABLE} reset --hard ${remote}/${branch}
|
||||||
|
WORKING_DIRECTORY ${dir}
|
||||||
|
DEPENDS init-${name}
|
||||||
|
)
|
||||||
|
endfunction(sync_git_repository)
|
||||||
|
|
||||||
|
set(SIMDJSON_CHECKPERF_REMOTE origin CACHE STRING "Remote repository to compare performance against")
|
||||||
|
set(SIMDJSON_CHECKPERF_BRANCH master CACHE STRING "Branch to compare performance against")
|
||||||
|
set(SIMDJSON_CHECKPERF_DIR ${CMAKE_CURRENT_BINARY_DIR}/checkperf-reference/${SIMDJSON_CHECKPERF_BRANCH} CACHE STRING "Location to put checkperf performance comparison repository")
|
||||||
|
set(SIMDJSON_CHECKPERF_ARGS ${EXAMPLE_JSON} CACHE STRING "Arguments to pass to parse during checkperf")
|
||||||
|
sync_git_repository(checkperf-repo ${SIMDJSON_CHECKPERF_DIR} ${SIMDJSON_CHECKPERF_REMOTE} ${SIMDJSON_CHECKPERF_BRANCH} ${SIMDJSON_GITHUB_REPOSITORY})
|
||||||
|
|
||||||
|
# Commands to cause cmake on benchmark/checkperf-master/build/
|
||||||
|
# - first, copy CMakeCache.txt
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${SIMDJSON_CHECKPERF_DIR}/build/CMakeCache.txt
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${SIMDJSON_CHECKPERF_DIR}/build
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${SIMDJSON_USER_CMAKECACHE} ${SIMDJSON_CHECKPERF_DIR}/build/CMakeCache.txt
|
||||||
|
DEPENDS checkperf-repo simdjson-user-cmakecache
|
||||||
|
)
|
||||||
|
# - second, cmake ..
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${SIMDJSON_CHECKPERF_DIR}/build/cmake_install.cmake # We make many things but this seems the most cross-platform one we can depend on
|
||||||
|
COMMAND
|
||||||
|
${CMAKE_COMMAND} -E env CXX=${CMAKE_CXX_COMPILER} CC=${CMAKE_C_COMPILER}
|
||||||
|
${CMAKE_COMMAND}
|
||||||
|
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||||
|
-DSIMDJSON_GOOGLE_BENCHMARKS=OFF
|
||||||
|
-DSIMDJSON_COMPETITION=OFF
|
||||||
|
-DSIMDJSON_DEVELOPER_MODE=YES
|
||||||
|
-G ${CMAKE_GENERATOR}
|
||||||
|
..
|
||||||
|
WORKING_DIRECTORY ${SIMDJSON_CHECKPERF_DIR}/build
|
||||||
|
DEPENDS ${SIMDJSON_CHECKPERF_DIR}/build/CMakeCache.txt
|
||||||
|
)
|
||||||
|
|
||||||
|
# - third, build parse.
|
||||||
|
if (CMAKE_CONFIGURATION_TYPES)
|
||||||
|
set(CHECKPERF_PARSE ${SIMDJSON_CHECKPERF_DIR}/build/benchmark/$<CONFIGURATION>/parse)
|
||||||
|
else()
|
||||||
|
set(CHECKPERF_PARSE ${SIMDJSON_CHECKPERF_DIR}/build/benchmark/dom/parse)
|
||||||
|
endif()
|
||||||
|
add_custom_target(
|
||||||
|
checkperf-parse ALL # TODO is ALL necessary?
|
||||||
|
# Build parse
|
||||||
|
COMMAND ${CMAKE_COMMAND} --build . --target parse --config $<CONFIGURATION>
|
||||||
|
WORKING_DIRECTORY ${SIMDJSON_CHECKPERF_DIR}/build
|
||||||
|
DEPENDS ${SIMDJSON_CHECKPERF_DIR}/build/cmake_install.cmake # We make many things but this seems the most cross-platform one we can depend on
|
||||||
|
)
|
||||||
|
|
||||||
|
# Target to build everything needed for the checkperf test
|
||||||
|
add_custom_target(checkperf DEPENDS parse perfdiff checkperf-parse)
|
||||||
|
|
||||||
|
# Add the actual checkperf test
|
||||||
|
add_test(
|
||||||
|
NAME checkperf
|
||||||
|
# COMMAND ECHO $<TARGET_FILE:perfdiff> \"$<TARGET_FILE:parse> -t ${SIMDJSON_CHECKPERF_ARGS}\" \"${CHECKPERF_PARSE} -t ${SIMDJSON_CHECKPERF_ARGS}\" }
|
||||||
|
COMMAND $<TARGET_FILE:perfdiff> $<TARGET_FILE:parse> ${CHECKPERF_PARSE} -H -t ${SIMDJSON_CHECKPERF_ARGS}
|
||||||
|
)
|
||||||
|
set_property(TEST checkperf APPEND PROPERTY LABELS per_implementation explicitonly)
|
||||||
|
set_property(TEST checkperf APPEND PROPERTY DEPENDS parse perfdiff ${SIMDJSON_USER_CMAKECACHE})
|
||||||
|
set_property(TEST checkperf PROPERTY RUN_SERIAL TRUE)
|
||||||
|
add_dependencies(per_implementation_tests checkperf)
|
||||||
|
add_dependencies(explicitonly_tests checkperf)
|
||||||
|
else()
|
||||||
|
if (CMAKE_GENERATOR MATCHES Ninja)
|
||||||
|
message(STATUS "We disable the checkperf targets under Ninja.")
|
||||||
|
else()
|
||||||
|
message(STATUS "Either git is unavailable or else it is too old. We are disabling checkperf targets.")
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
#include <unistd.h>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -62,11 +62,14 @@ void print_usage(ostream& out) {
|
|||||||
out << "-v - Verbose output." << endl;
|
out << "-v - Verbose output." << endl;
|
||||||
out << "-s stage1 - Stop after find_structural_bits." << endl;
|
out << "-s stage1 - Stop after find_structural_bits." << endl;
|
||||||
out << "-s all - Run all stages." << endl;
|
out << "-s all - Run all stages." << endl;
|
||||||
out << "-H - Make the buffers hot (reduce page allocation during parsing)" << endl;
|
out << "-C - Leave the buffers cold (includes page allocation and related OS tasks during parsing, speed tied to OS performance)" << endl;
|
||||||
|
out << "-H - Make the buffers hot (reduce page allocation and related OS tasks during parsing) [default]" << endl;
|
||||||
out << "-a IMPL - Use the given parser implementation. By default, detects the most advanced" << endl;
|
out << "-a IMPL - Use the given parser implementation. By default, detects the most advanced" << endl;
|
||||||
out << " implementation supported on the host machine." << endl;
|
out << " implementation supported on the host machine." << endl;
|
||||||
for (auto impl : simdjson::available_implementations) {
|
for (auto impl : simdjson::get_available_implementations()) {
|
||||||
out << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << endl;
|
if(impl->supported_by_runtime_system()) {
|
||||||
|
out << "-a " << std::left << std::setw(9) << impl->name() << " - Use the " << impl->description() << " parser implementation." << endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,69 +81,83 @@ void exit_usage(string message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct option_struct {
|
struct option_struct {
|
||||||
vector<char*> files;
|
vector<char*> files{};
|
||||||
bool stage1_only = false;
|
bool stage1_only = false;
|
||||||
|
|
||||||
int32_t iterations = 200;
|
int32_t iterations = 200;
|
||||||
int32_t iteration_step = 50;
|
int32_t iteration_step = -1;
|
||||||
|
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
bool tabbed_output = false;
|
bool tabbed_output = false;
|
||||||
bool hotbuffers = false;
|
/**
|
||||||
|
* Benchmarking on a cold parser instance means that the parsing may include
|
||||||
|
* memory allocation at the OS level. This may lead to apparently odd results
|
||||||
|
* such that higher speed under the Windows Subsystem for Linux than under the
|
||||||
|
* regular Windows, for the same machine. It is arguably misleading to benchmark
|
||||||
|
* how the OS allocates memory, when we really want to just benchmark simdjson.
|
||||||
|
*/
|
||||||
|
bool hotbuffers = true;
|
||||||
|
|
||||||
option_struct(int argc, char **argv) {
|
option_struct(int argc, char **argv) {
|
||||||
#ifndef _MSC_VER
|
int c;
|
||||||
int c;
|
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "vtn:i:a:s:H")) != -1) {
|
while ((c = getopt(argc, argv, "vtn:i:a:s:HC")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'n':
|
case 'n':
|
||||||
iterations = atoi(optarg);
|
iterations = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
iteration_step = atoi(optarg);
|
iteration_step = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
tabbed_output = true;
|
tabbed_output = true;
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
case 'a': {
|
case 'a': {
|
||||||
const implementation *impl = simdjson::available_implementations[optarg];
|
const implementation *impl = simdjson::get_available_implementations()[optarg];
|
||||||
if (!impl) {
|
if ((!impl) || (!impl->supported_by_runtime_system())) {
|
||||||
std::string exit_message = string("Unsupported option value -a ") + optarg + ": expected -a with one of ";
|
std::string exit_message = string("Unsupported option value -a ") + optarg + ": expected -a with one of ";
|
||||||
for (auto imple : simdjson::available_implementations) {
|
for (auto imple : simdjson::get_available_implementations()) {
|
||||||
|
if(imple->supported_by_runtime_system()) {
|
||||||
exit_message += imple->name();
|
exit_message += imple->name();
|
||||||
exit_message += " ";
|
exit_message += " ";
|
||||||
}
|
}
|
||||||
exit_usage(exit_message);
|
|
||||||
}
|
}
|
||||||
simdjson::active_implementation = impl;
|
exit_usage(exit_message);
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'H':
|
|
||||||
hotbuffers = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
if (!strcmp(optarg, "stage1")) {
|
|
||||||
stage1_only = true;
|
|
||||||
} else if (!strcmp(optarg, "all")) {
|
|
||||||
stage1_only = false;
|
|
||||||
} else {
|
|
||||||
exit_usage(string("Unsupported option value -s ") + optarg + ": expected -s stage1 or all");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// reaching here means an argument was given to getopt() which did not have a case label
|
|
||||||
exit_usage("Unexpected argument - missing case for option "+
|
|
||||||
std::string(1,static_cast<char>(c))+
|
|
||||||
" (programming error)");
|
|
||||||
}
|
}
|
||||||
|
simdjson::get_active_implementation() = impl;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
#else
|
case 'C':
|
||||||
int optind = 1;
|
hotbuffers = false;
|
||||||
#endif
|
break;
|
||||||
|
case 'H':
|
||||||
|
hotbuffers = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
if (!strcmp(optarg, "stage1")) {
|
||||||
|
stage1_only = true;
|
||||||
|
} else if (!strcmp(optarg, "all")) {
|
||||||
|
stage1_only = false;
|
||||||
|
} else {
|
||||||
|
exit_usage(string("Unsupported option value -s ") + optarg + ": expected -s stage1 or all");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// reaching here means an argument was given to getopt() which did not have a case label
|
||||||
|
exit_usage("Unexpected argument - missing case for option "+
|
||||||
|
std::string(1,static_cast<char>(c))+
|
||||||
|
" (programming error)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iteration_step == -1) {
|
||||||
|
iteration_step = iterations / 50;
|
||||||
|
if (iteration_step < 200) { iteration_step = 200; }
|
||||||
|
if (iteration_step > iterations) { iteration_step = iterations; }
|
||||||
|
}
|
||||||
|
|
||||||
// All remaining arguments are considered to be files
|
// All remaining arguments are considered to be files
|
||||||
for (int i=optind; i<argc; i++) {
|
for (int i=optind; i<argc; i++) {
|
||||||
@@ -149,17 +166,6 @@ struct option_struct {
|
|||||||
if (files.empty()) {
|
if (files.empty()) {
|
||||||
exit_usage("No files specified");
|
exit_usage("No files specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keeps the numbers the same for CI (old ./parse didn't have a two-stage loop)
|
|
||||||
if (files.size() == 1) {
|
|
||||||
iteration_step = iterations;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(__linux__)
|
|
||||||
if (tabbed_output) {
|
|
||||||
exit_error("tabbed_output (-t) flag only works under linux.\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -169,7 +175,7 @@ int main(int argc, char *argv[]) {
|
|||||||
option_struct options(argc, argv);
|
option_struct options(argc, argv);
|
||||||
if (options.verbose) {
|
if (options.verbose) {
|
||||||
verbose_stream = &cout;
|
verbose_stream = &cout;
|
||||||
verbose() << "Implementation: " << simdjson::active_implementation->name() << endl;
|
verbose() << "Implementation: " << simdjson::get_active_implementation()->name() << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start collecting events. We put this early so if it prints an error message, it's the
|
// Start collecting events. We put this early so if it prints an error message, it's the
|
||||||
@@ -212,7 +218,7 @@ int main(int argc, char *argv[]) {
|
|||||||
if (!options.verbose) { progress.erase(); }
|
if (!options.verbose) { progress.erase(); }
|
||||||
|
|
||||||
for (size_t i=0; i<options.files.size(); i++) {
|
for (size_t i=0; i<options.files.size(); i++) {
|
||||||
benchmarkers[i]->print(options.tabbed_output, options.iterations);
|
benchmarkers[i]->print(options.tabbed_output);
|
||||||
delete benchmarkers[i];
|
delete benchmarkers[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "simdjson.h"
|
||||||
|
|
||||||
|
#define NB_ITERATION 20
|
||||||
|
#define MIN_BATCH_SIZE 10000
|
||||||
|
#define MAX_BATCH_SIZE 10000000
|
||||||
|
|
||||||
|
bool test_baseline = false;
|
||||||
|
bool test_per_batch = true;
|
||||||
|
bool test_best_batch = false;
|
||||||
|
|
||||||
|
bool compare(std::pair<size_t, double> i, std::pair<size_t, double> j) {
|
||||||
|
return i.second > j.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
if (argc <= 1) {
|
||||||
|
std::cerr << "Usage: " << argv[0] << " <jsonfile>" << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
const char *filename = argv[1];
|
||||||
|
auto v = simdjson::padded_string::load(filename);
|
||||||
|
if (v.error()) {
|
||||||
|
std::cerr << "Could not load the file " << filename << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
const simdjson::padded_string& p = v.value_unsafe();
|
||||||
|
if (test_baseline) {
|
||||||
|
std::wclog << "Baseline: Getline + normal parse... " << std::endl;
|
||||||
|
std::cout << "Gigabytes/second\t"
|
||||||
|
<< "Nb of documents parsed" << std::endl;
|
||||||
|
for (auto i = 0; i < 3; i++) {
|
||||||
|
// Actual test
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::error_code alloc_error = parser.allocate(p.size());
|
||||||
|
if (alloc_error) {
|
||||||
|
std::cerr << alloc_error << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
std::istringstream ss(std::string(p.data(), p.size()));
|
||||||
|
|
||||||
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
int count = 0;
|
||||||
|
std::string line;
|
||||||
|
int parse_res = simdjson::SUCCESS;
|
||||||
|
while (getline(ss, line)) {
|
||||||
|
// TODO we're likely triggering simdjson's padding reallocation here. Is
|
||||||
|
// that intentional?
|
||||||
|
parser.parse(line);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
std::chrono::duration<double> secs = end - start;
|
||||||
|
double speedinGBs = static_cast<double>(p.size()) /
|
||||||
|
(static_cast<double>(secs.count()) * 1000000000.0);
|
||||||
|
std::cout << speedinGBs << "\t\t\t\t" << count << std::endl;
|
||||||
|
|
||||||
|
if (parse_res != simdjson::SUCCESS) {
|
||||||
|
std::cerr << "Parsing failed" << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<size_t, double> batch_size_res;
|
||||||
|
if (test_per_batch) {
|
||||||
|
std::wclog << "parse_many: Speed per batch_size... from " << MIN_BATCH_SIZE
|
||||||
|
<< " bytes to " << MAX_BATCH_SIZE << " bytes..." << std::endl;
|
||||||
|
std::cout << "Batch Size\t"
|
||||||
|
<< "Gigabytes/second\t"
|
||||||
|
<< "Nb of documents parsed" << std::endl;
|
||||||
|
for (size_t i = MIN_BATCH_SIZE; i <= MAX_BATCH_SIZE;
|
||||||
|
i += (MAX_BATCH_SIZE - MIN_BATCH_SIZE) / 100) {
|
||||||
|
batch_size_res.insert(std::pair<size_t, double>(i, 0));
|
||||||
|
int count;
|
||||||
|
for (size_t j = 0; j < 5; j++) {
|
||||||
|
// Actual test
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::error_code error;
|
||||||
|
|
||||||
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
count = 0;
|
||||||
|
simdjson::dom::document_stream docs;
|
||||||
|
if ((error = parser.parse_many(p, i).get(docs))) {
|
||||||
|
std::wcerr << "Parsing failed with: " << error << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
for (auto result : docs) {
|
||||||
|
error = result.error();
|
||||||
|
if (error) {
|
||||||
|
std::wcerr << "Parsing failed with: " << error << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
auto end = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
std::chrono::duration<double> secs = end - start;
|
||||||
|
double speedinGBs = static_cast<double>(p.size()) /
|
||||||
|
(static_cast<double>(secs.count()) * 1000000000.0);
|
||||||
|
if (speedinGBs > batch_size_res.at(i))
|
||||||
|
batch_size_res[i] = speedinGBs;
|
||||||
|
}
|
||||||
|
std::cout << i << "\t\t" << std::fixed << std::setprecision(3)
|
||||||
|
<< batch_size_res.at(i) << "\t\t\t\t" << count << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t optimal_batch_size{};
|
||||||
|
double best_speed{};
|
||||||
|
if (test_per_batch) {
|
||||||
|
std::pair<size_t, double> best_results;
|
||||||
|
best_results =
|
||||||
|
(*min_element(batch_size_res.begin(), batch_size_res.end(), compare));
|
||||||
|
optimal_batch_size = best_results.first;
|
||||||
|
best_speed = best_results.second;
|
||||||
|
} else {
|
||||||
|
optimal_batch_size = MIN_BATCH_SIZE;
|
||||||
|
}
|
||||||
|
std::wclog << "Seemingly optimal batch_size: " << optimal_batch_size << "..."
|
||||||
|
<< std::endl;
|
||||||
|
std::wclog << "Best speed: " << best_speed << "..." << std::endl;
|
||||||
|
|
||||||
|
if (test_best_batch) {
|
||||||
|
std::wclog << "Starting speed test... Best of " << NB_ITERATION
|
||||||
|
<< " iterations..." << std::endl;
|
||||||
|
std::vector<double> res;
|
||||||
|
for (int i = 0; i < NB_ITERATION; i++) {
|
||||||
|
|
||||||
|
// Actual test
|
||||||
|
simdjson::dom::parser parser;
|
||||||
|
simdjson::error_code error;
|
||||||
|
|
||||||
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
// This includes allocation of the parser
|
||||||
|
simdjson::dom::document_stream docs;
|
||||||
|
if ((error = parser.parse_many(p, optimal_batch_size).get(docs))) {
|
||||||
|
std::wcerr << "Parsing failed with: " << error << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
for (auto result : docs) {
|
||||||
|
error = result.error();
|
||||||
|
if (error) {
|
||||||
|
std::wcerr << "Parsing failed with: " << error << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto end = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
std::chrono::duration<double> secs = end - start;
|
||||||
|
res.push_back(secs.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
double min_result = *min_element(res.begin(), res.end());
|
||||||
|
double speedinGBs =
|
||||||
|
static_cast<double>(p.size()) / (min_result * 1000000000.0);
|
||||||
|
|
||||||
|
std::cout << "Min: " << min_result << " bytes read: " << p.size()
|
||||||
|
<< " Gigabytes/second: " << speedinGBs << std::endl;
|
||||||
|
}
|
||||||
|
#ifdef SIMDJSON_THREADS_ENABLED
|
||||||
|
// Multithreading probably does not help matters for small files (less than 10
|
||||||
|
// MB).
|
||||||
|
if (p.size() < 10000000) {
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Warning: your file is small and the performance results are "
|
||||||
|
"probably meaningless"
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "as far as multithreaded performance goes." << std::endl;
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "Try to concatenate the file with itself to generate a large one."
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << "In bash: " << std::endl;
|
||||||
|
std::cout << "for i in {1..1000}; do cat '" << filename
|
||||||
|
<< "' >> bar.ndjson; done" << std::endl;
|
||||||
|
std::cout << argv[0] << " bar.ndjson" << std::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -29,9 +29,10 @@ std::string exec(const char* cmd) {
|
|||||||
std::string result;
|
std::string result;
|
||||||
std::unique_ptr<FILE, decltype(&closepipe)> pipe(popen(cmd, "r"), closepipe);
|
std::unique_ptr<FILE, decltype(&closepipe)> pipe(popen(cmd, "r"), closepipe);
|
||||||
if (!pipe) {
|
if (!pipe) {
|
||||||
throw std::runtime_error("popen() failed!");
|
std::cerr << "popen() failed!" << std::endl;
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
|
while (fgets(buffer.data(), int(buffer.size()), pipe.get()) != nullptr) {
|
||||||
result += buffer.data();
|
result += buffer.data();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -43,10 +44,10 @@ double readThroughput(std::string parseOutput) {
|
|||||||
double result = 0;
|
double result = 0;
|
||||||
int numResults = 0;
|
int numResults = 0;
|
||||||
while (std::getline(output, line)) {
|
while (std::getline(output, line)) {
|
||||||
int pos = 0;
|
std::string::size_type pos = 0;
|
||||||
for (int i=0; i<5; i++) {
|
for (int i=0; i<5; i++) {
|
||||||
pos = line.find('\t', pos);
|
pos = line.find('\t', pos);
|
||||||
if (pos < 0) {
|
if (pos == std::string::npos) {
|
||||||
std::cerr << "Command printed out a line with less than 5 fields in it:\n" << line << std::endl;
|
std::cerr << "Command printed out a line with less than 5 fields in it:\n" << line << std::endl;
|
||||||
}
|
}
|
||||||
pos++;
|
pos++;
|
||||||
@@ -63,23 +64,33 @@ double readThroughput(std::string parseOutput) {
|
|||||||
|
|
||||||
const double INTERLEAVED_ATTEMPTS = 7;
|
const double INTERLEAVED_ATTEMPTS = 7;
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, const char *argv[]) {
|
||||||
if (argc != 3) {
|
if (argc < 3) {
|
||||||
std::cerr << "Usage: " << argv[0] << " <new parse cmd> <reference parse cmd>" << std::endl;
|
std::cerr << "Usage: " << argv[0] << " <old parse exe> <new parse exe> [<parse arguments>]" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string newCommand = argv[1];
|
||||||
|
std::string refCommand = argv[2];
|
||||||
|
for (int i=3; i<argc; i++) {
|
||||||
|
newCommand += " ";
|
||||||
|
newCommand += argv[i];
|
||||||
|
refCommand += " ";
|
||||||
|
refCommand += argv[i];
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<double> ref;
|
std::vector<double> ref;
|
||||||
std::vector<double> newcode;
|
std::vector<double> newcode;
|
||||||
for (int attempt=0; attempt < INTERLEAVED_ATTEMPTS; attempt++) {
|
for (int attempt=0; attempt < INTERLEAVED_ATTEMPTS; attempt++) {
|
||||||
std::cout << "Attempt #" << (attempt+1) << " of " << INTERLEAVED_ATTEMPTS << std::endl;
|
std::cout << "Attempt #" << (attempt+1) << " of " << INTERLEAVED_ATTEMPTS << std::endl;
|
||||||
|
|
||||||
// Read new throughput
|
// Read new throughput
|
||||||
double newThroughput = readThroughput(exec(argv[1]));
|
double newThroughput = readThroughput(exec(newCommand.c_str()));
|
||||||
std::cout << "New throughput: " << newThroughput << std::endl;
|
std::cout << "New throughput: " << newThroughput << std::endl;
|
||||||
newcode.push_back(newThroughput);
|
newcode.push_back(newThroughput);
|
||||||
|
|
||||||
// Read reference throughput
|
// Read reference throughput
|
||||||
double referenceThroughput = readThroughput(exec(argv[2]));
|
double referenceThroughput = readThroughput(exec(refCommand.c_str()));
|
||||||
std::cout << "Ref throughput: " << referenceThroughput << std::endl;
|
std::cout << "Ref throughput: " << referenceThroughput << std::endl;
|
||||||
ref.push_back(referenceThroughput);
|
ref.push_back(referenceThroughput);
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
|
||||||
#include "simdjson.h"
|
#include "simdjson.h"
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include "linux-perf-events.h"
|
#include "linux-perf-events.h"
|
||||||
@@ -43,7 +41,7 @@ using stat_t = struct stat_s;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
really_inline void simdjson_process_atom(stat_t &s,
|
simdjson_inline void simdjson_process_atom(stat_t &s,
|
||||||
simdjson::dom::element element) {
|
simdjson::dom::element element) {
|
||||||
if (element.is<int64_t>()) {
|
if (element.is<int64_t>()) {
|
||||||
s.integer_count++;
|
s.integer_count++;
|
||||||
@@ -52,7 +50,10 @@ really_inline void simdjson_process_atom(stat_t &s,
|
|||||||
} else if(element.is<double>()) {
|
} else if(element.is<double>()) {
|
||||||
s.float_count++;
|
s.float_count++;
|
||||||
} else if (element.is<bool>()) {
|
} else if (element.is<bool>()) {
|
||||||
if (element.get<bool>()) {
|
bool v;
|
||||||
|
simdjson::error_code error;
|
||||||
|
if ((error = element.get(v))) { std::cerr << error << std::endl; abort(); }
|
||||||
|
if (v) {
|
||||||
s.true_count++;
|
s.true_count++;
|
||||||
} else {
|
} else {
|
||||||
s.false_count++;
|
s.false_count++;
|
||||||
@@ -63,9 +64,11 @@ really_inline void simdjson_process_atom(stat_t &s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void simdjson_recurse(stat_t &s, simdjson::dom::element element) {
|
void simdjson_recurse(stat_t &s, simdjson::dom::element element) {
|
||||||
|
simdjson::error_code error;
|
||||||
if (element.is<simdjson::dom::array>()) {
|
if (element.is<simdjson::dom::array>()) {
|
||||||
s.array_count++;
|
s.array_count++;
|
||||||
auto [array, array_error] = element.get<simdjson::dom::array>();
|
simdjson::dom::array array;
|
||||||
|
if ((error = element.get(array))) { std::cerr << error << std::endl; abort(); }
|
||||||
for (auto child : array) {
|
for (auto child : array) {
|
||||||
if (child.is<simdjson::dom::array>() || child.is<simdjson::dom::object>()) {
|
if (child.is<simdjson::dom::array>() || child.is<simdjson::dom::object>()) {
|
||||||
simdjson_recurse(s, child);
|
simdjson_recurse(s, child);
|
||||||
@@ -75,13 +78,14 @@ void simdjson_recurse(stat_t &s, simdjson::dom::element element) {
|
|||||||
}
|
}
|
||||||
} else if (element.is<simdjson::dom::object>()) {
|
} else if (element.is<simdjson::dom::object>()) {
|
||||||
s.object_count++;
|
s.object_count++;
|
||||||
auto [object, object_error] = element.get<simdjson::dom::object>();
|
simdjson::dom::object object;
|
||||||
for (auto [key, value] : object) {
|
if ((error = element.get(object))) { std::cerr << error << std::endl; abort(); }
|
||||||
|
for (auto field : object) {
|
||||||
s.string_count++; // for key
|
s.string_count++; // for key
|
||||||
if (value.is<simdjson::dom::array>() || value.is<simdjson::dom::object>()) {
|
if (field.value.is<simdjson::dom::array>() || field.value.is<simdjson::dom::object>()) {
|
||||||
simdjson_recurse(s, value);
|
simdjson_recurse(s, field.value);
|
||||||
} else {
|
} else {
|
||||||
simdjson_process_atom(s, value);
|
simdjson_process_atom(s, field.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -92,7 +96,8 @@ void simdjson_recurse(stat_t &s, simdjson::dom::element element) {
|
|||||||
stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
|
stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
|
||||||
stat_t answer{};
|
stat_t answer{};
|
||||||
simdjson::dom::parser parser;
|
simdjson::dom::parser parser;
|
||||||
auto [doc, error] = parser.parse(p);
|
simdjson::dom::element doc;
|
||||||
|
auto error = parser.parse(p).get(doc);
|
||||||
if (error) {
|
if (error) {
|
||||||
answer.valid = false;
|
answer.valid = false;
|
||||||
return answer;
|
return answer;
|
||||||
@@ -103,7 +108,7 @@ stat_t simdjson_compute_stats(const simdjson::padded_string &p) {
|
|||||||
answer.non_ascii_byte_count = count_nonasciibytes(
|
answer.non_ascii_byte_count = count_nonasciibytes(
|
||||||
reinterpret_cast<const uint8_t *>(p.data()), p.size());
|
reinterpret_cast<const uint8_t *>(p.data()), p.size());
|
||||||
answer.byte_count = p.size();
|
answer.byte_count = p.size();
|
||||||
answer.structural_indexes_count = parser.n_structural_indexes;
|
answer.structural_indexes_count = parser.implementation->n_structural_indexes;
|
||||||
simdjson_recurse(answer, doc);
|
simdjson_recurse(answer, doc);
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
@@ -132,7 +137,8 @@ int main(int argc, char *argv[]) {
|
|||||||
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
|
std::cerr << "warning: ignoring everything after " << argv[optind + 1]
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
auto [p, error] = simdjson::padded_string::load(filename);
|
simdjson::padded_string p;
|
||||||
|
auto error = simdjson::padded_string::load(filename).get(p);
|
||||||
if (error) {
|
if (error) {
|
||||||
std::cerr << "Could not load the file " << filename << std::endl;
|
std::cerr << "Could not load the file " << filename << std::endl;
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@@ -160,7 +166,6 @@ int main(int argc, char *argv[]) {
|
|||||||
s.true_count, s.false_count, s.byte_count, s.structural_indexes_count);
|
s.true_count, s.false_count, s.byte_count, s.structural_indexes_count);
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
simdjson::dom::parser parser;
|
simdjson::dom::parser parser;
|
||||||
const simdjson::implementation &stage_parser = *simdjson::active_implementation;
|
|
||||||
simdjson::error_code alloc_error = parser.allocate(p.size());
|
simdjson::error_code alloc_error = parser.allocate(p.size());
|
||||||
if (alloc_error) {
|
if (alloc_error) {
|
||||||
std::cerr << alloc_error << std::endl;
|
std::cerr << alloc_error << std::endl;
|
||||||
@@ -178,14 +183,14 @@ int main(int argc, char *argv[]) {
|
|||||||
for (uint32_t i = 0; i < iterations; i++) {
|
for (uint32_t i = 0; i < iterations; i++) {
|
||||||
unified.start();
|
unified.start();
|
||||||
// The default template is simdjson::architecture::NATIVE.
|
// The default template is simdjson::architecture::NATIVE.
|
||||||
bool isok = (stage_parser.stage1((const uint8_t *)p.data(), p.size(), parser, false) == simdjson::SUCCESS);
|
bool isok = (parser.implementation->stage1((const uint8_t *)p.data(), p.size(), simdjson::stage1_mode::regular) == simdjson::SUCCESS);
|
||||||
unified.end(results);
|
unified.end(results);
|
||||||
|
|
||||||
cy1 += results[0];
|
cy1 += results[0];
|
||||||
cl1 += results[1];
|
cl1 += results[1];
|
||||||
|
|
||||||
unified.start();
|
unified.start();
|
||||||
isok = isok && (stage_parser.stage2((const uint8_t *)p.data(), p.size(), parser) == simdjson::SUCCESS);
|
isok = isok && (parser.implementation->stage2(parser.doc) == simdjson::SUCCESS);
|
||||||
unified.end(results);
|
unified.end(results);
|
||||||
|
|
||||||
cy2 += results[0];
|
cy2 += results[0];
|
||||||
@@ -194,8 +199,8 @@ int main(int argc, char *argv[]) {
|
|||||||
std::cerr << "failure?" << std::endl;
|
std::cerr << "failure?" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("%f %f %f %f ", cy1 * 1.0 / iterations, cl1 * 1.0 / iterations,
|
printf("%f %f %f %f ", static_cast<double>(cy1) / static_cast<double>(iterations), static_cast<double>(cl1) / static_cast<double>(iterations),
|
||||||
cy2 * 1.0 / iterations, cl2 * 1.0 / iterations);
|
static_cast<double>(cy2) / static_cast<double>(iterations), static_cast<double>(cl2) / static_cast<double>(iterations));
|
||||||
#endif // __linux__
|
#endif // __linux__
|
||||||
printf("\n");
|
printf("\n");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
+42
-19
@@ -1,12 +1,21 @@
|
|||||||
#ifndef __EVENT_COUNTER_H
|
#ifndef __EVENT_COUNTER_H
|
||||||
#define __EVENT_COUNTER_H
|
#define __EVENT_COUNTER_H
|
||||||
|
|
||||||
|
#ifndef SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
|
#ifdef __aarch64__
|
||||||
|
// on ARM, we use just cycles and instructions
|
||||||
|
#define SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS 1
|
||||||
|
#else
|
||||||
|
// elsewhere, we try to use four counters.
|
||||||
|
#define SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
#include <unistd.h>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -46,6 +55,12 @@ struct event_count {
|
|||||||
event_count(const event_count& other): elapsed(other.elapsed), event_counts(other.event_counts) { }
|
event_count(const event_count& other): elapsed(other.elapsed), event_counts(other.event_counts) { }
|
||||||
|
|
||||||
// The types of counters (so we can read the getter more easily)
|
// The types of counters (so we can read the getter more easily)
|
||||||
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
|
enum event_counter_types {
|
||||||
|
CPU_CYCLES,
|
||||||
|
INSTRUCTIONS
|
||||||
|
};
|
||||||
|
#else
|
||||||
enum event_counter_types {
|
enum event_counter_types {
|
||||||
CPU_CYCLES,
|
CPU_CYCLES,
|
||||||
INSTRUCTIONS,
|
INSTRUCTIONS,
|
||||||
@@ -53,15 +68,16 @@ struct event_count {
|
|||||||
CACHE_REFERENCES,
|
CACHE_REFERENCES,
|
||||||
CACHE_MISSES
|
CACHE_MISSES
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
double elapsed_sec() const { return duration<double>(elapsed).count(); }
|
double elapsed_sec() const { return duration<double>(elapsed).count(); }
|
||||||
double elapsed_ns() const { return duration<double, std::nano>(elapsed).count(); }
|
double elapsed_ns() const { return duration<double, std::nano>(elapsed).count(); }
|
||||||
double cycles() const { return event_counts[CPU_CYCLES]; }
|
double cycles() const { return static_cast<double>(event_counts[CPU_CYCLES]); }
|
||||||
double instructions() const { return event_counts[INSTRUCTIONS]; }
|
double instructions() const { return static_cast<double>(event_counts[INSTRUCTIONS]); }
|
||||||
double branch_misses() const { return event_counts[BRANCH_MISSES]; }
|
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
double cache_references() const { return event_counts[CACHE_REFERENCES]; }
|
double branch_misses() const { return static_cast<double>(event_counts[BRANCH_MISSES]); }
|
||||||
double cache_misses() const { return event_counts[CACHE_MISSES]; }
|
double cache_references() const { return static_cast<double>(event_counts[CACHE_REFERENCES]); }
|
||||||
|
double cache_misses() const { return static_cast<double>(event_counts[CACHE_MISSES]); }
|
||||||
|
#endif
|
||||||
event_count& operator=(const event_count& other) {
|
event_count& operator=(const event_count& other) {
|
||||||
this->elapsed = other.elapsed;
|
this->elapsed = other.elapsed;
|
||||||
this->event_counts = other.event_counts;
|
this->event_counts = other.event_counts;
|
||||||
@@ -84,9 +100,9 @@ struct event_count {
|
|||||||
|
|
||||||
struct event_aggregate {
|
struct event_aggregate {
|
||||||
int iterations = 0;
|
int iterations = 0;
|
||||||
event_count total;
|
event_count total{};
|
||||||
event_count best;
|
event_count best{};
|
||||||
event_count worst;
|
event_count worst{};
|
||||||
|
|
||||||
event_aggregate() {}
|
event_aggregate() {}
|
||||||
|
|
||||||
@@ -105,41 +121,48 @@ struct event_aggregate {
|
|||||||
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
|
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
|
||||||
double cycles() const { return total.cycles() / iterations; }
|
double cycles() const { return total.cycles() / iterations; }
|
||||||
double instructions() const { return total.instructions() / iterations; }
|
double instructions() const { return total.instructions() / iterations; }
|
||||||
|
#if !SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
double branch_misses() const { return total.branch_misses() / iterations; }
|
double branch_misses() const { return total.branch_misses() / iterations; }
|
||||||
double cache_references() const { return total.cache_references() / iterations; }
|
double cache_references() const { return total.cache_references() / iterations; }
|
||||||
double cache_misses() const { return total.cache_misses() / iterations; }
|
double cache_misses() const { return total.cache_misses() / iterations; }
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct event_collector {
|
struct event_collector {
|
||||||
event_count count;
|
event_count count{};
|
||||||
time_point<steady_clock> start_clock;
|
time_point<steady_clock> start_clock{};
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
|
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
|
||||||
event_collector() : linux_events(vector<int>{
|
event_collector(bool quiet = false) : linux_events(vector<int>{
|
||||||
|
#if SIMDJSON_SIMPLE_PERFORMANCE_COUNTERS
|
||||||
|
PERF_COUNT_HW_CPU_CYCLES,
|
||||||
|
PERF_COUNT_HW_INSTRUCTIONS,
|
||||||
|
#else
|
||||||
PERF_COUNT_HW_CPU_CYCLES,
|
PERF_COUNT_HW_CPU_CYCLES,
|
||||||
PERF_COUNT_HW_INSTRUCTIONS,
|
PERF_COUNT_HW_INSTRUCTIONS,
|
||||||
PERF_COUNT_HW_BRANCH_MISSES,
|
PERF_COUNT_HW_BRANCH_MISSES,
|
||||||
PERF_COUNT_HW_CACHE_REFERENCES,
|
PERF_COUNT_HW_CACHE_REFERENCES,
|
||||||
PERF_COUNT_HW_CACHE_MISSES
|
PERF_COUNT_HW_CACHE_MISSES
|
||||||
}) {}
|
#endif
|
||||||
|
}, quiet) {}
|
||||||
bool has_events() {
|
bool has_events() {
|
||||||
return linux_events.is_working();
|
return linux_events.is_working();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
event_collector() {}
|
event_collector(simdjson_unused bool _quiet = false) {}
|
||||||
bool has_events() {
|
bool has_events() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
really_inline void start() {
|
simdjson_inline void start() {
|
||||||
#if defined(__linux)
|
#if defined(__linux)
|
||||||
linux_events.start();
|
linux_events.start();
|
||||||
#endif
|
#endif
|
||||||
start_clock = steady_clock::now();
|
start_clock = steady_clock::now();
|
||||||
}
|
}
|
||||||
really_inline event_count& end() {
|
simdjson_inline event_count& end() {
|
||||||
time_point<steady_clock> end_clock = steady_clock::now();
|
time_point<steady_clock> end_clock = steady_clock::now();
|
||||||
#if defined(__linux)
|
#if defined(__linux)
|
||||||
linux_events.end(count.event_counts);
|
linux_events.end(count.event_counts);
|
||||||
@@ -149,4 +172,4 @@ struct event_collector {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "json_benchmark/file_runner.h"
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
using namespace json_benchmark;
|
||||||
|
|
||||||
|
template<typename I>
|
||||||
|
struct runner : public file_runner<I> {
|
||||||
|
typename I::StringType result;
|
||||||
|
|
||||||
|
bool setup(benchmark::State &state) {
|
||||||
|
return this->load_json(state, TWITTER_JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool before_run(benchmark::State &state) {
|
||||||
|
if (!file_runner<I>::before_run(state)) { return false; }
|
||||||
|
result = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(benchmark::State &) {
|
||||||
|
return this->implementation.run(this->json, 505874901689851904ULL, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R>
|
||||||
|
bool diff(benchmark::State &state, runner<R> &reference) {
|
||||||
|
return diff_results(state, result, reference.result, diff_flags::NONE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct simdjson_dom;
|
||||||
|
|
||||||
|
template<typename I> simdjson_inline static void find_tweet(benchmark::State &state) {
|
||||||
|
run_json_benchmark<runner<I>, runner<simdjson_dom>>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace find_tweet
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
|
||||||
|
#include "find_tweet.h"
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
struct nlohmann_json {
|
||||||
|
using StringType=std::string;
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string &result) {
|
||||||
|
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
|
||||||
|
for (auto tweet : root["statuses"]) {
|
||||||
|
if (tweet["id"] == find_id) {
|
||||||
|
result = tweet["text"];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, nlohmann_json)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace find_tweet
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
|
||||||
|
#include "find_tweet.h"
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
struct nlohmann_json_sax {
|
||||||
|
using StringType=std::string;
|
||||||
|
|
||||||
|
struct Handler : json::json_sax_t
|
||||||
|
{
|
||||||
|
bool text_key = false;
|
||||||
|
bool id_key = false;
|
||||||
|
bool found_id = false;
|
||||||
|
uint64_t find_id;
|
||||||
|
std::string &result;
|
||||||
|
|
||||||
|
Handler(std::string &r,uint64_t id): result(r), find_id(id) { }
|
||||||
|
|
||||||
|
// We assume id is found before text
|
||||||
|
bool key(string_t& val) override {
|
||||||
|
if (found_id) { // If have found id, find text key
|
||||||
|
if (val.compare("text") == 0) { text_key = true; }
|
||||||
|
}
|
||||||
|
else if (val.compare("id") == 0) { id_key = true; } // Otherwise, find id key
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool number_unsigned(number_unsigned_t val) override {
|
||||||
|
if (id_key && (val == find_id)) { // If id key, check if id value matches find_id
|
||||||
|
found_id = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool string(string_t& val) override {
|
||||||
|
if (text_key) {
|
||||||
|
result = val;
|
||||||
|
return false; // End parsing
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Irrelevant events
|
||||||
|
bool null() override { return true; }
|
||||||
|
bool boolean(bool val) override { return true; }
|
||||||
|
bool number_float(number_float_t val, const string_t& s) override { return true; }
|
||||||
|
bool number_integer(number_integer_t val) override { return true; }
|
||||||
|
bool start_object(std::size_t elements) override { return true; }
|
||||||
|
bool end_object() override { return true; }
|
||||||
|
bool start_array(std::size_t elements) override { return true; }
|
||||||
|
bool end_array() override { return true; }
|
||||||
|
bool binary(json::binary_t& val) override { return true; }
|
||||||
|
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { return false; }
|
||||||
|
}; // Handler
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string &result) {
|
||||||
|
Handler handler(result,find_id);
|
||||||
|
json::sax_parse(json.data(), &handler);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}; // nlohmann_json_sax
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, nlohmann_json_sax)->UseManualTime();
|
||||||
|
} // namespace find_tweet
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
|
||||||
|
#include "find_tweet.h"
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
struct rapidjson_base {
|
||||||
|
using StringType=std::string_view;
|
||||||
|
|
||||||
|
Document doc{};
|
||||||
|
|
||||||
|
bool run(Document &root, uint64_t find_id, std::string_view &result) {
|
||||||
|
if (root.HasParseError() || !root.IsObject()) { return false; }
|
||||||
|
auto statuses = root.FindMember("statuses");
|
||||||
|
if (statuses == root.MemberEnd() || !statuses->value.IsArray()) { return false; }
|
||||||
|
for (auto &tweet : statuses->value.GetArray()) {
|
||||||
|
if (!tweet.IsObject()) { return false; }
|
||||||
|
auto id = tweet.FindMember("id");
|
||||||
|
if (id == tweet.MemberEnd() || !id->value.IsUint64()) { return false; }
|
||||||
|
if (id->value.GetUint64() == find_id) {
|
||||||
|
auto text = tweet.FindMember("text");
|
||||||
|
if (text == tweet.MemberEnd() || !text->value.IsString()) { return false; }
|
||||||
|
result = { text->value.GetString(), text->value.GetStringLength() };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rapidjson : rapidjson_base {
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
|
return rapidjson_base::run(doc.Parse<kParseValidateEncodingFlag>(json.data()), find_id, result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, rapidjson)->UseManualTime();
|
||||||
|
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
struct rapidjson_insitu : rapidjson_base {
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
|
return rapidjson_base::run(doc.ParseInsitu<kParseValidateEncodingFlag|kParseInsituFlag>(json.data()), find_id, result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, rapidjson_insitu)->UseManualTime();
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
} // namespace find_tweet
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
|
||||||
|
#include "find_tweet.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
struct rapidjson_sax {
|
||||||
|
using StringType=std::string_view;
|
||||||
|
|
||||||
|
struct Handler {
|
||||||
|
bool text_key = false;
|
||||||
|
bool id_key = false;
|
||||||
|
bool found_id = false;
|
||||||
|
uint64_t find_id;
|
||||||
|
std::string_view &result;
|
||||||
|
|
||||||
|
Handler(std::string_view &r,uint64_t id): result(r), find_id(id) { }
|
||||||
|
|
||||||
|
// We assume id is found before text
|
||||||
|
bool Key(const char* key, SizeType length, bool copy) {
|
||||||
|
if (found_id) { // If have found id, find text key
|
||||||
|
if ((length == 4) && (memcmp(key,"text",4) == 0)) { text_key = true; }
|
||||||
|
}
|
||||||
|
else if ((length == 2) && (memcmp(key,"id",2) == 0)) { id_key = true; } // Otherwise, find id key
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool Uint64(uint64_t i) {
|
||||||
|
if (id_key && (i == find_id)) { // If id key, check if id value matches find_id
|
||||||
|
found_id = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool String(const char* str, SizeType length, bool copy) {
|
||||||
|
if (text_key) {
|
||||||
|
result = {str,length};
|
||||||
|
return false; // End parsing
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Irrelevant events
|
||||||
|
bool Null() { return true; }
|
||||||
|
bool Bool(bool b) { return true; }
|
||||||
|
bool Double(double d) { return true; }
|
||||||
|
bool Int(int i) { return true; }
|
||||||
|
bool Int64(int64_t i) { return true; }
|
||||||
|
bool Uint(unsigned i) { return true; }
|
||||||
|
bool RawNumber(const char* str, SizeType length, bool copy) { return true; }
|
||||||
|
bool StartObject() { return true; }
|
||||||
|
bool EndObject(SizeType memberCount) { return true; }
|
||||||
|
bool StartArray() { return true; }
|
||||||
|
bool EndArray(SizeType elementCount) { return true; }
|
||||||
|
}; // handler
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
|
Reader reader;
|
||||||
|
Handler handler(result,find_id);
|
||||||
|
InsituStringStream ss(json.data());
|
||||||
|
reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag | kParseFullPrecisionFlag>(ss,handler);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}; // rapidjson_sax
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, rapidjson_sax)->UseManualTime();
|
||||||
|
} // namespace find_tweet
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_SAJSON
|
||||||
|
|
||||||
|
#include "find_tweet.h"
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
struct sajson {
|
||||||
|
using StringType=std::string_view;
|
||||||
|
|
||||||
|
size_t ast_buffer_size{0};
|
||||||
|
size_t *ast_buffer{nullptr};
|
||||||
|
~sajson() { free(ast_buffer); }
|
||||||
|
|
||||||
|
simdjson_inline std::string_view get_string_view(const ::sajson::value &obj, std::string_view key) {
|
||||||
|
auto val = obj.get_value_of_key({key.data(), key.length()});
|
||||||
|
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field is not a string"; }
|
||||||
|
return { val.as_cstring(), val.get_string_length() };
|
||||||
|
}
|
||||||
|
simdjson_inline uint64_t get_str_uint64(const ::sajson::value &obj, std::string_view key) {
|
||||||
|
// Since sajson only supports 53-bit numbers, and IDs in twitter.json can be > 53 bits, we read the corresponding id_str and parse that.
|
||||||
|
auto val = obj.get_value_of_key({key.data(), key.length()});
|
||||||
|
if (val.get_type() != ::sajson::TYPE_STRING) { throw "field not a string"; }
|
||||||
|
auto str = val.as_cstring();
|
||||||
|
char *endptr;
|
||||||
|
uint64_t result = strtoull(str, &endptr, 10);
|
||||||
|
if (endptr != &str[val.get_string_length()]) { throw "field is a string, but not an integer string"; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
|
if (!ast_buffer) {
|
||||||
|
ast_buffer_size = json.size();
|
||||||
|
ast_buffer = (size_t *)std::malloc(ast_buffer_size * sizeof(size_t));
|
||||||
|
}
|
||||||
|
auto doc = ::sajson::parse(
|
||||||
|
::sajson::bounded_allocation(ast_buffer, ast_buffer_size),
|
||||||
|
::sajson::mutable_string_view(json.size(), json.data())
|
||||||
|
);
|
||||||
|
if (!doc.is_valid()) { return false; }
|
||||||
|
|
||||||
|
auto root = doc.get_root();
|
||||||
|
if (root.get_type() != ::sajson::TYPE_OBJECT) { printf("a\n"); return false; }
|
||||||
|
auto statuses = root.get_value_of_key({"statuses", strlen("statuses")});
|
||||||
|
if (statuses.get_type() != ::sajson::TYPE_ARRAY) { return false; }
|
||||||
|
|
||||||
|
for (size_t i=0; i<statuses.get_length(); i++) {
|
||||||
|
auto tweet = statuses.get_array_element(i);
|
||||||
|
if (tweet.get_type() != ::sajson::TYPE_OBJECT) { printf("b\n"); return false; }
|
||||||
|
// TODO if there is a way to get the raw string, it might be faster to iota find_id and then
|
||||||
|
// compare it to each id_str, instead of parsing each int and comparing to find_id.
|
||||||
|
if (get_str_uint64(tweet, "id_str") == find_id) {
|
||||||
|
result = get_string_view(tweet, "text");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, sajson)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace find_tweet
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAJSON
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "find_tweet.h"
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
struct simdjson_dom {
|
||||||
|
using StringType=std::string_view;
|
||||||
|
|
||||||
|
dom::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
|
result = "";
|
||||||
|
auto doc = parser.parse(json);
|
||||||
|
for (auto tweet : doc["statuses"]) {
|
||||||
|
if (uint64_t(tweet["id"]) == find_id) {
|
||||||
|
result = tweet["text"];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, simdjson_dom)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace find_tweet
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
#include "find_tweet.h"
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
struct simdjson_ondemand {
|
||||||
|
using StringType=std::string_view;
|
||||||
|
|
||||||
|
ondemand::parser parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
|
// Walk the document, parsing as we go
|
||||||
|
auto doc = parser.iterate(json);
|
||||||
|
for (auto tweet : doc.find_field("statuses")) {
|
||||||
|
if (uint64_t(tweet.find_field("id")) == find_id) {
|
||||||
|
result = tweet.find_field("text");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, simdjson_ondemand)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace find_tweet
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SIMDJSON_COMPETITION_YYJSON
|
||||||
|
|
||||||
|
#include "find_tweet.h"
|
||||||
|
|
||||||
|
namespace find_tweet {
|
||||||
|
|
||||||
|
struct yyjson_base {
|
||||||
|
using StringType=std::string_view;
|
||||||
|
|
||||||
|
bool run(yyjson_doc *doc, uint64_t find_id, std::string_view &result) {
|
||||||
|
if (!doc) { return false; }
|
||||||
|
yyjson_val *root = yyjson_doc_get_root(doc);
|
||||||
|
if (!yyjson_is_obj(root)) { return false; }
|
||||||
|
yyjson_val *statuses = yyjson_obj_get(root, "statuses");
|
||||||
|
if (!yyjson_is_arr(statuses)) { return false; }
|
||||||
|
|
||||||
|
// Walk the document, parsing the tweets as we go
|
||||||
|
size_t tweet_idx, tweets_max;
|
||||||
|
yyjson_val *tweet;
|
||||||
|
yyjson_arr_foreach(statuses, tweet_idx, tweets_max, tweet) {
|
||||||
|
if (!yyjson_is_obj(tweet)) { return false; }
|
||||||
|
auto id = yyjson_obj_get(tweet, "id");
|
||||||
|
if (!yyjson_is_uint(id)) { return false; }
|
||||||
|
if (yyjson_get_uint(id) == find_id) {
|
||||||
|
auto text = yyjson_obj_get(tweet, "text");
|
||||||
|
if (yyjson_is_str(id)) { return false; }
|
||||||
|
result = { yyjson_get_str(text), yyjson_get_len(text) };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct yyjson : yyjson_base {
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
|
return yyjson_base::run(yyjson_read(json.data(), json.size(), 0), find_id, result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, yyjson)->UseManualTime();
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
struct yyjson_insitu : yyjson_base {
|
||||||
|
bool run(simdjson::padded_string &json, uint64_t find_id, std::string_view &result) {
|
||||||
|
return yyjson_base::run(yyjson_read_opts(json.data(), json.size(), YYJSON_READ_INSITU, 0, 0), find_id, result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
BENCHMARK_TEMPLATE(find_tweet, yyjson_insitu)->UseManualTime();
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
} // namespace find_tweet
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_YYJSON
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
class ChunkWriter
|
|
||||||
def initialize(output_dir, miss_templates, file_size=640*1000, block_size=64)
|
|
||||||
@@output_dir = output_dir
|
|
||||||
@@miss_templates = miss_templates
|
|
||||||
@@file_size = file_size
|
|
||||||
@@block_size = block_size
|
|
||||||
end
|
|
||||||
|
|
||||||
def prepare_chunk(chunks, include_newline)
|
|
||||||
Array(chunks).map do |chunk|
|
|
||||||
"#{chunk}#{' '*(@@block_size-chunk.bytesize-1)}#{include_newline ? "\n" : " "}"
|
|
||||||
end.join("")
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_files(filename, start1, repeat1, end1, repeat2: '', include_newline: true)
|
|
||||||
start1 = prepare_chunk(start1, include_newline)
|
|
||||||
repeat1 = prepare_chunk(repeat1, include_newline)
|
|
||||||
end1 = prepare_chunk(end1, include_newline)
|
|
||||||
write_full(File.join(@@output_dir, "#{filename}-full.json"), start1, repeat1, end1)
|
|
||||||
|
|
||||||
repeat2 = prepare_chunk(repeat2, include_newline)
|
|
||||||
repeat2 = repeat2 * (repeat1.bytesize/repeat2.bytesize)
|
|
||||||
write_half(File.join(@@output_dir, "#{filename}.json"), start1, repeat1, end1, repeat2)
|
|
||||||
write_half_miss(File.join(@@output_dir, "#{filename}-miss.json"), start1, repeat1, end1, repeat2)
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_full(filename, start1, repeat1, end1)
|
|
||||||
puts "Writing #{filename} ..."
|
|
||||||
File.open(filename, "w") do |file|
|
|
||||||
write_chunks(file, start1, repeat1, end1, @@file_size)
|
|
||||||
end
|
|
||||||
raise "OMG wrong file size #{File.size(filename)} (should be #{@@file_size})" if File.size(filename) != @@file_size
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_half(filename, start1, repeat1, end1, repeat2)
|
|
||||||
# repeat1 is already represented in start1 and end1, so it doesn't need quite
|
|
||||||
# half the iterations.
|
|
||||||
repeat1_len = (@@file_size/2) - start1.bytesize - end1.bytesize
|
|
||||||
halfway_point = start1.bytesize + repeat1_len + repeat2.bytesize
|
|
||||||
|
|
||||||
puts "Writing #{filename} ..."
|
|
||||||
File.open(filename, "w") do |file|
|
|
||||||
write_chunks(file, start1, repeat1, repeat2, halfway_point)
|
|
||||||
write_chunks(file, repeat2, repeat2, end1, @@file_size-halfway_point)
|
|
||||||
end
|
|
||||||
raise "OMG wrong file size #{File.size(filename)} (should be #{@@file_size})" if File.size(filename) != @@file_size
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_half_miss(filename, start1, repeat1, end1, repeat2)
|
|
||||||
miss_template = Array(File.read(File.join(@@miss_templates, "#{repeat1.bytesize}.txt")).chomp.split("\n"))
|
|
||||||
# Take the start and end out of the template
|
|
||||||
repeat_template = miss_template[(start1.bytesize/64)..(-end1.bytesize/64-1)]
|
|
||||||
# If repeat is 128 bytes, each *pair* of elements is set. Use that.
|
|
||||||
repeat_chunks = repeat1.bytesize/64
|
|
||||||
repeat_template = (repeat_chunks - 1).step(repeat_template.size - 1, repeat_chunks).map { |i| repeat_template[i] }
|
|
||||||
|
|
||||||
puts "Writing #{filename} ..."
|
|
||||||
File.open(filename, "w") do |file|
|
|
||||||
file.write(start1)
|
|
||||||
repeat_template.each do |should_repeat|
|
|
||||||
file.write(should_repeat == "1" ? repeat1 : repeat2)
|
|
||||||
end
|
|
||||||
file.write(end1)
|
|
||||||
end
|
|
||||||
raise "OMG wrong file size #{File.size(filename)} (should be #{@@file_size})" if File.size(filename) != @@file_size
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_chunks(file, start1, repeat1, end1, size)
|
|
||||||
pos = 0
|
|
||||||
file.write(start1)
|
|
||||||
pos += start1.bytesize
|
|
||||||
|
|
||||||
repeat_end = size-end1.bytesize
|
|
||||||
loop do
|
|
||||||
file.write(repeat1)
|
|
||||||
pos += repeat1.bytesize
|
|
||||||
break if pos >= repeat_end
|
|
||||||
end
|
|
||||||
|
|
||||||
file.write(end1)
|
|
||||||
pos += end1.bytesize
|
|
||||||
return pos
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
output_dir = File.expand_path("../jsonexamples/generated", File.dirname(__FILE__))
|
|
||||||
miss_templates = File.expand_path("miss-templates", File.dirname(__FILE__))
|
|
||||||
Dir.mkdir(output_dir) unless File.directory?(output_dir)
|
|
||||||
w = ChunkWriter.new(output_dir, miss_templates)
|
|
||||||
w.write_files "utf-8", '["֏","֏",{}', ',"֏","֏",{}', ',"֏","֏","֏"]', repeat2: ',"ab","ab",{}'
|
|
||||||
w.write_files "escape", '["\\"","\\"",{}', ',"\\"","\\"",{}', ',"\\"","\\"","\\""]', repeat2: ',"ab","ab",{}'
|
|
||||||
w.write_files "0-structurals", '"ab"', '', ''
|
|
||||||
# w.write_files "1-structurals", [ '[', '"ab"' ], [ ',', '"ab"' ], [ ',', '{', '}', ']' ]
|
|
||||||
# w.write_files "2-structurals", '["ab"', ',"ab"', [',{', '}]']
|
|
||||||
# w.write_files "3-structurals", '[{}', ',{}', ',"ab"]'
|
|
||||||
# w.write_files "4-structurals", '["ab","ab"', ',"ab","ab"', ',{}]'
|
|
||||||
# w.write_files "5-structurals", '["ab",{}', ',"ab",{}', ',"ab","ab"]'
|
|
||||||
# w.write_files "6-structurals", '["ab","ab","ab"', ',"ab","ab","ab"', ',"ab",{}]'
|
|
||||||
w.write_files "7-structurals", '["ab","ab",{}', ',"ab","ab",{}', ',"ab","ab","ab"]'
|
|
||||||
# w.write_files "8-structurals", '["ab","ab","ab","ab"', ',"ab","ab","ab","ab"', ',"ab","ab",{}]'
|
|
||||||
# w.write_files "9-structurals", '["ab","ab","ab",{}', ',"ab","ab","ab",{}', ',"ab","ab","ab","ab"]'
|
|
||||||
# w.write_files "10-structurals", '["ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab"', ',"ab","ab","ab",{}]'
|
|
||||||
# w.write_files "11-structurals", '["ab","ab","ab","ab",{}', ',"ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab"]'
|
|
||||||
# w.write_files "12-structurals", '["ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab",{}]'
|
|
||||||
# w.write_files "13-structurals", '["ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab"]'
|
|
||||||
# w.write_files "14-structurals", '["ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab",{}]'
|
|
||||||
w.write_files "15-structurals", '["ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab"]'
|
|
||||||
# w.write_files "16-structurals", '["ab","ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab",{}]'
|
|
||||||
# w.write_files "17-structurals", '["ab","ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab","ab"]'
|
|
||||||
# w.write_files "18-structurals", '["ab","ab","ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab","ab",{}]'
|
|
||||||
# w.write_files "19-structurals", '["ab","ab","ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab"]'
|
|
||||||
# w.write_files "20-structurals", '["ab","ab","ab","ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab","ab","ab",{}]'
|
|
||||||
# w.write_files "21-structurals", '["ab","ab","ab","ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab","ab"]'
|
|
||||||
# w.write_files "22-structurals", '["ab","ab","ab","ab","ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab","ab","ab"', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab",{}]'
|
|
||||||
w.write_files "23-structurals", '["ab","ab","ab","ab","ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab","ab",{}', ',"ab","ab","ab","ab","ab","ab","ab","ab","ab","ab","ab"]'
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
def gen_seeds(start_blocks, repeat_blocks, end_blocks)
|
|
||||||
total_size = 640*1000
|
|
||||||
total_blocks = total_size/64
|
|
||||||
seed_space = 1..1000000
|
|
||||||
target_blocks = total_blocks*0.5
|
|
||||||
target_flips = total_blocks*0.25
|
|
||||||
percent_flips = 0.25*repeat_blocks
|
|
||||||
|
|
||||||
puts "Seeds for #{start_blocks} start blocks, #{end_blocks} end blocks and #{repeat_blocks} repeat blocks: #{percent_flips*100}% flips"
|
|
||||||
closest_flips = nil
|
|
||||||
closest_seeds = []
|
|
||||||
seed_space.each do |seed|
|
|
||||||
r = Random.new(seed)
|
|
||||||
# First block is always type 1
|
|
||||||
flips = 0
|
|
||||||
type1 = true
|
|
||||||
type1_blocks = start_blocks
|
|
||||||
finished_blocks = start_blocks
|
|
||||||
last_repeat = total_blocks-end_blocks
|
|
||||||
while finished_blocks < last_repeat
|
|
||||||
if r.rand < percent_flips
|
|
||||||
flips += 1
|
|
||||||
type1 = !type1
|
|
||||||
end
|
|
||||||
type1_blocks += repeat_blocks if type1
|
|
||||||
finished_blocks += repeat_blocks
|
|
||||||
end
|
|
||||||
|
|
||||||
# Last one is always type 1
|
|
||||||
flips += 1 if !type1
|
|
||||||
type1 = true
|
|
||||||
type1_blocks += end_blocks
|
|
||||||
finished_blocks += end_blocks
|
|
||||||
|
|
||||||
raise "simulated the wrong number of blocks #{finished_blocks}" if finished_blocks != total_blocks
|
|
||||||
|
|
||||||
if type1_blocks == target_blocks
|
|
||||||
if flips == target_flips
|
|
||||||
puts seed
|
|
||||||
closest_seeds << seed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
puts closest_seeds
|
|
||||||
end
|
|
||||||
|
|
||||||
gen_seeds(1,1,1)
|
|
||||||
gen_seeds(1,1,2)
|
|
||||||
gen_seeds(2,2,4)
|
|
||||||
@@ -4,15 +4,16 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
never_inline
|
// Gigabyte: https://en.wikipedia.org/wiki/Gigabyte
|
||||||
|
simdjson_never_inline
|
||||||
double bench(std::string filename, simdjson::padded_string& p) {
|
double bench(std::string filename, simdjson::padded_string& p) {
|
||||||
std::chrono::time_point<std::chrono::steady_clock> start_clock =
|
std::chrono::time_point<std::chrono::steady_clock> start_clock =
|
||||||
std::chrono::steady_clock::now();
|
std::chrono::steady_clock::now();
|
||||||
simdjson::padded_string::load(filename).first.swap(p);
|
simdjson::padded_string::load(filename).value_unsafe().swap(p);
|
||||||
std::chrono::time_point<std::chrono::steady_clock> end_clock =
|
std::chrono::time_point<std::chrono::steady_clock> end_clock =
|
||||||
std::chrono::steady_clock::now();
|
std::chrono::steady_clock::now();
|
||||||
std::chrono::duration<double> elapsed = end_clock - start_clock;
|
std::chrono::duration<double> elapsed = end_clock - start_clock;
|
||||||
return (p.size() / (1024. * 1024 * 1024.)) / elapsed.count();
|
return (static_cast<double>(p.size()) / (1000000000.)) / elapsed.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
@@ -28,12 +29,12 @@ int main(int argc, char *argv[]) {
|
|||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
simdjson::padded_string p;
|
simdjson::padded_string p;
|
||||||
bench(filename, p);
|
bench(filename, p);
|
||||||
double meanval = 0;
|
double meanval = 0;
|
||||||
double maxval = 0;
|
double maxval = 0;
|
||||||
double minval = 10000;
|
double minval = 10000;
|
||||||
std::cout << "file size: "<< (p.size() / (1024. * 1024 * 1024.)) << " GB" <<std::endl;
|
std::cout << "file size: "<< (static_cast<double>(p.size()) / (1000000000.)) << " GB" <<std::endl;
|
||||||
size_t times = p.size() > 1024*1024*1024 ? 5 : 50;
|
size_t times = p.size() > 1000000000 ? 5 : 50;
|
||||||
#if __cpp_exceptions
|
#if __cpp_exceptions
|
||||||
try {
|
try {
|
||||||
#endif
|
#endif
|
||||||
@@ -49,7 +50,7 @@ std::cout << "file size: "<< (p.size() / (1024. * 1024 * 1024.)) << " GB" <<std
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
std::cout << "average speed: " << meanval / times << " GB/s"<< std::endl;
|
std::cout << "average speed: " << meanval / static_cast<double>(times) << " GB/s"<< std::endl;
|
||||||
std::cout << "min speed : " << minval << " GB/s" << std::endl;
|
std::cout << "min speed : " << minval << " GB/s" << std::endl;
|
||||||
std::cout << "max speed : " << maxval << " GB/s" << std::endl;
|
std::cout << "max speed : " << maxval << " GB/s" << std::endl;
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "json_benchmark/file_runner.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace json_benchmark;
|
||||||
|
|
||||||
|
template <typename I> struct runner : public file_runner<I> {
|
||||||
|
std::string_view result;
|
||||||
|
std::unique_ptr<char[]> buffer;
|
||||||
|
|
||||||
|
bool setup(benchmark::State &state) {
|
||||||
|
bool isok = this->load_json(state, TWITTER_JSON);
|
||||||
|
if (isok) {
|
||||||
|
// Let us allocate a sizeable buffer.
|
||||||
|
buffer = std::unique_ptr<char[]>(new char[this->json.size() * 4 + 1024]);
|
||||||
|
}
|
||||||
|
return isok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool before_run(benchmark::State &state) {
|
||||||
|
if (!file_runner<I>::before_run(state)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Clear the buffer.
|
||||||
|
::memset(buffer.get(), 0, this->json.size() * 4 + 1024);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool run(benchmark::State &) {
|
||||||
|
return this->implementation.run(this->json, buffer.get(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R>
|
||||||
|
bool diff(benchmark::State &state, runner<R> &reference) {
|
||||||
|
return diff_results(state, result.size(), reference.result.size(), diff_flags::NONE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct simdjson_ondemand;
|
||||||
|
|
||||||
|
template <typename I>
|
||||||
|
simdjson_inline static void json2msgpack(benchmark::State &state) {
|
||||||
|
run_json_benchmark<runner<I>, runner<simdjson_ondemand>>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifdef SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace nlohmann;
|
||||||
|
|
||||||
|
struct nlohmann_json2msgpack {
|
||||||
|
inline std::string_view to_msgpack(const simdjson::padded_string &json,
|
||||||
|
uint8_t *buf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void write_double(const double d) noexcept;
|
||||||
|
inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
inline void write_string(const std::string& str);
|
||||||
|
inline void recursive_processor(basic_json<> element);
|
||||||
|
|
||||||
|
uint8_t *buff{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string_view nlohmann_json2msgpack::to_msgpack(const simdjson::padded_string &json,
|
||||||
|
uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
auto val = nlohmann::json::parse(json.data(), json.data() + json.size());
|
||||||
|
recursive_processor(val);
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::write_string(const std::string & str) {
|
||||||
|
write_byte(0xdb);
|
||||||
|
write_uint32(uint32_t(str.size()));
|
||||||
|
::memcpy(buff, str.data(), str.size());
|
||||||
|
buff += str.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nlohmann_json2msgpack::recursive_processor(json element) {
|
||||||
|
switch (element.type()) {
|
||||||
|
case nlohmann::detail::value_t::array: {
|
||||||
|
uint32_t counter = 0;
|
||||||
|
write_byte(0xdd);
|
||||||
|
std::vector<json> array = element.get<std::vector<json>>();
|
||||||
|
write_uint32(uint32_t(array.size()));
|
||||||
|
for (auto child : array) {
|
||||||
|
recursive_processor(child);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case nlohmann::detail::value_t::object: {
|
||||||
|
write_byte(0xdf);
|
||||||
|
std::map<std::string,json> object = element.get<std::map<std::string,json>>();
|
||||||
|
write_uint32(uint32_t(object.size()));
|
||||||
|
for (auto field : object) {
|
||||||
|
write_string(field.first);
|
||||||
|
recursive_processor(field.second);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case nlohmann::detail::value_t::number_integer:
|
||||||
|
case nlohmann::detail::value_t::number_unsigned:
|
||||||
|
case nlohmann::detail::value_t::number_float:
|
||||||
|
write_double(double(element));
|
||||||
|
break;
|
||||||
|
case nlohmann::detail::value_t::string:
|
||||||
|
write_string(std::string(element));
|
||||||
|
break;
|
||||||
|
case nlohmann::detail::value_t::boolean:
|
||||||
|
write_byte(0xc2 + bool(element));
|
||||||
|
break;
|
||||||
|
case nlohmann::detail::value_t::null:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
case nlohmann::detail::value_t::discarded:
|
||||||
|
case nlohmann::detail::value_t::binary:
|
||||||
|
default:
|
||||||
|
printf("unexpected\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nlohmann_json {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
|
||||||
|
nlohmann_json2msgpack parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
result = parser.to_msgpack(json, reinterpret_cast<uint8_t *>(buffer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, nlohmann_json)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_NLOHMANN_JSON
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifdef SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
struct rapidjson2msgpack {
|
||||||
|
inline std::string_view to_msgpack(char *json, uint8_t *buf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void write_double(const double d) noexcept;
|
||||||
|
inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
inline void write_uint32_at(const uint32_t w, uint8_t *p) noexcept;
|
||||||
|
void write_string(const char * s, size_t length) noexcept;
|
||||||
|
inline void recursive_processor(Value &v);
|
||||||
|
|
||||||
|
uint8_t *buff{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
std::string_view rapidjson2msgpack<parseflag>::to_msgpack(char *json, uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
Document doc{};
|
||||||
|
if(parseflag & kParseInsituFlag) {
|
||||||
|
doc.ParseInsitu<parseflag>(json);
|
||||||
|
} else {
|
||||||
|
doc.Parse<parseflag>(json);
|
||||||
|
}
|
||||||
|
recursive_processor(doc);
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_string(const char * c, size_t len) noexcept {
|
||||||
|
write_byte(0xdb);
|
||||||
|
write_uint32(uint32_t(len));
|
||||||
|
::memcpy(buff, c, len);
|
||||||
|
buff += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::write_uint32_at(const uint32_t w, uint8_t *p) noexcept {
|
||||||
|
::memcpy(p, &w, sizeof(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
void rapidjson2msgpack<parseflag>::recursive_processor(Value &v) {
|
||||||
|
switch (v.GetType()) {
|
||||||
|
case kArrayType:
|
||||||
|
write_byte(0xdd);
|
||||||
|
write_uint32(v.Size());
|
||||||
|
for (Value::ValueIterator i = v.Begin(); i != v.End(); ++i) {
|
||||||
|
recursive_processor(*i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kObjectType:
|
||||||
|
write_byte(0xdf);
|
||||||
|
write_uint32(uint32_t(v.MemberEnd()-v.MemberBegin()));
|
||||||
|
for (Value::MemberIterator m = v.MemberBegin(); m != v.MemberEnd();
|
||||||
|
++m) {
|
||||||
|
write_string(m->name.GetString(), m->name.GetStringLength());
|
||||||
|
recursive_processor(m->value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kStringType:
|
||||||
|
write_string(v.GetString(), v.GetStringLength());
|
||||||
|
break;
|
||||||
|
case kNumberType:
|
||||||
|
write_double(v.GetDouble());
|
||||||
|
break;
|
||||||
|
case kFalseType:
|
||||||
|
write_byte(0xc2);
|
||||||
|
break;
|
||||||
|
case kTrueType:
|
||||||
|
write_byte(0xc3);
|
||||||
|
break;
|
||||||
|
case kNullType:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int parseflag>
|
||||||
|
struct rapidjson_base {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
|
||||||
|
rapidjson2msgpack<parseflag> parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
result =
|
||||||
|
parser.to_msgpack(json.data(), reinterpret_cast<uint8_t *>(buffer));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
using rapidjson = rapidjson_base<kParseValidateEncodingFlag|kParseFullPrecisionFlag>;
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, rapidjson)->UseManualTime();
|
||||||
|
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_APPROX
|
||||||
|
using rapidjson_approx = rapidjson_base<kParseValidateEncodingFlag>;
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, rapidjson_approx)->UseManualTime();
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_APPROX
|
||||||
|
|
||||||
|
#if SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
using rapidjson_insitu = rapidjson_base<kParseValidateEncodingFlag|kParseInsituFlag>;
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, rapidjson_insitu)->UseManualTime();
|
||||||
|
#endif // SIMDJSON_COMPETITION_ONDEMAND_INSITU
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_RAPIDJSON
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifdef SIMDJSON_COMPETITION_SAJSON
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace sajson;
|
||||||
|
|
||||||
|
|
||||||
|
struct sajson2msgpack {
|
||||||
|
inline std::string_view to_msgpack(char *json, size_t size, uint8_t *buf);
|
||||||
|
virtual ~sajson2msgpack() { free(ast_buffer); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void write_double(const double d) noexcept;
|
||||||
|
inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
inline void write_string(const char * s, size_t length) noexcept;
|
||||||
|
inline void recursive_processor(const sajson::value &v);
|
||||||
|
|
||||||
|
uint8_t *buff{};
|
||||||
|
size_t ast_buffer_size{0};
|
||||||
|
size_t *ast_buffer{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::string_view sajson2msgpack::to_msgpack(char *json, size_t size, uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
|
||||||
|
if (!ast_buffer) {
|
||||||
|
ast_buffer_size = size;
|
||||||
|
ast_buffer = (size_t *)std::malloc(ast_buffer_size * sizeof(size_t));
|
||||||
|
}
|
||||||
|
auto doc = parse(
|
||||||
|
bounded_allocation(ast_buffer, ast_buffer_size),
|
||||||
|
mutable_string_view(size, json)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto root = doc.get_root();
|
||||||
|
recursive_processor(root);
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::write_string(const char * c, size_t len) noexcept {
|
||||||
|
write_byte(0xdb);
|
||||||
|
write_uint32(uint32_t(len));
|
||||||
|
::memcpy(buff, c, len);
|
||||||
|
buff += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sajson2msgpack::recursive_processor(const sajson::value &node) {
|
||||||
|
using namespace sajson;
|
||||||
|
switch (node.get_type()) {
|
||||||
|
case TYPE_NULL:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
case TYPE_FALSE:
|
||||||
|
write_byte(0xc2);
|
||||||
|
break;
|
||||||
|
case TYPE_TRUE:
|
||||||
|
write_byte(0xc3);
|
||||||
|
break;
|
||||||
|
case TYPE_ARRAY: {
|
||||||
|
auto length = node.get_length();
|
||||||
|
write_byte(0xdf);
|
||||||
|
write_uint32(uint32_t(length));
|
||||||
|
for (size_t i = 0; i < length; ++i) {
|
||||||
|
recursive_processor(node.get_array_element(i));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TYPE_OBJECT: {
|
||||||
|
auto length = node.get_length();
|
||||||
|
write_byte(0xdd);
|
||||||
|
write_uint32(uint32_t(length));
|
||||||
|
for (auto i = 0u; i < length; ++i) {
|
||||||
|
auto s = node.get_object_key(i);
|
||||||
|
write_string(s.data(), s.length());
|
||||||
|
recursive_processor(node.get_object_value(i));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TYPE_STRING:
|
||||||
|
write_string(node.as_cstring(), node.get_string_length());
|
||||||
|
break;
|
||||||
|
case TYPE_DOUBLE:
|
||||||
|
case TYPE_INTEGER:
|
||||||
|
write_double(node.get_number_value());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false && "unknown node type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct sajson {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
|
||||||
|
sajson2msgpack parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
result =
|
||||||
|
parser.to_msgpack(json.data(), json.size(), reinterpret_cast<uint8_t *>(buffer));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, sajson)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_COMPETITION_SAJSON
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
#pragma once
|
||||||
|
#if SIMDJSON_EXCEPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
#include "json2msgpack.h"
|
||||||
|
|
||||||
|
namespace json2msgpack {
|
||||||
|
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
struct simdjsondom2msgpack {
|
||||||
|
/**
|
||||||
|
* @brief Converts the provided JSON into msgpack.
|
||||||
|
*
|
||||||
|
* @param json JSON input
|
||||||
|
* @param buf temporary buffer (must be large enough, with simdjson::SIMDJSON_PADDING bytes
|
||||||
|
* of padding)
|
||||||
|
* @return std::string_view msgpack output, writting to the temporary buffer
|
||||||
|
*/
|
||||||
|
inline std::string_view to_msgpack(const simdjson::padded_string &json,
|
||||||
|
uint8_t *buf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
simdjson_really_inline void write_double(const double d) noexcept;
|
||||||
|
simdjson_really_inline void write_string(const std::string_view v) noexcept;
|
||||||
|
simdjson_really_inline void write_byte(const uint8_t b) noexcept;
|
||||||
|
simdjson_really_inline void write_uint32(const uint32_t w) noexcept;
|
||||||
|
simdjson_really_inline uint8_t *skip_uint32() noexcept;
|
||||||
|
simdjson_really_inline void write_uint32_at(const uint32_t w,
|
||||||
|
uint8_t *p) noexcept;
|
||||||
|
|
||||||
|
inline void recursive_processor(simdjson::dom::element element);
|
||||||
|
|
||||||
|
dom::parser parser;
|
||||||
|
uint8_t *buff{};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string_view
|
||||||
|
simdjsondom2msgpack::to_msgpack(const simdjson::padded_string &json,
|
||||||
|
uint8_t *buf) {
|
||||||
|
buff = buf;
|
||||||
|
|
||||||
|
recursive_processor(parser.parse(json));
|
||||||
|
return std::string_view(reinterpret_cast<char *>(buf), size_t(buff - buf));
|
||||||
|
}
|
||||||
|
void simdjsondom2msgpack::write_string(const std::string_view v) noexcept {
|
||||||
|
write_byte(0xdb);
|
||||||
|
write_uint32(uint32_t(v.size()));
|
||||||
|
::memcpy(buff, v.data(), v.size());
|
||||||
|
buff += v.size();
|
||||||
|
}
|
||||||
|
void simdjsondom2msgpack::write_double(const double d) noexcept {
|
||||||
|
*buff++ = 0xcb;
|
||||||
|
::memcpy(buff, &d, sizeof(d));
|
||||||
|
buff += sizeof(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjsondom2msgpack::write_byte(const uint8_t b) noexcept {
|
||||||
|
*buff = b;
|
||||||
|
buff++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjsondom2msgpack::write_uint32(const uint32_t w) noexcept {
|
||||||
|
::memcpy(buff, &w, sizeof(w));
|
||||||
|
buff += sizeof(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *simdjsondom2msgpack::skip_uint32() noexcept {
|
||||||
|
uint8_t *ret = buff;
|
||||||
|
buff += sizeof(uint32_t);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void simdjsondom2msgpack::write_uint32_at(const uint32_t w, uint8_t *p) noexcept {
|
||||||
|
::memcpy(p, &w, sizeof(w));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void simdjsondom2msgpack::recursive_processor(simdjson::dom::element element) {
|
||||||
|
switch (element.type()) {
|
||||||
|
case dom::element_type::ARRAY: {
|
||||||
|
uint32_t counter = 0;
|
||||||
|
write_byte(0xdd);
|
||||||
|
uint8_t *location = skip_uint32();
|
||||||
|
for (auto child : dom::array(element)) {
|
||||||
|
counter++;
|
||||||
|
recursive_processor(child);
|
||||||
|
}
|
||||||
|
write_uint32_at(counter, location);}
|
||||||
|
break;
|
||||||
|
case dom::element_type::OBJECT:{
|
||||||
|
uint32_t counter = 0;
|
||||||
|
write_byte(0xdf);
|
||||||
|
uint8_t *location = skip_uint32();
|
||||||
|
for (dom::key_value_pair field : dom::object(element)) {
|
||||||
|
counter++;
|
||||||
|
write_string(field.key);
|
||||||
|
recursive_processor(field.value);
|
||||||
|
}
|
||||||
|
write_uint32_at(counter, location);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case dom::element_type::INT64:
|
||||||
|
case dom::element_type::UINT64:
|
||||||
|
case dom::element_type::DOUBLE:
|
||||||
|
write_double( double(element));
|
||||||
|
break;
|
||||||
|
case dom::element_type::STRING:
|
||||||
|
write_string(std::string_view(element));
|
||||||
|
break;
|
||||||
|
case dom::element_type::BOOL:
|
||||||
|
write_byte(0xc2 + bool(element));
|
||||||
|
break;
|
||||||
|
case dom::element_type::NULL_VALUE:
|
||||||
|
write_byte(0xc0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct simdjson_dom {
|
||||||
|
using StringType = std::string_view;
|
||||||
|
|
||||||
|
simdjsondom2msgpack parser{};
|
||||||
|
|
||||||
|
bool run(simdjson::padded_string &json, char *buffer,
|
||||||
|
std::string_view &result) {
|
||||||
|
result = parser.to_msgpack(json, reinterpret_cast<uint8_t *>(buffer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BENCHMARK_TEMPLATE(json2msgpack, simdjson_dom)->UseManualTime();
|
||||||
|
|
||||||
|
} // namespace json2msgpack
|
||||||
|
|
||||||
|
#endif // SIMDJSON_EXCEPTIONS
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user