Serialize Example Source Files

serialize_trajectories.cpp

  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}