提交 3b8ae875 编写于 作者: M Megvii Engine Team

Project import generated by Copybara.

GitOrigin-RevId: 4e5b4025
上级
/build
/main*
/output
*.swp
*.pyc
TARGET ?= main
CXX := g++
SRC_EXT := cpp
OPTFLAG ?= -O2
BUILD_DIR ?= build
override CXXFLAGS += \
-Isrc -std=c++11 -ggdb -flto \
-Wall -Wextra -Wnon-virtual-dtor -Wno-unused-parameter -Winvalid-pch \
-Wno-unused-local-typedefs \
$(OPTFLAG)
override LDFLAGS += -flto
override V ?= @
CXXSOURCES := $(shell find src example -name "*.$(SRC_EXT)")
OBJS := $(addprefix $(BUILD_DIR)/,$(CXXSOURCES:.$(SRC_EXT)=.o))
DEPFILES := $(OBJS:.o=.d)
all: $(TARGET)
-include $(DEPFILES)
$(BUILD_DIR)/%.o: %.$(SRC_EXT)
@echo "[cxx] $< ..."
@mkdir -pv $(dir $@)
$(V)$(CXX) $(CXXFLAGS) -MM -MT "$@" "$<" > "$(@:.o=.d)"
$(V)$(CXX) -c $< -o $@ $(CXXFLAGS)
$(TARGET): $(OBJS)
@echo "Linking ..."
$(V)$(CXX) $(OBJS) -o $@ $(LDFLAGS)
clean:
rm -rf $(BUILD_DIR) $(TARGET)
.PHONY: all clean
# vim: ft=make
# Midout
Move Intermediate Dynamic-dispatch OUT; the name is obtained by applying
the infamous [middle-out](http://www.piedpiper.com) algorithm on the word
`middle-out`.
## Usage
1. Include `midout.h` in the source code and mark removable regions with
`MIDOUT_BEGIN` and `MIDOUT_END`.
2. Compile the whole project with macro `MIDOUT_PROFILING` defined.
3. Execute the program on possible inputs to gather trace information on used
blocks. The trace would be written to `midout_trace.<pid>` by default, and
this output name can be changed via the environment variable `MIDOUT_OUTPUT`.
4. Generate midout header file by `./gen_header.py`.
5. Recompile the project and ensure that the header generated in the previous
step is included before `midout.h`. Remember to enable LTO.
6. Now all the blocks marked in step 1 that are not executed in step 3 have been
removed from the final executable; if any of them is indeed used at runtime,
a trap would be triggered.
See `run.sh` for a concrete example.
#include "midout.h"
#include <cstdio>
#include <string>
MIDOUT_DECL(Opr);
enum class Opr {
ADD, SUB
};
namespace calc {
template<Opr opr, int iv>
struct kern_impl;
template<int iv>
struct kern_impl<Opr::ADD, iv> {
__attribute__((noinline))
static int apply(int a, int b) {
return a + b + iv;
}
};
template<int iv>
struct kern_impl<Opr::SUB, iv> {
__attribute__((noinline))
static int apply(int a, int b) {
return a - b + iv;
}
};
template<Opr opr, int iv>
int kern(int a, int b) {
MIDOUT_BEGIN(Opr, midout_iv(opr), iv) {
return kern_impl<opr, iv>::apply(a, b);
} MIDOUT_END();
}
}
int main(int argc, char **argv) {
if (argc != 4) {
fprintf(stderr, "usage: %s <num0> <num1> <+/-/p/m>\n"
" to compute sum/difference of two numbers\n",
argv[0]);
return -1;
}
int a = std::stoi(argv[1]),
b = std::stoi(argv[2]),
c;
switch (argv[3][0]) {
case '+':
c = calc::kern<Opr::ADD, 0>(a, b);
break;
case '-':
c = calc::kern<Opr::SUB, 0>(a, b);
break;
case 'p':
c = calc::kern<Opr::ADD, 1>(a, b);
break;
case 'm':
c = calc::kern<Opr::SUB, 1>(a, b);
break;
default:
fprintf(stderr, "bad opr\n");
return 2;
}
printf("result=%d\n", c);
}
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import sys
import subprocess
MAGIC = 'midout_trace v1\n'
class MidoutHeaderGen:
_tag_names = None
_region_names = None
def __init__(self):
self._tag_names = set()
self._region_names = set()
def add_item(self, name: str):
prefix = 'midout::Region<midout::tags::'
assert name.startswith(prefix), 'bad name: {!r}'.format(name)
comma = name.find(',', len(prefix))
self._tag_names.add(name[len(prefix):comma])
self._region_names.add(name)
def write(self, fout):
print('#define MIDOUT_GENERATED \\', file=fout)
print('namespace midout { namespace tags { \\', file=fout)
for i in sorted(self._tag_names):
print('class {}; \\'.format(i), file=fout)
print('} \\', file=fout)
for i in self._region_names:
i = i.replace('midout::', '')
i = i.replace('__ndk1::', '')
print('template<> \\', file=fout)
print('struct {} {{ static constexpr bool enable = true; }}; \\'.
format(i), file=fout)
print('}', file=fout)
def main():
parser = argparse.ArgumentParser(
description='generate header file from midout traces',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-o', '--output',
help='output header file; default to stdout')
parser.add_argument('inputs', nargs='+', help='input trace files')
args = parser.parse_args()
gen = MidoutHeaderGen()
for i in args.inputs:
with open(i) as fin:
assert fin.read(len(MAGIC)) == MAGIC, 'bad trace file'
demangle = subprocess.check_output(
['c++filt', '-t'], input='\n'.join(list(fin)).encode('utf-8'))
for line in demangle.decode('utf-8').split('\n'):
line = line.strip()
if line:
gen.add_item(line)
if not args.output:
gen.write(sys.stdout)
else:
with open(args.output, 'w') as fout:
gen.write(fout)
if __name__ == '__main__':
main()
#!/bin/bash -e
# $File: run.sh
# $Date: Wed Feb 15 23:13:39 2017 +0800
# $Author: jiakai <jia.kai66@gmail.com>
cd $(dirname $0)
MAKE="make -f Makefile.impl"
if [ "$1" = "--clean" ]; then
rm -rf build main*
echo "clean finished"
exit 0
fi
$MAKE TARGET=main_prof CXXFLAGS=-DMIDOUT_PROFILING BUILD_DIR=build/prof
rm -rf output
mkdir -p output
cd output
ln -sv ../main_prof main
if [ "$1" = "-i" ]; then
cat <<EOF
--------------------------------------------------------------------------------
You would be dropped into a shell now; you can run ./main with some opr mode,
and unused modes would be stripped in the next build.
EOF
bash
else
./main 1 1 p
fi
cd ..
./gen_header.py -o output/midout_gen.h output/midout_trace.*
$MAKE V= TARGET=main_midout \
CXXFLAGS='-include output/midout_gen.h' BUILD_DIR=build/run
$MAKE TARGET=main_vanilla BUILD_DIR=build/vanilla
function print_stat {
echo "$1: $(stat -c '%s' $2)"
nm $2 | c++filt | grep calc | sed -e 's/^/ /'
}
echo "-----------------"
print_stat vanilla main_vanilla
print_stat midout main_midout
#include "midout.h"
// to avoid linking error for empty lib on some stupid platforms
void midout_empty() {
}
#ifdef MIDOUT_PROFILING
#pragma message "midout profiling enabled"
#include <string>
#include <vector>
#include <unordered_set>
#include <algorithm>
#include <mutex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
namespace {
class UsedTypes {
std::unordered_set<const char*> m_types;
std::mutex m_mtx;
public:
void add(const char *type) {
std::lock_guard<std::mutex> guard{m_mtx};
m_types.insert(type);
}
~UsedTypes() {
auto output_fname = getenv("MIDOUT_OUTPUT");
char output_fname_storage[256];
if (!output_fname) {
output_fname = output_fname_storage;
snprintf(output_fname, sizeof(output_fname_storage),
"midout_trace.%d", static_cast<int>(getpid()));
}
FILE *fout = fopen(output_fname, "w");
if (!fout) {
fprintf(stderr,
"midout: failed to open output file %s: %s\n",
output_fname, strerror(errno));
}
fprintf(fout, "midout_trace v1\n");
std::vector<std::string> sorted_types;
sorted_types.reserve(m_types.size());
for (auto &&i: m_types) {
sorted_types.emplace_back(i);
}
std::sort(sorted_types.begin(), sorted_types.end());
for (auto &&i: sorted_types) {
fputs(i.c_str(), fout);
fputc('\n', fout);
}
fclose(fout);
fprintf(stderr, "midout: %zu items written to %s\n",
sorted_types.size(), output_fname);
}
};
}
void midout::on_region_used(const std::type_info &type) {
static UsedTypes used_types;
used_types.add(type.name());
}
#endif // MIDOUT_PROFILING
#ifdef MIDOUT_GENERATED
#pragma message "stripping by midout enabled"
#endif
#pragma once
#define _MIDOUT_NR_PARAMS_GET(_1, _2, _3, _4, _5, _6, _7, _8, _n, ...) _n
//! get number macro params
#define MIDOUT_NR_PARAMS(...) \
_MIDOUT_NR_PARAMS_GET(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
#define _MIDOUT_MAP1(f, _1) f(_1)
#define _MIDOUT_MAP2(f, _1, _2) f(_1), f(_2)
#define _MIDOUT_MAP3(f, _1, _2, _3) f(_1), f(_2), f(_3)
#define _MIDOUT_MAP4(f, _1, _2, _3, _4) f(_1), f(_2), f(_3), f(_4)
#define _MIDOUT_MAP5(f, _1, _2, _3, _4, _5) \
f(_1), f(_2), f(_3), f(_4), f(_5)
#define _MIDOUT_MAP6(f, _1, _2, _3, _4, _5, _6) \
f(_1), f(_2), f(_3), f(_4), f(_5), f(_6)
#define _MIDOUT_MAP7(f, _1, _2, _3, _4, _5, _6, _7) \
f(_1), f(_2), f(_3), f(_4), f(_5), f(_6), f(_7)
#define _MIDOUT_MAP8(f, _1, _2, _3, _4, _5, _6, _7, _8) \
f(_1), f(_2), f(_3), f(_4), f(_5), f(_6), f(_7), f(_8)
#define _MIDOUT_MAP_CALL1(f, n, ...) _MIDOUT_MAP##n(f, __VA_ARGS__)
#define _MIDOUT_MAP_CALL(f, n, ...) _MIDOUT_MAP_CALL1(f, n, __VA_ARGS__)
//! apply macro function \p f to given list
#define MIDOUT_MAP(f, ...) \
_MIDOUT_MAP_CALL(f, MIDOUT_NR_PARAMS(__VA_ARGS__), __VA_ARGS__)
#if defined(MIDOUT_GENERATED) || defined(MIDOUT_PROFILING)
#include <type_traits>
#include <typeinfo>
namespace midout {
namespace details {
//! used as extra params to MIDOUT_BEGIN to wrap int value as type
template<int val>
using iv = std::integral_constant<int, val>;
template<class T>
struct type_wrapper {
using type = T;
};
//! convert type and non-type template params to type
template<class T> type_wrapper<T> as_type() { return {}; }
template<int I> type_wrapper<iv<I>> as_type() { return {}; }
}
//! cast compile-time constant value to type
#define midout_iv(_val) ::midout::details::iv<static_cast<int>(_val)>
#define _midout_astype(x) \
typename decltype(::midout::details::as_type<x>())::type
#define _midout_tparams(...) MIDOUT_MAP(_midout_astype, __VA_ARGS__)
}
/*!
* \brief declare a tag that can be used in MIDOUT_BEGIN
*
* This macro must be called from the global scope
*
* \param tag tag name, which must be a valid C++ identifier
*/
#define MIDOUT_DECL(tag) \
namespace midout { \
namespace tags { \
class tag; \
} \
static_assert(std::is_same<::midout::tags::tag, tags::tag>::value, \
"MIDOUT_DECL must be called from global scope"); \
}
#endif // MIDOUT_GENERATED || MIDOUT_PROFILING
#ifndef MIDOUT_GENERATED
#ifdef MIDOUT_PROFILING
namespace midout {
void on_region_used(const std::type_info &type);
template<class Tag, typename ...Args>
struct Region {
static void on_used() {
on_region_used(typeid(Region));
}
};
}
/*!
* \brief mark a code block to be removed if not executed during profiling
* \param tag tag name that has been declared via MIDOUT_DECL
*
* Variadic args (each can either be an int or a class) can be passed to
* disambiguate different regions in the same tag
*
* The code block must be enclosed by a pair of braces and ended by MIDOUT_END
*/
#define MIDOUT_BEGIN(tag, ...) \
do { \
::midout::Region< \
_midout_tparams(::midout::tags::tag, ## __VA_ARGS__)>::on_used(); \
if (1)
#define MIDOUT_END() \
} while(0)
#else // MIDOUT_PROFILING
#define MIDOUT_DECL(tag)
#define MIDOUT_BEGIN(tag, ...) do
#define MIDOUT_END() while(0)
#endif // MIDOUT_PROFILING
#else // MIDOUT_GENERATED
namespace midout {
template<class Tag, typename ...Args>
struct Region {
static constexpr bool enable = false;
};
}
MIDOUT_GENERATED
#define MIDOUT_BEGIN(tag, ...) \
do { \
if (::midout::Region< \
_midout_tparams(::midout::tags::tag, ## __VA_ARGS__)>::enable)
#define MIDOUT_END() \
else { \
__builtin_trap(); \
} \
} while(0)
#endif // MIDOUT_GENERATED
// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册