|
|
@@ -30,9 +30,15 @@
|
|
|
|
|
|
set -eu
|
|
|
|
|
|
+readonly script_dir="$(dirname $0)"
|
|
|
+
|
|
|
+# Change this if your checkout of github.com/google/protobuf is in a different
|
|
|
+# location.
|
|
|
+readonly GOOGLE_PROTOBUF_CHECKOUT="$script_dir/../../protobuf"
|
|
|
+
|
|
|
function usage() {
|
|
|
cat >&2 <<EOF
|
|
|
-Usage: $0 [-p <true|false>] [-s2|-s3] <field count> <field type>
|
|
|
+Usage: $0 [-p <true|false>] [-s2|-s3] <field count> <field types...>
|
|
|
|
|
|
Currently supported field types:
|
|
|
int32, sint32, uint32, fixed32, sfixed32,
|
|
|
@@ -40,6 +46,10 @@ Currently supported field types:
|
|
|
float, double, string,
|
|
|
...and repeated variants of the above.
|
|
|
|
|
|
+ Additionally, you can specify "all" to run the harness
|
|
|
+ multiple times with all of the (non-repeated) field types
|
|
|
+ listed above.
|
|
|
+
|
|
|
Options:
|
|
|
-p <true|false>: Adds a packed option to each field.
|
|
|
-s[23]: Generate proto2 or proto3 syntax. proto3 is
|
|
|
@@ -83,83 +93,62 @@ EOF
|
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------
|
|
|
-# Functions for generating the Swift harness.
|
|
|
+# Functions for running harnesses and collecting results.
|
|
|
|
|
|
-function print_swift_set_field() {
|
|
|
- num=$1
|
|
|
- type=$2
|
|
|
+# Executes the test harness for a language under Instruments and concatenates
|
|
|
+# its results to the partial results file.
|
|
|
+function run_harness_and_concatenate_results() {
|
|
|
+ language="$1"
|
|
|
+ harness="$2"
|
|
|
+ partial_results="$3"
|
|
|
|
|
|
- case "$type" in
|
|
|
- repeated\ string)
|
|
|
- echo " for _ in 0..<repeatedCount {"
|
|
|
- echo " message.field$num.append(\"$((200+num))\")"
|
|
|
- echo " }"
|
|
|
- ;;
|
|
|
- repeated\ *)
|
|
|
- echo " for _ in 0..<repeatedCount {"
|
|
|
- echo " message.field$num.append($((200+num)))"
|
|
|
- echo " }"
|
|
|
- ;;
|
|
|
- string)
|
|
|
- echo " message.field$num = \"$((200+num))\""
|
|
|
- ;;
|
|
|
- *)
|
|
|
- echo " message.field$num = $((200+num))"
|
|
|
- ;;
|
|
|
- esac
|
|
|
-}
|
|
|
-
|
|
|
-function generate_perf_harness() {
|
|
|
- cat >"$gen_harness_path" <<EOF
|
|
|
-extension Harness {
|
|
|
- func run() {
|
|
|
- measure {
|
|
|
- // Loop enough times to get meaningfully large measurements.
|
|
|
- for _ in 0..<runCount {
|
|
|
- var message = PerfMessage()
|
|
|
- measureSubtask("Populate message fields") {
|
|
|
- populateFields(of: &message)
|
|
|
- }
|
|
|
-
|
|
|
- // Exercise binary serialization.
|
|
|
- let data = try measureSubtask("Encode binary") {
|
|
|
- return try message.serializeProtobuf()
|
|
|
- }
|
|
|
- message = try measureSubtask("Decode binary") {
|
|
|
- return try PerfMessage(protobuf: data)
|
|
|
- }
|
|
|
-
|
|
|
- // Exercise JSON serialization.
|
|
|
- let json = try measureSubtask("Encode JSON") {
|
|
|
- return try message.serializeJSON()
|
|
|
- }
|
|
|
- let jsonDecodedMessage = try measureSubtask("Decode JSON") {
|
|
|
- return try PerfMessage(json: json)
|
|
|
- }
|
|
|
-
|
|
|
- // Exercise equality.
|
|
|
- measureSubtask("Test equality") {
|
|
|
- guard message == jsonDecodedMessage else {
|
|
|
- fatalError("Binary- and JSON-decoded messages were not equal!")
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private func populateFields(of message: inout PerfMessage) {
|
|
|
+ cat >> "$partial_results" <<EOF
|
|
|
+ "$language": {
|
|
|
EOF
|
|
|
|
|
|
- for field_number in $(seq 1 "$field_count"); do
|
|
|
- print_swift_set_field "$field_number" "$field_type" >>"$gen_harness_path"
|
|
|
- done
|
|
|
-
|
|
|
- cat >> "$gen_harness_path" <<EOF
|
|
|
- }
|
|
|
-}
|
|
|
+ echo "Running $language test harness in Instruments..."
|
|
|
+ instruments -t "$script_dir/Protobuf" -D "$results_trace" \
|
|
|
+ "$harness" -e DYLD_LIBRARY_PATH "$script_dir/_generated" \
|
|
|
+ "$partial_results"
|
|
|
+
|
|
|
+ cp "$harness" "${harness}_stripped"
|
|
|
+ strip -u -r "${harness}_stripped"
|
|
|
+ unstripped_size=$(stat -f "%z" "$harness")
|
|
|
+ stripped_size=$(stat -f "%z" "${harness}_stripped")
|
|
|
+
|
|
|
+ echo "${language} harness size before stripping: $unstripped_size bytes"
|
|
|
+ echo "${language} harness size after stripping: $stripped_size bytes"
|
|
|
+ echo
|
|
|
+
|
|
|
+ cat >> "$partial_results" <<EOF
|
|
|
+ harnessSize: {
|
|
|
+ "Unstripped": $unstripped_size,
|
|
|
+ "Stripped": $stripped_size,
|
|
|
+ },
|
|
|
+ },
|
|
|
EOF
|
|
|
}
|
|
|
|
|
|
+# Inserts the partial visualization results from all the languages tested into
|
|
|
+# the final results.js file.
|
|
|
+function insert_visualization_results() {
|
|
|
+ while IFS= read -r line
|
|
|
+ do
|
|
|
+ if [[ "$line" =~ ^//NEW-DATA-HERE$ ]]; then
|
|
|
+ cat "$partial_results"
|
|
|
+ fi
|
|
|
+ echo "$line"
|
|
|
+ done < "$results_js" > "${results_js}.new"
|
|
|
+
|
|
|
+ rm "$results_js"
|
|
|
+ mv "${results_js}.new" "$results_js"
|
|
|
+}
|
|
|
+
|
|
|
+# ---------------------------------------------------------------
|
|
|
+# Pull in language specific helpers.
|
|
|
+source "$script_dir/perf_runner_cpp.sh"
|
|
|
+source "$script_dir/perf_runner_swift.sh"
|
|
|
+
|
|
|
# ---------------------------------------------------------------
|
|
|
# Script main logic.
|
|
|
|
|
|
@@ -182,17 +171,20 @@ if [[ "$proto_syntax" != "2" ]] && [[ "$proto_syntax" != "3" ]]; then
|
|
|
usage
|
|
|
fi
|
|
|
|
|
|
-if [[ "$#" -ne 2 ]]; then
|
|
|
+if [[ "$#" -lt 2 ]]; then
|
|
|
usage
|
|
|
fi
|
|
|
|
|
|
-readonly field_count=$1
|
|
|
-readonly field_type=$2
|
|
|
-readonly script_dir="$(dirname $0)"
|
|
|
-
|
|
|
-# If the Instruments template has changed since the last run, copy it into the
|
|
|
-# user's template folder. (Would be nice if we could just run the template from
|
|
|
-# the local directory, but Instruments doesn't seem to support that.)
|
|
|
+readonly field_count="$1"; shift
|
|
|
+if [[ "$1" == "all" ]]; then
|
|
|
+ readonly requested_field_types=( \
|
|
|
+ int32 sint32 uint32 fixed32 sfixed32 \
|
|
|
+ int64 sint64 uint64 fixed64 sfixed64 \
|
|
|
+ float double string \
|
|
|
+ )
|
|
|
+else
|
|
|
+ readonly requested_field_types=( "$@" )
|
|
|
+fi
|
|
|
|
|
|
# Make sure the runtime and plug-in are up to date first.
|
|
|
( cd "$script_dir/.." >/dev/null; swift build -c release )
|
|
|
@@ -205,44 +197,52 @@ cp "$script_dir/../.build/release/protoc-gen-swift" \
|
|
|
mkdir -p "$script_dir/_generated"
|
|
|
mkdir -p "$script_dir/_results"
|
|
|
|
|
|
-gen_message_path="$script_dir/_generated/message.proto"
|
|
|
-gen_harness_path="$script_dir/_generated/Harness+Generated.swift"
|
|
|
-results="$script_dir/_results/$field_count fields of $field_type"
|
|
|
-harness="$script_dir/_generated/harness"
|
|
|
-
|
|
|
-echo "Generating test proto with $field_count fields..."
|
|
|
-generate_test_proto "$field_count" "$field_type"
|
|
|
-
|
|
|
-echo "Generating test harness..."
|
|
|
-generate_perf_harness "$field_count" "$field_type"
|
|
|
-
|
|
|
-protoc --plugin="$script_dir/../.build/release/protoc-gen-swiftForPerf" \
|
|
|
- --swiftForPerf_out=FileNaming=DropPath:"$script_dir/_generated" \
|
|
|
- "$gen_message_path"
|
|
|
-
|
|
|
-echo "Building test harness..."
|
|
|
-time ( swiftc -O -target x86_64-apple-macosx10.10 \
|
|
|
- -o "$harness" \
|
|
|
- -I "$script_dir/../.build/release" \
|
|
|
- -L "$script_dir/../.build/release" \
|
|
|
- -lSwiftProtobuf \
|
|
|
- "$gen_harness_path" \
|
|
|
- "$script_dir/Harness.swift" \
|
|
|
- "$script_dir/_generated/message.pb.swift" \
|
|
|
- "$script_dir/main.swift" \
|
|
|
-)
|
|
|
-echo
|
|
|
-
|
|
|
-echo "Running test harness in Instruments..."
|
|
|
-instruments -t "$script_dir/Protobuf" -D "$results" "$harness"
|
|
|
-open "$results.trace"
|
|
|
-
|
|
|
-dylib="$script_dir/../.build/release/libSwiftProtobuf.dylib"
|
|
|
-echo "Dylib size before stripping: $(stat -f "%z" "$dylib") bytes"
|
|
|
-strip -u -r "$dylib"
|
|
|
-echo "Dylib size after stripping: $(stat -f "%z" "$dylib") bytes"
|
|
|
-echo
|
|
|
-
|
|
|
-echo "Harness size before stripping: $(stat -f "%z" "$harness") bytes"
|
|
|
-strip -u -r "$harness"
|
|
|
-echo "Harness size after stripping: $(stat -f "%z" "$harness") bytes"
|
|
|
+# If the visualization results file isn't there, copy it from the template so
|
|
|
+# that the harnesses can populate it.
|
|
|
+results_js="$script_dir/_results/results.js"
|
|
|
+if [[ ! -f "$results_js" ]]; then
|
|
|
+ cp "$script_dir/js/results.js.template" "$results_js"
|
|
|
+fi
|
|
|
+
|
|
|
+# Iterate over the requested field types and run the harnesses.
|
|
|
+for field_type in "${requested_field_types[@]}"; do
|
|
|
+ gen_message_path="$script_dir/_generated/message.proto"
|
|
|
+ results_trace="$script_dir/_results/$field_count fields of $field_type"
|
|
|
+
|
|
|
+ echo "Generating test proto with $field_count fields of type $field_type..."
|
|
|
+ generate_test_proto "$field_count" "$field_type"
|
|
|
+
|
|
|
+ protoc --plugin="$script_dir/../.build/release/protoc-gen-swiftForPerf" \
|
|
|
+ --swiftForPerf_out=FileNaming=DropPath:"$script_dir/_generated" \
|
|
|
+ --cpp_out="$script_dir" \
|
|
|
+ "$gen_message_path"
|
|
|
+
|
|
|
+ # Start a session.
|
|
|
+ partial_results="$script_dir/_results/partial.js"
|
|
|
+ cat > "$partial_results" <<EOF
|
|
|
+ {
|
|
|
+ date: "$(date -u +"%FT%T.000Z")",
|
|
|
+ type: "$field_count fields of type $field_type",
|
|
|
+ branch: "$(git rev-parse --abbrev-ref HEAD)",
|
|
|
+ commit: "$(git rev-parse HEAD)",
|
|
|
+ uncommitted_changes: $([[ -z $(git status -s) ]] && echo false || echo true),
|
|
|
+EOF
|
|
|
+
|
|
|
+ harness_swift="$script_dir/_generated/harness_swift"
|
|
|
+ run_swift_harness "$harness_swift"
|
|
|
+
|
|
|
+ harness_cpp="$script_dir/_generated/harness_cpp"
|
|
|
+ run_cpp_harness "$harness_cpp"
|
|
|
+
|
|
|
+ # Close out the session.
|
|
|
+ cat >> "$partial_results" <<EOF
|
|
|
+ },
|
|
|
+EOF
|
|
|
+
|
|
|
+ insert_visualization_results "$partial_results" "$results_js"
|
|
|
+
|
|
|
+ open -g "$results_trace.trace"
|
|
|
+done
|
|
|
+
|
|
|
+# Open the HTML once at the end.
|
|
|
+open -g "$script_dir/harness-visualization.html"
|