Essentially, instead of having the inlined UntypedFunction::Create(f)
return an UntypedFunction which is then passed as an argument to
non-inlined RoboCallerReceivers::AddReceiverImpl(), we let
UntypedFunction::PrepareArgs(f) return a few different kinds of
trivial structs (depending on what sort of type f has) which are
passed as arguments to non-inlined RoboCallerReceivers::AddReceiver()
(which then converts them to UntypedFunction by calling
UntypedFunction::Create()). These structs are smaller than
UntypedFunction and optimized for argument passing, so many fewer
instructions are needed.
Example code:
struct Foo {
void Receive(int, float, int, float);
void TestAddLambdaReceiver();
webrtc::RoboCaller<int, float, int, float> rc;
};
void Foo::TestAddLambdaReceiver() {
rc.AddReceiver([this](int a, float b, int c, float d){
Receive(a, b, c, d);});
}
On arm32, we get before this CL:
Foo::TestAddLambdaReceiver():
push {r11, lr}
mov r11, sp
sub sp, sp, #24
ldr r1, .LCPI0_0
mov r2, #0
stm sp, {r0, r2}
add r1, pc, r1
str r2, [sp, #20]
str r1, [sp, #16]
mov r1, sp
bl RoboCallerReceivers::AddReceiverImpl
mov sp, r11
pop {r11, pc}
.LCPI0_0:
.long CallInlineStorage<Foo::TestAddLambdaReceiver()::$_0>
CallInlineStorage<Foo::TestAddLambdaReceiver()::$_0>:
ldr r0, [r0]
b Foo::Receive(int, float, int, float)
After this CL:
Foo::TestAddLambdaReceiver():
ldr r3, .LCPI0_0
mov r2, r0
add r3, pc, r3
b RoboCallerReceivers::AddReceiver<1u>
.LCPI0_0:
.long CallInlineStorage<Foo::TestAddLambdaReceiver()::$_0>
CallInlineStorage<Foo::TestAddLambdaReceiver()::$_0>:
ldr r0, [r0]
b Foo::Receive(int, float, int, float)
(Symbol names abbreviated so that they'll fit on one line.)
So a reduction from 64 to 28 bytes. The improvements on arm64 and
x86_64 are similar.
Bug: webrtc:11943
Change-Id: I93fbba083be0235051c3279d3e3f6852a4a9fdad
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/185960
Commit-Queue: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32244}
103 lines
3.5 KiB
C++
103 lines
3.5 KiB
C++
/*
|
|
* Copyright 2020 The WebRTC Project Authors. All rights reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#ifndef RTC_BASE_ROBO_CALLER_H_
|
|
#define RTC_BASE_ROBO_CALLER_H_
|
|
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "api/function_view.h"
|
|
#include "rtc_base/system/assume.h"
|
|
#include "rtc_base/system/inline.h"
|
|
#include "rtc_base/untyped_function.h"
|
|
|
|
namespace webrtc {
|
|
namespace robo_caller_impl {
|
|
|
|
class RoboCallerReceivers {
|
|
public:
|
|
RoboCallerReceivers();
|
|
RoboCallerReceivers(const RoboCallerReceivers&) = delete;
|
|
RoboCallerReceivers& operator=(const RoboCallerReceivers&) = delete;
|
|
RoboCallerReceivers(RoboCallerReceivers&&) = delete;
|
|
RoboCallerReceivers& operator=(RoboCallerReceivers&&) = delete;
|
|
~RoboCallerReceivers();
|
|
|
|
template <typename UntypedFunctionArgsT>
|
|
RTC_NO_INLINE void AddReceiver(UntypedFunctionArgsT args) {
|
|
receivers_.push_back(UntypedFunction::Create(args));
|
|
}
|
|
|
|
void Foreach(rtc::FunctionView<void(UntypedFunction&)> fv);
|
|
|
|
private:
|
|
std::vector<UntypedFunction> receivers_;
|
|
};
|
|
|
|
extern template void RoboCallerReceivers::AddReceiver(
|
|
UntypedFunction::TrivialUntypedFunctionArgs<1>);
|
|
extern template void RoboCallerReceivers::AddReceiver(
|
|
UntypedFunction::TrivialUntypedFunctionArgs<2>);
|
|
extern template void RoboCallerReceivers::AddReceiver(
|
|
UntypedFunction::TrivialUntypedFunctionArgs<3>);
|
|
extern template void RoboCallerReceivers::AddReceiver(
|
|
UntypedFunction::TrivialUntypedFunctionArgs<4>);
|
|
extern template void RoboCallerReceivers::AddReceiver(
|
|
UntypedFunction::NontrivialUntypedFunctionArgs);
|
|
extern template void RoboCallerReceivers::AddReceiver(
|
|
UntypedFunction::FunctionPointerUntypedFunctionArgs);
|
|
|
|
} // namespace robo_caller_impl
|
|
|
|
// A collection of receivers (callable objects) that can be called all at once.
|
|
// Optimized for minimal binary size.
|
|
//
|
|
// Neither copyable nor movable. Could easily be made movable if necessary.
|
|
//
|
|
// TODO(kwiberg): Add support for removing receivers, if necessary. AddReceiver
|
|
// would have to return some sort of ID that the caller could save and then pass
|
|
// to RemoveReceiver. Alternatively, the callable objects could return one value
|
|
// if they wish to stay in the CSC and another value if they wish to be removed.
|
|
// It depends on what's convenient for the callers...
|
|
template <typename... ArgT>
|
|
class RoboCaller {
|
|
public:
|
|
RoboCaller() = default;
|
|
RoboCaller(const RoboCaller&) = delete;
|
|
RoboCaller& operator=(const RoboCaller&) = delete;
|
|
RoboCaller(RoboCaller&&) = delete;
|
|
RoboCaller& operator=(RoboCaller&&) = delete;
|
|
|
|
// Adds a new receiver. The receiver (a callable object or a function pointer)
|
|
// must be movable, but need not be copyable. Its call signature should be
|
|
// `void(ArgT...)`.
|
|
template <typename F>
|
|
void AddReceiver(F&& f) {
|
|
receivers_.AddReceiver(
|
|
UntypedFunction::PrepareArgs<void(ArgT...)>(std::forward<F>(f)));
|
|
}
|
|
|
|
// Calls all receivers with the given arguments.
|
|
template <typename... ArgU>
|
|
void Send(ArgU&&... args) {
|
|
receivers_.Foreach([&](UntypedFunction& f) {
|
|
f.Call<void(ArgT...)>(std::forward<ArgU>(args)...);
|
|
});
|
|
}
|
|
|
|
private:
|
|
robo_caller_impl::RoboCallerReceivers receivers_;
|
|
};
|
|
|
|
} // namespace webrtc
|
|
|
|
#endif // RTC_BASE_ROBO_CALLER_H_
|