Skip to content

Commit 512a994

Browse files
authored
fix(profiler): improve performance (#280)
* chore(profiler): add timings average in logs * fix(perf): improve performance of profiler * chore(profiler): set c++ standard * perf(profiler): improve read file performance * chore(profiler): set thread names * refactor(profiler): extract function * refactor(profiler): extract function * chore: generate binaries * prune: remove duplicated confusing interface * refactor(profiler): add supportFPS * refactor(profiler): move function * refactor: simplify ensureCppProfilerIsInstalled * refactor: rename function * refactor: move function * refactor: move function * refactor: move function * refactor: remove platformProfiler from android * chore: add profiler to measure flashlight performance
1 parent 78dab21 commit 512a994

21 files changed

+324
-261
lines changed

packages/commands/test/src/__tests__/measurePerformance.test.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@ import { Logger, LogLevel } from "@perf-profiler/logger";
66

77
const mockPerformancePolling = new PerformancePollingMock();
88

9-
jest.mock("@perf-profiler/profiler", () => ({
10-
...jest.requireActual("@perf-profiler/profiler"),
11-
profiler: {
12-
...jest.requireActual("@perf-profiler/profiler").profiler,
13-
installProfilerOnDevice: jest.fn(),
14-
getPidId: jest.fn(() => 123),
15-
pollPerformanceMeasures: jest.fn((pid, { onMeasure, onStartMeasuring }) => {
16-
mockPerformancePolling.setCallback(onMeasure);
17-
onStartMeasuring();
18-
}),
19-
},
20-
}));
9+
jest.mock("@perf-profiler/profiler", () => {
10+
const mockedProfiler = jest.requireActual("@perf-profiler/profiler").profiler;
11+
12+
mockedProfiler.installProfilerOnDevice = jest.fn();
13+
mockedProfiler.getPidId = jest.fn(() => 123);
14+
mockedProfiler.pollPerformanceMeasures = jest.fn((pid, { onMeasure, onStartMeasuring }) => {
15+
mockPerformancePolling.setCallback(onMeasure);
16+
onStartMeasuring();
17+
});
18+
19+
return {
20+
...jest.requireActual("@perf-profiler/profiler"),
21+
profiler: mockedProfiler,
22+
};
23+
});
2124

2225
Logger.setLogLevel(LogLevel.SILENT);
2326

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
cmake_minimum_required(VERSION 3.16.0)
22
project(android-cmake-flashlight)
33
add_executable(BAMPerfProfiler src/main.cpp src/atrace.cpp src/utils.cpp)
4+
5+
set(CMAKE_CXX_STANDARD 21)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

packages/platforms/android/cpp-profiler/src/atrace.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void printATraceLines()
2626
std::lock_guard<std::mutex> guard(aTraceLinesMutex);
2727
for (auto itr = aTraceLines.begin(), end_itr = aTraceLines.end(); itr != end_itr; ++itr)
2828
{
29-
std::cout << *itr << std::endl;
29+
std::cout << *itr << "\n";
3030
}
3131
aTraceLines.clear();
3232
}

packages/platforms/android/cpp-profiler/src/main.cpp

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,16 @@
33
#include <iostream>
44
#include <filesystem>
55
#include "atrace.h"
6-
#include <fstream>
76
#include <thread>
87
#include <unistd.h>
98
#include "utils.h"
109

1110
using std::cerr;
1211
using std::cout;
13-
using std::endl;
1412
using std::string;
1513

1614
namespace fs = std::filesystem;
1715

18-
void readFile(string filePath)
19-
{
20-
std::ifstream file(filePath);
21-
if (file.is_open())
22-
{
23-
string line;
24-
while (std::getline(file, line))
25-
{
26-
log(line.c_str());
27-
}
28-
file.close();
29-
}
30-
else
31-
{
32-
cerr << "CPP_ERROR_CANNOT_OPEN_FILE " + filePath << endl;
33-
}
34-
}
35-
3616
class PidClosedError : public std::runtime_error
3717
{
3818
public:
@@ -68,6 +48,9 @@ void printMemoryStats(std::vector<string> pids)
6848
}
6949
}
7050

51+
long long totalDurationSum = 0;
52+
long long measureCount = 0;
53+
7154
long long printPerformanceMeasure(std::vector<string> pids)
7255
{
7356
auto start = std::chrono::system_clock::now();
@@ -97,13 +80,23 @@ long long printPerformanceMeasure(std::vector<string> pids)
9780
auto atraceDuration = std::chrono::duration_cast<std::chrono::milliseconds>(atraceEnd - memoryEnd);
9881
auto totalDuration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
9982

83+
long long totalDurationMs = totalDuration.count();
84+
10085
cout << "TOTAL EXEC TIME: " << totalDuration.count() << "|";
10186
cout << "CPU TIME: " << cpuDuration.count() << "|";
10287
cout << "MEMORY TIME: " << memoryDuration.count() << "|";
103-
cout << "ATRACE TIME: " << atraceDuration.count() << endl;
88+
cout << "ATRACE TIME: " << atraceDuration.count() << "\n";
89+
90+
measureCount++;
91+
totalDurationSum += totalDurationMs;
92+
93+
log(separator);
94+
cout << "AVERAGE TOTAL EXEC TIME: " << totalDurationSum / measureCount << "\n";
10495

10596
log("=STOP MEASURE=");
10697

98+
cout << std::flush;
99+
107100
return totalDuration.count();
108101
}
109102

