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
This commit is contained in:
Ruchir Rastogi 2019-12-12 17:16:59 -08:00
parent 926fa88c35
commit 79dd3eaa87
5 changed files with 108 additions and 6 deletions

View file

@ -47,3 +47,23 @@ cc_library_headers {
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",
],
}

View file

@ -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();

View file

@ -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);

View file

@ -154,6 +154,9 @@ struct stats_event_api_table {
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

View file

@ -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 @@ static int64_t get_elapsed_realtime_ns() {
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 @@ struct stats_event* stats_event_obtain() {
}
void stats_event_release(struct stats_event* event) {
free(event->buf);
free(event);
}
@ -297,6 +296,10 @@ uint32_t stats_event_get_errors(struct stats_event* event) {
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 @@ void stats_event_build(struct stats_event* event) {
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;
}