chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
@@ -0,0 +1,46 @@
# TFLite iOS benchmark app.
## Description
An iOS app to benchmark TFLite models.
The app reads benchmark parameters from a JSON file named
`benchmark_params.json` in its `benchmark_data` directory. Any downloaded models
for benchmarking should also be placed in `benchmark_data` directory.
The JSON file specifies the name of the model file and other benchmarking
parameters like inputs to the model, type of inputs, number of iterations,
number of threads. The default values in the JSON file are for the
Mobilenet_1.0_224 model ([paper][mobilenet-paper],
[tflite&pb][mobilenet-model]).
## Building / running the app
* Follow the [iOS build instructions][build-ios] to configure the Bazel
workspace and `.bazelrc` file correctly.
* Run `build_benchmark_framework.sh` script to build the benchmark framework.
This script will build the benchmark framework targeting iOS arm64 and put
it under `TFLiteBenchmark/TFLiteBenchmark/Frameworks` directory.
* If you want more detailed profiling, run the build script with `-p` option:
`build_benchmark_framework.sh -p`.
* Modify `benchmark_params.json` change the `input_layer`, `input_layer_shape`
and other benchmark parameters.
* Change `Build Phases -> Copy Bundle Resources` and add the model file to the
resources that need to be copied.
* Ensure that `Build Phases -> Link Binary With Library` contains the
`Accelerate framework` and `TensorFlowLiteBenchmarkC.framework`.
* Now try running the app. The app has a single button that runs the benchmark
on the model and displays results in a text view below. You can also see the
console output section in your Xcode to see more detailed benchmark
information.
[build-ios]: https://tensorflow.org/lite/guide/build_ios
[mobilenet-model]: https://storage.googleapis.com/download.tensorflow.org/models/mobilenet_v1_2018_02_22/mobilenet_v1_1.0_224.tgz
[mobilenet-paper]: https://arxiv.org/pdf/1704.04861.pdf
@@ -0,0 +1,22 @@
// Copyright 2018 The TensorFlow Authors. All Rights Reserved.
//
// 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.
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property(strong, nonatomic) UIWindow *window;
@end
@@ -0,0 +1,27 @@
// Copyright 2018 The TensorFlow Authors. All Rights Reserved.
//
// 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.
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
@end
@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23727" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Benchmark View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="BenchmarkViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Vd4-Gf-qKO">
<rect key="frame" x="26" y="101" width="333" height="556"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="j0O-Lq-1tJ">
<rect key="frame" x="69" y="30" width="247" height="63"/>
<constraints>
<constraint firstAttribute="height" constant="63" id="8VO-Ln-L2h"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<state key="normal" title="Benchmark model"/>
<connections>
<action selector="onBenchmarkModel:" destination="BYZ-38-t0r" eventType="touchUpInside" id="Rb1-hs-Mub"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="Vd4-Gf-qKO" firstAttribute="top" secondItem="j0O-Lq-1tJ" secondAttribute="bottom" constant="18" id="Kd3-pP-C1k"/>
<constraint firstItem="j0O-Lq-1tJ" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="QJU-cq-L87"/>
<constraint firstItem="Vd4-Gf-qKO" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="Tew-W4-Vq5"/>
<constraint firstItem="j0O-Lq-1tJ" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="Uce-n7-kZI"/>
<constraint firstItem="j0O-Lq-1tJ" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="64" id="Uhq-Rw-NKT"/>
<constraint firstItem="Vd4-Gf-qKO" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="26" id="aXc-6M-kyL"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="Vd4-Gf-qKO" secondAttribute="bottom" constant="10" id="tz5-wP-LZs"/>
</constraints>
</view>
<connections>
<outlet property="resultsView" destination="Vd4-Gf-qKO" id="dBT-f6-SYw"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="140" y="122.78860569715144"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,21 @@
// Copyright 2018 The TensorFlow Authors. All Rights Reserved.
//
// 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.
#import <UIKit/UIKit.h>
@interface BenchmarkViewController : UIViewController
@property(weak, nonatomic) IBOutlet UITextView *resultsView;
@end
@@ -0,0 +1,152 @@
// Copyright 2018 The TensorFlow Authors. All Rights Reserved.
//
// 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.
#import "BenchmarkViewController.h"
#import <algorithm>
#import <sstream>
#import <string>
#import <vector>
#if defined(USE_TFLITE_BENCHMARK_HEADERS)
#include "tensorflow/lite/tools/benchmark/experimental/c/benchmark_c_api.h"
#include "tensorflow/lite/tools/logging.h"
#else
#import <TensorFlowLiteBenchmarkC/TensorFlowLiteBenchmarkC.h>
#endif
namespace {
NSString* FilePathForResourceName(NSString* filename) {
NSString* name = [filename stringByDeletingPathExtension];
NSString* extension = [filename pathExtension];
NSString* file_path = [[NSBundle mainBundle] pathForResource:name ofType:extension];
if (file_path == NULL) {
TFLITE_LOG(FATAL) << "Couldn't find '" << [name UTF8String] << "." << [extension UTF8String]
<< "' in bundle.";
}
return file_path;
}
NSDictionary* ParseJson() {
NSString* params_json_path = FilePathForResourceName(@"benchmark_params.json");
NSData* data = [NSData dataWithContentsOfFile:params_json_path];
return [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
}
std::string FormatCommandLineParam(NSString* key, NSString* value) {
std::ostringstream stream;
stream << "--" << [key UTF8String] << "=" << [value UTF8String];
return stream.str();
}
// Reads the |benchmark_params.json| to read command line parameters and returns them as a vector of
// strings.
void ReadCommandLineParameters(std::vector<std::string>* params) {
NSDictionary* param_dict = ParseJson();
for (NSString* key in param_dict) {
NSString* value = param_dict[key];
if ([key isEqualToString:@"graph"]) {
value = FilePathForResourceName(value);
}
params->push_back(FormatCommandLineParam(key, value));
}
}
std::vector<char*> StringVecToCharPtrVec(const std::vector<std::string>& str_vec) {
std::vector<char*> charptr_vec;
std::transform(str_vec.begin(), str_vec.end(), std::back_inserter(charptr_vec),
[](const std::string& s) -> char* { return const_cast<char*>(s.c_str()); });
return charptr_vec;
}
class ResultsListener {
public:
void OnBenchmarkEnd(TfLiteBenchmarkResults* results);
std::string Results() { return results_; }
private:
std::string results_;
};
void OutputMicrosecondsStatToStream(const TfLiteBenchmarkInt64Stat& time_us,
const std::string& prefix, std::ostringstream* stream) {
*stream << prefix << "Num runs: " << time_us.count << "\n";
*stream << prefix << "Average: " << time_us.avg / 1e3 << " ms\n";
*stream << prefix << "Min: " << time_us.min / 1e3 << " ms \n";
*stream << prefix << "Max: " << time_us.max / 1e3 << " ms \n";
*stream << prefix << "Std deviation: " << time_us.std_deviation / 1e3 << " ms\n";
}
void ResultsListener::OnBenchmarkEnd(TfLiteBenchmarkResults* results) {
std::ostringstream stream;
const std::string prefix = " - ";
TfLiteBenchmarkInt64Stat inference = TfLiteBenchmarkResultsGetInferenceTimeMicroseconds(results);
TfLiteBenchmarkInt64Stat warmup = TfLiteBenchmarkResultsGetWarmupTimeMicroseconds(results);
stream << "Startup latency: ";
stream << TfLiteBenchmarkResultsGetStartupLatencyMicroseconds(results) / 1e3 << " ms\n";
stream << "\nInference:\n";
OutputMicrosecondsStatToStream(inference, prefix, &stream);
stream << "\nWarmup:\n";
OutputMicrosecondsStatToStream(warmup, prefix, &stream);
results_ = stream.str();
}
void OnBenchmarkEnd(void* user_data, TfLiteBenchmarkResults* results) {
if (user_data != nullptr) {
reinterpret_cast<ResultsListener*>(user_data)->OnBenchmarkEnd(results);
}
}
std::string RunBenchmark() {
ResultsListener results_listener;
TfLiteBenchmarkTfLiteModel* benchmark = TfLiteBenchmarkTfLiteModelCreate();
TfLiteBenchmarkListener* listener = TfLiteBenchmarkListenerCreate();
TfLiteBenchmarkListenerSetCallbacks(listener, &results_listener, nullptr, nullptr, nullptr,
OnBenchmarkEnd);
TfLiteBenchmarkTfLiteModelAddListener(benchmark, listener);
// TODO(shashishekhar): Passing arguments like this is brittle, refactor the BenchmarkParams
// so that it contains arguments for BenchmarkTfLiteModel and set parameters using BenchmarkParams
std::vector<std::string> command_line_params;
// Benchmark model expects first arg to be program name.
// push a string for name of program.
command_line_params.push_back("benchmark_tflite_model");
ReadCommandLineParameters(&command_line_params);
std::vector<char*> argv = StringVecToCharPtrVec(command_line_params);
int argc = static_cast<int>(argv.size());
TfLiteBenchmarkTfLiteModelRunWithArgs(benchmark, argc, argv.data());
std::string results = results_listener.Results();
TfLiteBenchmarkListenerDelete(listener);
TfLiteBenchmarkTfLiteModelDelete(benchmark);
return results;
}
} // namespace
@interface BenchmarkViewController ()
@end
@implementation BenchmarkViewController
- (IBAction)onBenchmarkModel:(UIButton*)sender {
std::string results = RunBenchmark();
[_resultsView setText:[NSString stringWithUTF8String:results.c_str()]];
}
@end
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>Main</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -0,0 +1,10 @@
{
"benchmark_name" : "mobile_net_benchmark",
"num_threads" : "4",
"num_runs" : "20",
"warmup_runs" : "1",
"graph" : "mobilenet_v1_1.0_224.tflite",
"input_layer" : "input",
"input_layer_shape" : "1,224,224,3",
"run_delay" : "-1"
}
@@ -0,0 +1,23 @@
// Copyright 2018 The TensorFlow Authors. All Rights Reserved.
//
// 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.
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
@@ -0,0 +1,73 @@
#!/bin/bash
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
#
# 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.
# ==============================================================================
set -e
WORKSPACE_ROOT=$(bazel info workspace 2> /dev/null)
BENCHMARK_DIR=tensorflow/lite/tools/benchmark
DEST_DIR="${BENCHMARK_DIR}/ios/TFLiteBenchmark/TFLiteBenchmark/Frameworks"
FRAMEWORK_TARGET=TensorFlowLiteBenchmarkC_framework
function usage() {
echo "Usage: $(basename "$0") [-p]"
echo "-p enable profiling"
exit 1
}
PROFILING_ARGS=""
while getopts "p" opt_name; do
case "$opt_name" in
p) PROFILING_ARGS='--define=ruy_profiler=true';;
*) usage;;
esac
done
shift $(($OPTIND - 1))
function check_ios_configured() {
if [ ! -f "${WORKSPACE_ROOT}/${BENCHMARK_DIR}/experimental/ios/BUILD" ]; then
echo "ERROR: Benchmark framework BUILD file not found."
echo "Please enable iOS support by running the \"./configure\" script" \
"from the workspace root."
exit 1
fi
}
function build_framework() {
set -x
pushd "${WORKSPACE_ROOT}"
# Build the framework.
bazel build --config=ios_arm64 -c opt --cxxopt=-std=c++17 ${PROFILING_ARGS} \
"//${BENCHMARK_DIR}/experimental/ios:${FRAMEWORK_TARGET}"
# Get the generated framework path.
BAZEL_OUTPUT_FILE_PATH=$(bazel cquery "//${BENCHMARK_DIR}/experimental/ios:${FRAMEWORK_TARGET}" --config=ios_arm64 --output=starlark --starlark:expr="' '.join([f.path for f in target.files.to_list()])")
# Copy the framework into the destination and unzip.
mkdir -p "${DEST_DIR}"
cp -f "${BAZEL_OUTPUT_FILE_PATH}" \
"${DEST_DIR}"
pushd "${DEST_DIR}"
unzip -o "${FRAMEWORK_TARGET}.zip"
rm -f "${FRAMEWORK_TARGET}.zip"
popd
popd
}
check_ios_configured
build_framework