@@ -115,15 +108,19 @@ std::vector<string> pidOf(string bundleId)
115108

116109
void pollPerformanceMeasures(std::string bundleId, int interval)
117110
{
111+
pthread_setname_np(pthread_self(), "FL-Main");
112+
118113
std::vector<string> pids;
119114

120115
// We read atrace lines before the app is started
121116
// since it can take a bit of time to start and clear the traceOutputPath
122117
// but we'll clear them out periodically while the app isn't started
123118
// TODO handle ATrace not available on OS
124119
std::thread aTraceReadThread(readATraceThread);
120+
pthread_setname_np(aTraceReadThread.native_handle(), "FL-Atrace");
125121

126-
cout << "Waiting for process to start..." << endl;
122+
cout << "Waiting for process to start..."
123+
<< "\n";
127124

128125
while (pids.empty())
129126
{
@@ -142,7 +139,7 @@ void pollPerformanceMeasures(std::string bundleId, int interval)
142139
}
143140
catch (const PidClosedError &e)
144141
{
145-
cerr << "CPP_ERROR_MAIN_PID_CLOSED " << e.what() << endl;
142+
cerr << "CPP_ERROR_MAIN_PID_CLOSED " << e.what() << "\n";
146143
pollPerformanceMeasures(bundleId, interval);
147144
return;
148145
}
@@ -152,12 +149,12 @@ void pollPerformanceMeasures(std::string bundleId, int interval)
152149

153150
void printCpuClockTick()
154151
{
155-
cout << sysconf(_SC_CLK_TCK) << endl;
152+
cout << sysconf(_SC_CLK_TCK) << "\n";
156153
}
157154

158155
void printRAMPageSize()
159156
{
160-
cout << sysconf(_SC_PAGESIZE) << endl;
157+
cout << sysconf(_SC_PAGESIZE) << "\n";
161158
}
162159

163160
int main(int argc, char **argv)
@@ -187,7 +184,7 @@ int main(int argc, char **argv)
187184
}
188185
else
189186
{
190-
cout << "Unknown method name: " << methodName << endl;
187+
cout << "Unknown method name: " << methodName << "\n";
191188
return 1;
192189
}
193190

packages/platforms/android/cpp-profiler/src/utils.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
#include <array>
44
#include <memory>
55
#include <chrono>
6+
#include <fstream>
67

78
using std::cout;
8-
using std::endl;
99

1010
void log(const std::string &msg)
1111
{
12-
cout << msg << endl;
12+
cout << msg << "\n";
1313
}
1414

1515
void logTimestamp()
@@ -19,7 +19,7 @@ void logTimestamp()
1919
now.time_since_epoch())
2020
.count();
2121

22-
cout << "Timestamp: " << timestamp << endl;
22+
cout << "Timestamp: " << timestamp << "\n";
2323
}
2424

2525
std::string executeCommand(std::string command)
@@ -57,3 +57,28 @@ std::vector<std::string> split(const std::string &str, char delimiter)
5757

5858
return result;
5959
}
60+
61+
#define BUFFER_SIZE 2048
62+
63+
void readFile(std::string_view path)
64+
{
65+
constexpr auto read_size = std::size_t(BUFFER_SIZE);
66+
auto stream = std::ifstream(path.data());
67+
stream.exceptions(std::ios_base::badbit);
68+
69+
if (not stream)
70+
{
71+
std::cerr << "CPP_ERROR_CANNOT_OPEN_FILE " << path << "\n";
72+
return;
73+
}
74+
75+
auto out = std::string();
76+
auto buf = std::string(read_size, '\0');
77+
while (stream.read(&buf[0], read_size))
78+
{
79+
out.append(buf, 0, stream.gcount());
80+
}
81+
out.append(buf, 0, stream.gcount());
82+
83+
log(out);
84+
}

packages/platforms/android/cpp-profiler/src/utils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ void log(const std::string &msg);
77
void logTimestamp();
88
std::string executeCommand(std::string command);
99
std::vector<std::string> split(const std::string &str, char delimiter);
10+
void readFile(std::string_view path);
1011

1112
#endif /* UTILS_H */

packages/platforms/android/src/commands.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,18 @@
22

33
import { Logger } from "@perf-profiler/logger";
44
import { Measure } from "@perf-profiler/types";
5-
import {
6-
getCpuClockTick,
7-
getRAMPageSize,
8-
ensureCppProfilerIsInstalled,
9-
} from "./commands/cppProfiler";
105
import { program } from "commander";
116
import { detectCurrentAppBundleId } from "./commands/detectCurrentAppBundleId";
127
import { getPidId } from "./commands/getPidId";
138
import { getAbi } from "./commands/getAbi";
14-
import { pollPerformanceMeasures } from "./commands/pollPerformanceMeasures";
9+
import { AndroidProfiler } from "./commands/platforms/AndroidProfiler";
10+
11+
const profiler = new AndroidProfiler();
1512

