Improve stats_event memory usage
We now truncate the buffer to the appropriate length when clients call
stats_event_build().
Benchmarking tests indicate that truncating the buffer to the
appropriate length increases the cost clients pay to write to the socket
by 2%. This is negligible enough that I decided to truncate the buffer
for both pushed and pulled atoms in order to simplify the API.
Test: m libstatssocket
Test: bit libstatssocket_benchmark:*
Bug: 144126231
Change-Id: I35dec748ff87c0821d0d06779a406997e6e64966
Merged-In: Ife976bb383ecff8de5064730692a95e2a3a82c9d
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index bd3d9ae..94c405d 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -47,3 +47,23 @@
export_include_dirs: ["include"],
host_supported: true,
}
+
+cc_benchmark {
+ name: "libstatssocket_benchmark",
+ srcs: [
+ "benchmark/main.cpp",
+ "benchmark/stats_event_benchmark.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libstatssocket",
+ ],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libgtest_prod",
+ ],
+}
diff --git a/libstats/socket/benchmark/main.cpp b/libstats/socket/benchmark/main.cpp
new file mode 100644
index 0000000..5ebdf6e
--- /dev/null
+++ b/libstats/socket/benchmark/main.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/benchmark.h>
+
+BENCHMARK_MAIN();
diff --git a/libstats/socket/benchmark/stats_event_benchmark.cpp b/libstats/socket/benchmark/stats_event_benchmark.cpp
new file mode 100644
index 0000000..b487c4d
--- /dev/null
+++ b/libstats/socket/benchmark/stats_event_benchmark.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "benchmark/benchmark.h"
+#include "stats_event.h"
+
+static struct stats_event* constructStatsEvent() {
+ struct stats_event* event = stats_event_obtain();
+ stats_event_set_atom_id(event, 100);
+
+ // randomly sample atom size
+ for (int i = 0; i < rand() % 800; i++) {
+ stats_event_write_int32(event, i);
+ }
+
+ return event;
+}
+
+static void BM_stats_event_truncate_buffer(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ struct stats_event* event = constructStatsEvent();
+ stats_event_build(event);
+ stats_event_write(event);
+ stats_event_release(event);
+ }
+}
+
+BENCHMARK(BM_stats_event_truncate_buffer);
+
+static void BM_stats_event_full_buffer(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ struct stats_event* event = constructStatsEvent();
+ stats_event_truncate_buffer(event, false);
+ stats_event_build(event);
+ stats_event_write(event);
+ stats_event_release(event);
+ }
+}
+
+BENCHMARK(BM_stats_event_full_buffer);
diff --git a/libstats/socket/include/stats_event.h b/libstats/socket/include/stats_event.h
index e7117d2..080e957 100644
--- a/libstats/socket/include/stats_event.h
+++ b/libstats/socket/include/stats_event.h
@@ -154,6 +154,9 @@
uint32_t (*get_errors)(struct stats_event*);
};
+// exposed for benchmarking only
+void stats_event_truncate_buffer(struct stats_event* event, bool truncate);
+
#ifdef __cplusplus
}
#endif // __CPLUSPLUS
diff --git a/libstats/socket/stats_event.c b/libstats/socket/stats_event.c
index 4098434..551b392 100644
--- a/libstats/socket/stats_event.c
+++ b/libstats/socket/stats_event.c
@@ -20,7 +20,6 @@
#include <time.h>
#include "stats_buffer_writer.h"
-#define STATS_EVENT_TAG 1937006964
#define LOGGER_ENTRY_MAX_PAYLOAD 4068
// Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
// See android_util_Stats_Log.cpp
@@ -39,13 +38,13 @@
// The stats_event struct holds the serialized encoding of an event
// within a buf. Also includes other required fields.
struct stats_event {
- uint8_t buf[MAX_EVENT_PAYLOAD];
+ uint8_t* buf;
size_t lastFieldPos; // location of last field within the buf
size_t size; // number of valid bytes within buffer
uint32_t numElements;
uint32_t atomId;
uint32_t errors;
- uint32_t tag;
+ bool truncate;
bool built;
};
@@ -58,12 +57,11 @@
struct stats_event* stats_event_obtain() {
struct stats_event* event = malloc(sizeof(struct stats_event));
-
- memset(event->buf, 0, MAX_EVENT_PAYLOAD);
+ event->buf = (uint8_t*)calloc(MAX_EVENT_PAYLOAD, 1);
event->buf[0] = OBJECT_TYPE;
event->atomId = 0;
event->errors = 0;
- event->tag = STATS_EVENT_TAG;
+ event->truncate = true; // truncate for both pulled and pushed atoms
event->built = false;
// place the timestamp
@@ -79,6 +77,7 @@
}
void stats_event_release(struct stats_event* event) {
+ free(event->buf);
free(event);
}
@@ -297,6 +296,10 @@
return event->errors;
}
+void stats_event_truncate_buffer(struct stats_event* event, bool truncate) {
+ event->truncate = truncate;
+}
+
void stats_event_build(struct stats_event* event) {
if (event->built) return;
@@ -317,6 +320,10 @@
event->size = POS_FIRST_FIELD + sizeof(uint8_t) + sizeof(uint32_t);
}
+ // Truncate the buffer to the appropriate length in order to limit our
+ // memory usage.
+ if (event->truncate) event->buf = (uint8_t*)realloc(event->buf, event->size);
+
event->built = true;
}