From 990fd2b0aac7580e333e75b27eefaaea27e7fdc0 Mon Sep 17 00:00:00 2001 From: Sergei Trifonov Date: Mon, 24 Jan 2022 22:16:47 +0300 Subject: [PATCH] add c++expr script for C++ one-liners --- utils/c++expr | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100755 utils/c++expr diff --git a/utils/c++expr b/utils/c++expr new file mode 100755 index 00000000000..ec24b97ebc9 --- /dev/null +++ b/utils/c++expr @@ -0,0 +1,288 @@ +#!/usr/bin/env bash +set -e + +usage() { + cat <&2 +USAGE: c++expr [-c CXX | -C | -I] [-i INCLUDE] [-b STEPS] [-t TESTS] [-o FILE] [-O CXX_OPTS...] [-g 'GLOBAL CODE'] 'MAIN CODE' +OPTIONS: + -c CXX use specified c++ compiler + -C use cmake + -I integrate into ClickHouse build tree in current directory + -i INC add #include + -l LIB link against LIB (only for -I or -C) + -b STEPS_NUM make program to benchmark specified code snippet and run tests with STEPS_NUM each + -b perf-top run infinite benchmark and show perf top + -t TESTS_NUM make program to benchmark specified code snippet and run TESTS_NUM tests + -o FILE do not run, just save binary executable file + -O CXX_OPTS forward option compiler (e.g. -O "-O3 -std=c++20") +EOF + exit 1 +} + +SOURCE_FILE=main.cpp +GLOBAL= +OUTPUT_EXECUTABLE= +INCS="vector iostream typeinfo cstdlib cmath sys/time.h" +LIBS="" +BENCHMARK_STEPS=0 +RUN_PERFTOP= +BENCHMARK_TESTS=5 +USE_CMAKE= +USE_CLICKHOUSE= +CXX=g++ +CXX_OPTS= +CMD_PARAMS= + +# +# Parse command line +# + +if [ "$1" == "--help" ]; then usage; fi +while getopts "vc:CIi:l:b:t:o:O:g:" OPT; do + case "$OPT" in + v) set -x; ;; + c) CXX="$OPTARG"; ;; + C) USE_CMAKE=y; ;; + I) USE_CLICKHOUSE=y; LIBS="$LIBS clickhouse_common_io"; ;; + i) INCS="$INCS $OPTARG"; ;; + l) LIBS="$LIBS $OPTARG"; ;; + b) if [ "$OPTARG" = perf-top ]; then BENCHMARK_STEPS=-1; RUN_PERFTOP=y; else BENCHMARK_STEPS="$OPTARG"; fi; ;; + t) BENCHMARK_TESTS="$OPTARG"; ;; + o) OUTPUT_EXECUTABLE="$OPTARG"; ;; + O) CXX_OPTS="$CXX_OPTS $OPTARG"; ;; + g) GLOBAL="$OPTARG"; ;; + esac +done +shift $(( $OPTIND - 1 )) + +# +# Positional arguments +# + +EXPR=$1 +shift + +if [ -z "$EXPR" ]; then usage; fi + +# +# Arguments forwarded to program should go after main code and before -- +# + +while [ -n "$1" ] && [ "$1" != "--" ]; do + CMD_PARAMS="$CMD_PARAMS $1" + shift +done +if [ "$1" == "--" ]; then shift; fi + +# +# Setup workdir +# + +find_clickhouse_root () { + local DIR="`pwd`" + while [ $DIR != "/" ]; do + if [ ! -e "$DIR/CMakeLists.txt" ]; then + echo "error: $DIR has no CMakeLists.txt" + return 1 + fi + if grep "project(ClickHouse)" "$DIR/CMakeLists.txt" >/dev/null 2>&1; then + echo $DIR + return 0 + fi + DIR="`dirname $DIR`" + done + echo "error: unable to find Clickhouse root folder" + return 1 +} + +find_clickhouse_build () { + local CLICKHOUSE_ROOT="`find_clickhouse_root`" + if [ -e "$CLICKHOUSE_ROOT/build/CMakeCache.txt" ]; then + echo "$CLICKHOUSE_ROOT/build" + return 0 + fi + echo "error: $CLICKHOUSE_ROOT/build/CMakeCache.txt doesn't exist" + return 1 +} + +CALL_DIR=`pwd` +EXECUTABLE=cppexpr_$$ +EXECUTABLE_DIR=. + +if [ -n "$USE_CLICKHOUSE" ]; then + SUBDIR=cppexpr_$$ + WORKDIR=$CALL_DIR/$SUBDIR + if [ ! -e $CALL_DIR/CMakeLists.txt ]; then + echo "error: $CALL_DIR/CMakeLists.txt is required for integration" >&2 + exit 1 + fi + + CLICKHOUSE_ROOT="`find_clickhouse_root`" + BUILD_ROOT="`find_clickhouse_build`" + CLICKHOUSE_PATH="${WORKDIR/$CLICKHOUSE_ROOT}" + EXECUTABLE_DIR="${BUILD_ROOT}${CLICKHOUSE_PATH}" + + if [ -z "$CLICKHOUSE_ROOT" ] || [ -z "$BUILD_ROOT" ] || [ -z "$CLICKHOUSE_PATH" ]; then + echo "error: unable to locate ClickHouse" >&2 + exit 1 + fi + + cp $CALL_DIR/CMakeLists.txt $CALL_DIR/CMakeLists.txt.backup.$$ + echo "add_subdirectory ($SUBDIR)" >>$CALL_DIR/CMakeLists.txt + cleanup() { + mv $CALL_DIR/CMakeLists.txt.backup.$$ $CALL_DIR/CMakeLists.txt + rm -rf $WORKDIR + rm -rf ${BUILD_ROOT}${CLICKHOUSE_PATH} + } +else + WORKDIR=/var/tmp/cppexpr_$$ + cleanup() { + rm -rf $WORKDIR + } +fi + +mkdir -p $WORKDIR +cd $WORKDIR + +# +# Generate CMakeLists.txt +# +if [ -n "$USE_CMAKE" ]; then + cat <>CMakeLists.txt +project(CppExpr) +SET(PROJECT_NAME CppExpr) +SET(CMAKE_INCLUDE_CURRENT_DIR TRUE) +cmake_minimum_required(VERSION 2.8) +set(CMAKE_CXX_FLAGS -fPIC) +set(CMAKE_C_FLAGS -fPIC) +set(CMAKE_BUILD_TYPE Release) +set(SOURCES $SOURCE_FILE) +add_executable($EXECUTABLE \${SOURCES}) +EOF +fi + +# +# Generate CMakeLists.txt for integration +# +if [ -n "$USE_CLICKHOUSE" ]; then + cat <>CMakeLists.txt +add_executable($EXECUTABLE $SOURCE_FILE) +EOF +fi + +# +# Add libraries to CMakeLists.txt +# +if [ -n "$LIBS" ]; then + cat <>CMakeLists.txt +target_link_libraries($EXECUTABLE PRIVATE $LIBS) +EOF +fi + +# +# Generate source code +# +>$SOURCE_FILE +for INC in $INCS; do + echo "#include <$INC>" >> $SOURCE_FILE +done +cat <>$SOURCE_FILE + +#define OUT(expr) std::cout << #expr << " -> " << (expr) << std::endl; +size_t max_tests = $BENCHMARK_TESTS; +size_t max_steps = $BENCHMARK_STEPS; +$GLOBAL +int main(int argc, char** argv) { + (void)argc; (void)argv; + try { +EOF + +if [ $BENCHMARK_STEPS -eq 0 ]; then + cat <>$SOURCE_FILE + $EXPR +EOF +else + cat <>$SOURCE_FILE + std::cout << "Steps per test: " << max_steps << std::endl; + if (max_steps == 0) max_steps = 1; + double total = 0.0; + for (size_t test = 0; test < max_tests; test++) { + timeval beg, end; + gettimeofday(&beg, nullptr); + for (size_t step = 0; step < max_steps; step++) { + asm volatile("" ::: "memory"); + $EXPR + } + gettimeofday(&end, nullptr); + double interval = (end.tv_sec - beg.tv_sec)*1e6 + (end.tv_usec - beg.tv_usec); + std::cout << "Test #" << test << ": " << interval / max_steps << " us\t" << max_steps * 1e6 / interval << " sps" << std::endl; + total += interval; + } + std::cout << "Average: " << total / max_tests / max_steps << " us\t" << max_steps * 1e6 / (total / max_tests) << " sps" << std::endl; +EOF +fi + +cat <>$SOURCE_FILE + return 0; + } catch (std::exception& e) { + std::cerr << "unhandled exception (" << typeid(e).name() << "):" << e.what() << std::endl; + } catch (...) { + std::cerr << "unknown unhandled exception\n"; + } + return 1; +} +#ifdef OUT +#undef OUT +#endif +EOF + +# +# Compile +# +if [ -n "$USE_CMAKE" ]; then + if ! (cmake . && make); then + cat -n $SOURCE_FILE + cleanup + exit 1 + fi +elif [ -n "$USE_CLICKHOUSE" ]; then + if ! (cd $BUILD_ROOT && ninja $EXECUTABLE) >stdout.log 2>stderr.log; then + cat stdout.log + cat stderr.log >&2 + cat -n $SOURCE_FILE + cleanup + exit 1 + fi +else + RET=0 + $CXX $CXX_OPTS -I$CALL_DIR -o $EXECUTABLE $SOURCE_FILE || RET=$? + if [ $RET -ne 0 ]; then + cat -n $SOURCE_FILE + cleanup + exit $RET + fi +fi + +# +# Execute +# +RET=0 +if [ -z "$OUTPUT_EXECUTABLE" ]; then + if [ -z "$RUN_PERFTOP" ]; then + "$@" $EXECUTABLE_DIR/$EXECUTABLE $CMD_PARAMS || RET=$? + else + "$@" $EXECUTABLE_DIR/$EXECUTABLE $CMD_PARAMS & + PID=$! + perf top -p $PID + kill $PID + fi +else + cp $EXECUTABLE_DIR/$EXECUTABLE $CALL_DIR/$OUTPUT_EXECUTABLE +fi + +# +# Cleanup +# +cleanup +echo "Exit code: $RET" +exit $RET