|
| 1 | +# PGO training workload for the torque NIF. |
| 2 | +# |
| 3 | +# Not a benchmark — it exercises every hot path (decode, encode, parse, get) |
| 4 | +# over representative small and large payloads so the Profile-Guided |
| 5 | +# Optimisation build (scripts/pgo-build.sh, and the release CI) can collect |
| 6 | +# realistic branch and call-frequency data. Run via that tooling with an |
| 7 | +# instrumented NIF; running it directly does nothing useful. |
| 8 | +# |
| 9 | +# It deliberately has NO external dependencies (JSON is built as strings rather |
| 10 | +# than via an encoder), so it runs in a bare mix environment — important for CI |
| 11 | +# where pulling the full bench dep tree would be heavy and fragile. |
| 12 | +# |
| 13 | +# Both a small (<20 KB, normal scheduler) and a large (>20 KB, dirty CPU |
| 14 | +# scheduler) payload are covered, since torque dispatches on input size. |
| 15 | + |
| 16 | +# Unicode is expressed as JSON \u escapes (ASCII source) so the file stays |
| 17 | +# parse-clean while still exercising the decoder's unescape and the encoder's |
| 18 | +# escape paths. |
| 19 | +small_json = |
| 20 | + ~s({"id":"req-0001","site":{"domain":"example.com","page":"https://example.com/articles/x","cat":["IAB1","IAB2-3"],"publisher":{"id":"pub-12345"}},"device":{"devicetype":2,"ua":"Mozilla/5.0 Macintosh; Intel Mac OS X 10_15_7 Chrome/120.0.0.0","ip":"203.0.113.42","geo":{"country":"US","lat":40.7128,"lon":-74.006,"zip":"10001"},"connectiontype":2},"user":{"id":"u-abcdef","name":"caf\\u00e9 r\\u00e9sum\\u00e9 \\u2728"},"imp":[{"id":"imp-1","banner":{"w":300,"h":250},"bidfloor":0.5},{"id":"imp-2","video":{"mimes":["video/mp4"],"maxduration":30},"bidfloor":2.0}],"regs":{"coppa":0},"ext":null,"test":true}) |
| 21 | + |
| 22 | +record = fn i -> |
| 23 | + ~s({"metadata":{"result_type":"recent","iso_language_code":"en"},"id":#{505_874_924_000_000_000 + i},"id_str":"#{505_874_924_000_000_000 + i}","text":"Sample tweet #{i} lorem ipsum dolor sit amet consectetur adipiscing elit","truncated":false,"in_reply_to_status_id":null,"user":{"id":#{1_000_000 + i},"screen_name":"username_#{i}","location":"San Francisco, CA","url":null,"followers_count":#{rem(i * 1337, 100_000)},"verified":false,"lang":"en","profile_image_url":"http://pbs.twimg.com/profile_images/#{i}/photo.jpeg"},"geo":null,"retweet_count":#{rem(i * 3, 1000)},"favorite_count":#{rem(i * 7, 2000)},"entities":{"hashtags":[{"text":"elixir","indices":[15,22]}],"urls":[],"user_mentions":[{"screen_name":"user_#{i}","id":#{2_000_000 + i}}]},"favorited":false,"lang":"en"}) |
| 24 | +end |
| 25 | + |
| 26 | +large_json = |
| 27 | + ~s({"statuses":[) <> |
| 28 | + Enum.map_join(1..200, ",", record) <> |
| 29 | + ~s(],"search_metadata":{"count":200,"completed_in":0.035,"max_id":505874924095815681,"query":"%23elixir"}}) |
| 30 | + |
| 31 | +small_term = Torque.decode!(small_json) |
| 32 | +large_term = Torque.decode!(large_json) |
| 33 | + |
| 34 | +to_proplist = fn f, v -> |
| 35 | + cond do |
| 36 | + is_map(v) -> {Enum.map(v, fn {k, val} -> {k, f.(f, val)} end)} |
| 37 | + is_list(v) -> Enum.map(v, &f.(f, &1)) |
| 38 | + true -> v |
| 39 | + end |
| 40 | +end |
| 41 | + |
| 42 | +small_proplist = to_proplist.(to_proplist, small_term) |
| 43 | +large_proplist = to_proplist.(to_proplist, large_term) |
| 44 | + |
| 45 | +fields = ~w(/id /site/domain /site/page /site/publisher/id /site/cat |
| 46 | + /device/devicetype /device/ua /device/ip /device/geo/country |
| 47 | + /device/geo/lat /device/connectiontype /user/id /imp /regs/coppa) |
| 48 | + |
| 49 | +IO.puts("PGO workload: small=#{byte_size(small_json)}B large=#{byte_size(large_json)}B") |
| 50 | + |
| 51 | +decode = fn -> |
| 52 | + Torque.decode!(small_json) |
| 53 | + Torque.decode!(large_json) |
| 54 | +end |
| 55 | + |
| 56 | +encode = fn -> |
| 57 | + Torque.encode!(small_term) |
| 58 | + Torque.encode!(large_term) |
| 59 | + Torque.encode_to_iodata(small_term) |
| 60 | + Torque.encode_to_iodata(large_term) |
| 61 | + Torque.encode!(small_proplist) |
| 62 | + Torque.encode!(large_proplist) |
| 63 | +end |
| 64 | + |
| 65 | +parse_get = fn -> |
| 66 | + {:ok, doc} = Torque.parse(small_json) |
| 67 | + {:ok, doc_uk} = Torque.parse(small_json, unique_keys: true) |
| 68 | + Enum.each(fields, &Torque.get(doc, &1)) |
| 69 | + Torque.get_many(doc, fields) |
| 70 | + Torque.get_many_nil(doc, fields) |
| 71 | + Torque.get_many(doc_uk, fields) |
| 72 | +end |
| 73 | + |
| 74 | +Enum.each(1..5_000, fn _ -> decode.() end) |
| 75 | +Enum.each(1..5_000, fn _ -> encode.() end) |
| 76 | +Enum.each(1..10_000, fn _ -> parse_get.() end) |
| 77 | + |
| 78 | +IO.puts("PGO workload complete") |
0 commit comments