1613
const debugCppConfig = () => {
17-
ensureCppProfilerIsInstalled();
18-
Logger.success(`CPU Clock tick: ${getCpuClockTick()}`);
19-
Logger.success(`RAM Page size: ${getRAMPageSize()}`);
14+
profiler.installProfilerOnDevice();
15+
Logger.success(`CPU Clock tick: ${profiler.getCpuClockTick()}`);
16+
Logger.success(`RAM Page size: ${profiler.getRAMPageSize()}`);
2017
};
2118

2219
program.command("debugCppConfig").description("Debug CPP Config").action(debugCppConfig);
@@ -65,7 +62,7 @@ program
6562
.action((options) => {
6663
const bundleId = options.bundleId || detectCurrentAppBundleId().bundleId;
6764

68-
pollPerformanceMeasures(bundleId, {
65+
profiler.pollPerformanceMeasures(bundleId, {
6966
onMeasure: (measure: Measure) => {
7067
const headers: string[] = [];
7168
const values: (number | undefined)[] = [];
Lines changed: 1 addition & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,6 @@
11
import { Logger } from "@perf-profiler/logger";
2-
import { canIgnoreAwsTerminationError, executeLongRunningProcess } from "./shell";
3-
import { POLLING_INTERVAL } from "@perf-profiler/types";
4-
import { profiler } from "./platforms/platformProfiler";
52

6-
/**
7-
* Main setup function for the cpp profiler
8-
*
9-
* It will:
10-
* - install the C++ profiler for the correct architecture on the device
11-
* - Starts the atrace process (the c++ profiler will then starts another thread to read from it)
12-
* - Populate needed values like CPU clock tick and RAM page size
13-
*
14-
* This needs to be done before measures and can take a few seconds
15-
*/
16-
export const ensureCppProfilerIsInstalled = () => {
17-
profiler.ensureCppProfilerIsInstalled();
18-
};
19-
20-
export const getCpuClockTick = () => {
21-
return profiler.getCpuClockTick();
22-
};
23-
24-
export const getRAMPageSize = () => {
25-
return profiler.getRAMPageSize();
26-
};
27-
28-
type CppPerformanceMeasure = {
3+
export type CppPerformanceMeasure = {
294
pid: string;
305
cpu: string;
316
ram: string;
@@ -52,43 +27,3 @@ export const parseCppMeasure = (measure: string): CppPerformanceMeasure => {
5227

5328
return { pid, cpu, ram, atrace, timestamp };
5429
};
55-
56-
export const pollPerformanceMeasures = (
57-
pid: string,
58-
onData: (measure: CppPerformanceMeasure) => void,
59-
onPidChanged?: (pid: string) => void
60-
) => {
61-
ensureCppProfilerIsInstalled();
62-
63-
const DELIMITER = "=STOP MEASURE=";
64-
65-
const process = executeLongRunningProcess(
66-
profiler.getDeviceCommand(
67-
`${profiler.getDeviceProfilerPath()} pollPerformanceMeasures ${pid} ${POLLING_INTERVAL}`
68-
),
69-
DELIMITER,
70-
(data: string) => {
71-
onData(parseCppMeasure(data));
72-
}
73-
);
74-
75-
process.stderr?.on("data", (data) => {
76-
const log = data.toString();
77-
78-
// Ignore errors, it might be that the thread is dead and we can't read stats anymore
79-
if (log.includes("CPP_ERROR_CANNOT_OPEN_FILE")) {
80-
Logger.debug(log);
81-
} else if (log.includes("CPP_ERROR_MAIN_PID_CLOSED")) {
82-
onPidChanged?.(pid);
83-
} else {
84-
if (!canIgnoreAwsTerminationError(log)) Logger.error(log);
85-
}
86-
});
87-
88-
return {
89-
stop: () => {
90-
process.kill("SIGINT");
91-
profiler.stop();
92-
},
93-
};
94-
};

packages/platforms/android/src/commands/cpu/CpuMeasureAggregator.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import { mapValues } from "lodash";
22
import { CpuMeasure as Measure } from "@perf-profiler/types";
3-
import { getCpuClockTick } from "../cppProfiler";
43
import { ProcessStat } from "./getCpuStatsByProcess";
54

65
export class CpuMeasureAggregator {
76
private previousTotalCpuTimePerProcessId: { [processId: string]: number } = {};
87

8+
constructor(private cpuClockTick: number) {}
9+
910
private groupCpuUsage(
1011
stats: ProcessStat[],
1112
groupByIteratee: (stat: ProcessStat) => string,
1213
timeInterval: number
1314
): {
1415
[by: string]: number;
1516
} {
16-
const TICKS_FOR_TIME_INTERVAL = (getCpuClockTick() * timeInterval) / 1000;
17+
const TICKS_FOR_TIME_INTERVAL = (this.cpuClockTick * timeInterval) / 1000;
1718

1819
const toPercentage = (value: number) => Math.min((value * 100) / TICKS_FOR_TIME_INTERVAL, 100);
1920

0 commit comments

Comments
 (0)