1/*
2 * Copyright (c) 2014-2023 National Technology and Engineering
3 * Solutions of Sandia, LLC. Under the terms of Contract DE-NA0003525
4 * with National Technology and Engineering Solutions of Sandia, LLC,
5 * the U.S. Government retains certain rights in this software.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31static constexpr auto helpmsg = R"(
32--------------------------------------------------------------------------------
33Compare storage costs for various methods of serializing trajectories
34
35 We have three ways to save trajectories:
36
37 1. tracktable::TrajectoryWriter (C++, Python)
38 This uses our own home-grown delimited text format. It is rather
39 verbose.
40
41 2. tracktable.rw.read_write_json (Python)
42 Write to JSON. This is also rather verbose and has trouble with
43 incremental loads.
44
45 3. boost::serialization
46 Write to Boost's archive format (text, binary or XML).
47
48 This example runs #1 and #3 on a sample trajectory and compares the
49 storage requirements.
50
51 This example demonstrates:
52 - use of boost program options
53 - use of boost archives
54 - use of trajectory writer
55 - Manual construction of points and trajectories
56
57Typical use:
58 ./serialize_trajectories --trajectory-count=100 --point-count=100
59--------------------------------------------------------------------------------)";
60
61#include <tracktable/Core/TracktableCommon.h>
62#include <tracktable/Domain/Terrestrial.h>
63#include <tracktable/RW/TrajectoryWriter.h>
64
65#include <boost/archive/binary_oarchive.hpp>
66#include <boost/archive/text_oarchive.hpp>
67#include <boost/archive/xml_oarchive.hpp>
68#include <boost/program_options.hpp>
69#include <boost/serialization/vector.hpp>
70
71#include <iostream>
72#include <locale>
73#include <sstream>
74#include <vector>
75
76namespace bpo = boost::program_options;
77
78using TrajectoryT = tracktable::domain::terrestrial::trajectory_type;
79using PointT = tracktable::domain::terrestrial::trajectory_point_type;
80
81/** @brief Creates a trajectory with a set number of points
82 * @param _numPoints The number of points to create
83 * @return The resulting trajectory */
84TrajectoryT build_trajectory(int _numPoints = 100);
85
86/** @brief Get the size of tracktable trajectory writer stored data
87 * @param trajectories Vector of trajectories to store
88 * @return size of stored data */
89std::size_t get_tracktable_trajectory_writer_size(std::vector<TrajectoryT> const& trajectories);
90
91/** @brief Get the size of boost text archive
92 * @param trajectories Vector of trajectories to store
93 * @return size of stored data */
94std::size_t get_boost_text_size(std::vector<TrajectoryT> const& trajectories);
95
96/** @brief Get the size of boost binary archive
97 * @param trajectories Vector of trajectories to store
98 * @return size of stored data */
99std::size_t get_boost_binary_size(std::vector<TrajectoryT> const& trajectories);
100
101/** @brief Get the size of boost xml archive
102 * @param trajectories Vector of trajectories to store
103 * @return size of stored data */
104std::size_t get_boost_xml_size(std::vector<TrajectoryT> const& trajectories);
105
106int main(int _argc, char* _argv[]) {
107 auto trajectoryCount = 100u;
108 auto pointsPerTrajectory = 100u;
109
110 boost::program_options::options_description commandLineOptions("Options");
111 // clang-format off
112 commandLineOptions.add_options()
113 ("help", "Print help")
114 ("trajectory-count",bpo::value(&trajectoryCount)->default_value(100u),
115 "number of trajectories to use")
116 ("point-count",bpo::value(&pointsPerTrajectory)->default_value(100u),
117 "number of points per trajectory")
118 ;
119 // clang-format on
120 auto vm = std::make_shared<boost::program_options::variables_map>();
121
122 // Parse the command lines, don't forget the 'notify' after
123 try {
124 // We use this try/catch to automatically display help when an unknown option is used
125 boost::program_options::store(
126 boost::program_options::command_line_parser(_argc, _argv).options(commandLineOptions).run(), *vm);
127 boost::program_options::notify(*vm);
128 } catch (boost::program_options::error e) {
129 std::cerr << e.what();
130 std::cerr << helpmsg << "\n\n";
131 std::cerr << commandLineOptions << std::endl;
132 return 1;
133 }
134 /** Parsing will give an error of an incorrect option is used, but it won't
135 * display the help unless we tell it too */
136 if (vm->count("help") != 0) {
137 std::cerr << helpmsg << "\n\n";
138 std::cerr << commandLineOptions << std::endl;
139 return 1;
140 }
141
142 // Construct test trajectories
143 std::vector<TrajectoryT> trajectories;
144 for (auto i = 0u; i < trajectoryCount; ++i) {
145 trajectories.push_back(build_trajectory(pointsPerTrajectory + (i % 11)));
146 }
147
148 auto trajectoryWriterSize = get_tracktable_trajectory_writer_size(trajectories);
149 auto boostTextSize = get_boost_text_size(trajectories);
150 auto boostBinarySize = get_boost_binary_size(trajectories);
151 auto boostXmlSize = get_boost_xml_size(trajectories);
152
153 std::cout.imbue(std::locale("")); // Add commas to int
154 std::cout << "Storage comparison for different serialization formats\n";
155 std::cout << "Trajectories: " << trajectoryCount << "\n";
156 std::cout << "Points per trajectory: " << pointsPerTrajectory << "\n\n" << std::right;
157
158 std::cout << "\tTrajectoryWriter: " << std::setw(15) << trajectoryWriterSize << "\n";
159 std::cout << "\tboost::text_oarchive: " << std::setw(15) << boostTextSize << "\n";
160 std::cout << "\tboost::binary_oarchive: " << std::setw(15) << boostBinarySize << "\n";
161 std::cout << "\tboost::xml_oarchive: " << std::setw(15) << boostXmlSize << std::endl;
162
163 return 0;
164}
165
166TrajectoryT build_trajectory(int _numPoints /* = 100*/) {
167 static auto seed = 0; // Make each trajectory unique but deterministic
168 PointT initialPoint;
169 TrajectoryT trajectory;
170
171 initialPoint.set_object_id("MyPoint" + std::to_string(seed));
172 initialPoint[0] = (seed)-10;
173 initialPoint[1] = (seed) + 20;
174 initialPoint.set_timestamp(tracktable::time_from_string("2001-02-03 04:05:06") + tracktable::days(seed));
175
176 constexpr auto floatProperty = "test_float_property";
177 constexpr auto timestampProperty = "test_timestamp_property";
178 constexpr auto stringProperty = "test_string_property";
179
180 initialPoint.set_property(floatProperty, _numPoints + 456.789 / seed);
181 initialPoint.set_property(stringProperty, "Frodo lives!");
182 // a timestamp property is separate from the timestamp of the point itself.
183 initialPoint.set_property(
184 timestampProperty, tracktable::time_from_string("2000-01-02 03:04:05") + tracktable::days(seed * 30));
185
186 PointT p(initialPoint);
187 for (int i = 0; i < _numPoints; ++i) {
188 p[0] += 0.1;
189 p[1] += 0.15;
190 p.set_timestamp(p.timestamp() + tracktable::seconds(5));
191 p.set_property(floatProperty, p.real_property(floatProperty) + 1.1);
192 p.set_property(timestampProperty, p.timestamp_property(timestampProperty) + tracktable::hours(1));
193 trajectory.push_back(p); // push_back will copy contents, so we don't have to worry about changing p
194 }
195
196 trajectory.set_property(floatProperty, 11456.789 + seed);
197 trajectory.set_property(stringProperty, "Frodo lives! So does Gandalf!");
198 trajectory.set_property(timestampProperty,
199 tracktable::time_from_string("2001-02-03 04:05:06") + tracktable::minutes(seed));
200
201 seed += _numPoints;
202 return trajectory;
203}
204
205std::size_t get_tracktable_trajectory_writer_size(std::vector<TrajectoryT> const& trajectories) {
206 std::ostringstream outbuf;
207 tracktable::TrajectoryWriter writer(outbuf);
208 writer.write(trajectories.begin(), trajectories.end());
209 return outbuf.str().size();
210}
211
212std::size_t get_boost_text_size(std::vector<TrajectoryT> const& trajectories) {
213 std::ostringstream outbuf;
214 boost::archive::text_oarchive archive(outbuf);
215 archive& trajectories;
216 return outbuf.str().size();
217}
218
219std::size_t get_boost_binary_size(std::vector<TrajectoryT> const& trajectories) {
220 std::ostringstream outbuf;
221 boost::archive::binary_oarchive archive(outbuf);
222
223 archive& trajectories;
224 return outbuf.str().size();
225}
226
227std::size_t get_boost_xml_size(std::vector<TrajectoryT> const& trajectories) {
228 std::ostringstream outbuf;
229 boost::archive::xml_oarchive archive(outbuf);
230
231 archive << BOOST_SERIALIZATION_NVP(trajectories);
232 return outbuf.str().size();
233}