<commit_before>/*
 * The main source of the Beng proxy server.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "direct.hxx"
#include "lb_instance.hxx"
#include "lb_check.hxx"
#include "lb_setup.hxx"
#include "lb_connection.hxx"
#include "tcp_stock.hxx"
#include "tcp_balancer.hxx"
#include "stock/MapStock.hxx"
#include "failure.hxx"
#include "bulldog.h"
#include "balancer.hxx"
#include "pipe_stock.hxx"
#include "log-glue.h"
#include "lb_config.hxx"
#include "lb_hmonitor.hxx"
#include "ssl/ssl_init.hxx"
#include "child_manager.hxx"
#include "pool.hxx"
#include "thread_pool.hxx"
#include "fb_pool.hxx"
#include "capabilities.hxx"
#include "isolate.hxx"
#include "system/SetupProcess.hxx"
#include "util/PrintException.hxx"
#include "util/Error.hxx"
#include "util/Macros.hxx"

#include <daemon/log.h>
#include <daemon/daemonize.h>

#include <assert.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>

#ifdef __linux
#include <sys/prctl.h>

#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
#endif

static constexpr cap_value_t cap_keep_list[1] = {
    /* keep the NET_RAW capability to be able to to use the socket
       option IP_TRANSPARENT */
    CAP_NET_RAW,
};

static constexpr struct timeval launch_worker_now = {
    0,
    10000,
};

static constexpr struct timeval launch_worker_delayed = {
    10,
    0,
};

static bool is_watchdog;
static pid_t worker_pid;

static void
worker_callback(int status, void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    int exit_status = WEXITSTATUS(status);
    if (WIFSIGNALED(status))
        fprintf(stderr, "worker %d died from signal %d%s\n",
                worker_pid, WTERMSIG(status),
                WCOREDUMP(status) ? " (core dumped)" : "");
    else if (exit_status == 0)
        fprintf(stderr, "worker %d exited with success\n",
                worker_pid);
    else
        fprintf(stderr, "worker %d exited with status %d\n",
                worker_pid, exit_status);

    worker_pid = 0;

    if (!instance->should_exit)
        instance->launch_worker_event.Add(launch_worker_delayed);
}

static void
launch_worker_callback(int fd gcc_unused, short event gcc_unused,
                       void *ctx)
{
    assert(is_watchdog);
    assert(worker_pid <= 0);

    struct lb_instance *instance = (struct lb_instance *)ctx;

    /* in libevent 2.0.16, it is necessary to re-add all EV_SIGNAL
       events after forking; this bug is not present in 1.4.13 and
       2.0.19 */
    deinit_signals(instance);
    children_event_del();

    worker_pid = fork();
    if (worker_pid < 0) {
        fprintf(stderr, "Failed to fork: %s\n", strerror(errno));

        init_signals(instance);
        children_event_add();

        instance->launch_worker_event.Add(launch_worker_delayed);
        return;
    }

    if (worker_pid == 0) {
        is_watchdog = false;

        instance->event_base.Reinit();
        init_signals(instance);

        children_init();
        all_listeners_event_add(instance);

        enable_all_controls(instance);

        /* run monitors only in the worker process */
        lb_hmonitor_enable();

        instance->ConnectCertCaches();
        return;
    }

    init_signals(instance);
    children_event_add();

    child_register(worker_pid, "worker", worker_callback, instance);
}

void
lb_instance::ShutdownCallback(void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    if (instance->should_exit)
        return;

    instance->should_exit = true;
    deinit_signals(instance);
    thread_pool_stop();

    if (is_watchdog && worker_pid > 0)
        kill(worker_pid, SIGTERM);

    children_shutdown();

    if (is_watchdog)
        instance->launch_worker_event.Cancel();

    deinit_all_controls(instance);

    if (!is_watchdog)
        instance->DisconnectCertCaches();

    while (!instance->connections.empty())
        lb_connection_close(&instance->connections.front());
    assert(instance->n_tcp_connections == 0);

    deinit_all_listeners(instance);

    thread_pool_join();
    thread_pool_deinit();

    lb_hmonitor_deinit();

    pool_commit();

    if (instance->tcp_balancer != nullptr)
        tcp_balancer_free(instance->tcp_balancer);

    if (instance->tcp_stock != nullptr)
        hstock_free(instance->tcp_stock);

    if (instance->balancer != nullptr)
        balancer_free(instance->balancer);

    if (instance->pipe_stock != nullptr)
        pipe_stock_free(instance->pipe_stock);

    fb_pool_disable();

    pool_commit();
}

static void
reload_event_callback(int fd gcc_unused, short event gcc_unused,
                      void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    daemonize_reopen_logfile();

    unsigned n_ssl_sessions = instance->FlushSSLSessionCache(LONG_MAX);
    daemon_log(3, "flushed %u SSL sessions\n", n_ssl_sessions);

    fb_pool_compress();
}

void
init_signals(struct lb_instance *instance)
{
    instance->shutdown_listener.Enable();

    instance->sighup_event.Set(SIGHUP, reload_event_callback, instance);
    instance->sighup_event.Add();
}

void
deinit_signals(struct lb_instance *instance)
{
    instance->shutdown_listener.Disable();
    instance->sighup_event.Delete();
}

int main(int argc, char **argv)
{
    struct lb_instance instance;

    /* configuration */

    parse_cmdline(&instance.cmdline, argc, argv);

    try {
        instance.config = new LbConfig(lb_config_load(instance.pool,
                                                      instance.cmdline.config_path));
    } catch (const std::exception &e) {
        PrintException(e);
        return EXIT_FAILURE;
    }

    if (instance.cmdline.check) {
        int status = EXIT_SUCCESS;

        const ScopeSslGlobalInit ssl_init;

        try {
            lb_check(*instance.config);
        } catch (const std::exception &e) {
            PrintException(e);
            status = EXIT_FAILURE;
        }

        delete instance.config;
        return status;
    }

    /* initialize */

    SetupProcess();

    lb_hmonitor_init(instance.pool);

    const ScopeSslGlobalInit ssl_init;

    direct_global_init();

    init_signals(&instance);

    try {
        Error error2;

        init_all_controls(&instance);

        if (!init_all_listeners(instance, error2)) {
            deinit_all_controls(&instance);
            fprintf(stderr, "%s\n", error2.GetMessage());
            return EXIT_FAILURE;
        }
    } catch (const std::exception &e) {
        fprintf(stderr, "%s\n", e.what());
        return EXIT_FAILURE;
    }

    /* daemonize */

    if (daemonize() < 0)
        exit(2);

    /* post-daemon initialization */

    fb_pool_init(true);

    children_init();

    instance.balancer = balancer_new(*instance.pool);
    instance.tcp_stock = tcp_stock_new(instance.cmdline.tcp_stock_limit);
    instance.tcp_balancer = tcp_balancer_new(*instance.tcp_stock,
                                             *instance.balancer);

    instance.pipe_stock = pipe_stock_new();

    failure_init();
    bulldog_init(instance.cmdline.bulldog_path);

    /* launch the access logger */

    if (!log_global_init(instance.cmdline.access_logger))
        return EXIT_FAILURE;

    /* daemonize II */

    if (daemon_user_defined(&instance.cmdline.user))
        capabilities_pre_setuid();

    if (daemon_user_set(&instance.cmdline.user) < 0)
        return EXIT_FAILURE;

    /* can't change to new (empty) rootfs if we may need to reconnect
       to PostgreSQL eventually */
    // TODO: bind-mount the PostgreSQL socket into the new rootfs
    if (!instance.config->HasCertDatabase())
        isolate_from_filesystem();

    if (daemon_user_defined(&instance.cmdline.user))
        capabilities_post_setuid(cap_keep_list, ARRAY_SIZE(cap_keep_list));

#ifdef __linux
    prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
#endif

    /* main loop */

    if (instance.cmdline.watchdog) {
        /* watchdog */

        all_listeners_event_del(&instance);

        is_watchdog = true;
        instance.launch_worker_event.Init(launch_worker_callback, &instance);
        instance.launch_worker_event.Add(launch_worker_now);
    } else {
        /* this is already the worker process: enable monitors here */
        lb_hmonitor_enable();

        instance.ConnectCertCaches();
    }

    instance.event_base.Dispatch();

    /* cleanup */

    children_shutdown();

    log_global_deinit();

    bulldog_deinit();
    failure_deinit();

    deinit_all_listeners(&instance);
    deinit_all_controls(&instance);

    fb_pool_deinit();

    delete instance.config;

    pool_recycler_clear();

    daemonize_cleanup();
}
<commit_msg>lb_main: remove redundant child_callback_t code<commit_after>/*
 * The main source of the Beng proxy server.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "direct.hxx"
#include "lb_instance.hxx"
#include "lb_check.hxx"
#include "lb_setup.hxx"
#include "lb_connection.hxx"
#include "tcp_stock.hxx"
#include "tcp_balancer.hxx"
#include "stock/MapStock.hxx"
#include "failure.hxx"
#include "bulldog.h"
#include "balancer.hxx"
#include "pipe_stock.hxx"
#include "log-glue.h"
#include "lb_config.hxx"
#include "lb_hmonitor.hxx"
#include "ssl/ssl_init.hxx"
#include "child_manager.hxx"
#include "pool.hxx"
#include "thread_pool.hxx"
#include "fb_pool.hxx"
#include "capabilities.hxx"
#include "isolate.hxx"
#include "system/SetupProcess.hxx"
#include "util/PrintException.hxx"
#include "util/Error.hxx"
#include "util/Macros.hxx"

#include <daemon/log.h>
#include <daemon/daemonize.h>

#include <assert.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>

#ifdef __linux
#include <sys/prctl.h>

#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
#endif

static constexpr cap_value_t cap_keep_list[1] = {
    /* keep the NET_RAW capability to be able to to use the socket
       option IP_TRANSPARENT */
    CAP_NET_RAW,
};

static constexpr struct timeval launch_worker_now = {
    0,
    10000,
};

static constexpr struct timeval launch_worker_delayed = {
    10,
    0,
};

static bool is_watchdog;
static pid_t worker_pid;

static void
worker_callback(gcc_unused int status, void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    worker_pid = 0;

    if (!instance->should_exit)
        instance->launch_worker_event.Add(launch_worker_delayed);
}

static void
launch_worker_callback(int fd gcc_unused, short event gcc_unused,
                       void *ctx)
{
    assert(is_watchdog);
    assert(worker_pid <= 0);

    struct lb_instance *instance = (struct lb_instance *)ctx;

    /* in libevent 2.0.16, it is necessary to re-add all EV_SIGNAL
       events after forking; this bug is not present in 1.4.13 and
       2.0.19 */
    deinit_signals(instance);
    children_event_del();

    worker_pid = fork();
    if (worker_pid < 0) {
        fprintf(stderr, "Failed to fork: %s\n", strerror(errno));

        init_signals(instance);
        children_event_add();

        instance->launch_worker_event.Add(launch_worker_delayed);
        return;
    }

    if (worker_pid == 0) {
        is_watchdog = false;

        instance->event_base.Reinit();
        init_signals(instance);

        children_init();
        all_listeners_event_add(instance);

        enable_all_controls(instance);

        /* run monitors only in the worker process */
        lb_hmonitor_enable();

        instance->ConnectCertCaches();
        return;
    }

    init_signals(instance);
    children_event_add();

    child_register(worker_pid, "worker", worker_callback, instance);
}

void
lb_instance::ShutdownCallback(void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    if (instance->should_exit)
        return;

    instance->should_exit = true;
    deinit_signals(instance);
    thread_pool_stop();

    if (is_watchdog && worker_pid > 0)
        kill(worker_pid, SIGTERM);

    children_shutdown();

    if (is_watchdog)
        instance->launch_worker_event.Cancel();

    deinit_all_controls(instance);

    if (!is_watchdog)
        instance->DisconnectCertCaches();

    while (!instance->connections.empty())
        lb_connection_close(&instance->connections.front());
    assert(instance->n_tcp_connections == 0);

    deinit_all_listeners(instance);

    thread_pool_join();
    thread_pool_deinit();

    lb_hmonitor_deinit();

    pool_commit();

    if (instance->tcp_balancer != nullptr)
        tcp_balancer_free(instance->tcp_balancer);

    if (instance->tcp_stock != nullptr)
        hstock_free(instance->tcp_stock);

    if (instance->balancer != nullptr)
        balancer_free(instance->balancer);

    if (instance->pipe_stock != nullptr)
        pipe_stock_free(instance->pipe_stock);

    fb_pool_disable();

    pool_commit();
}

static void
reload_event_callback(int fd gcc_unused, short event gcc_unused,
                      void *ctx)
{
    struct lb_instance *instance = (struct lb_instance *)ctx;

    daemonize_reopen_logfile();

    unsigned n_ssl_sessions = instance->FlushSSLSessionCache(LONG_MAX);
    daemon_log(3, "flushed %u SSL sessions\n", n_ssl_sessions);

    fb_pool_compress();
}

void
init_signals(struct lb_instance *instance)
{
    instance->shutdown_listener.Enable();

    instance->sighup_event.Set(SIGHUP, reload_event_callback, instance);
    instance->sighup_event.Add();
}

void
deinit_signals(struct lb_instance *instance)
{
    instance->shutdown_listener.Disable();
    instance->sighup_event.Delete();
}

int main(int argc, char **argv)
{
    struct lb_instance instance;

    /* configuration */

    parse_cmdline(&instance.cmdline, argc, argv);

    try {
        instance.config = new LbConfig(lb_config_load(instance.pool,
                                                      instance.cmdline.config_path));
    } catch (const std::exception &e) {
        PrintException(e);
        return EXIT_FAILURE;
    }

    if (instance.cmdline.check) {
        int status = EXIT_SUCCESS;

        const ScopeSslGlobalInit ssl_init;

        try {
            lb_check(*instance.config);
        } catch (const std::exception &e) {
            PrintException(e);
            status = EXIT_FAILURE;
        }

        delete instance.config;
        return status;
    }

    /* initialize */

    SetupProcess();

    lb_hmonitor_init(instance.pool);

    const ScopeSslGlobalInit ssl_init;

    direct_global_init();

    init_signals(&instance);

    try {
        Error error2;

        init_all_controls(&instance);

        if (!init_all_listeners(instance, error2)) {
            deinit_all_controls(&instance);
            fprintf(stderr, "%s\n", error2.GetMessage());
            return EXIT_FAILURE;
        }
    } catch (const std::exception &e) {
        fprintf(stderr, "%s\n", e.what());
        return EXIT_FAILURE;
    }

    /* daemonize */

    if (daemonize() < 0)
        exit(2);

    /* post-daemon initialization */

    fb_pool_init(true);

    children_init();

    instance.balancer = balancer_new(*instance.pool);
    instance.tcp_stock = tcp_stock_new(instance.cmdline.tcp_stock_limit);
    instance.tcp_balancer = tcp_balancer_new(*instance.tcp_stock,
                                             *instance.balancer);

    instance.pipe_stock = pipe_stock_new();

    failure_init();
    bulldog_init(instance.cmdline.bulldog_path);

    /* launch the access logger */

    if (!log_global_init(instance.cmdline.access_logger))
        return EXIT_FAILURE;

    /* daemonize II */

    if (daemon_user_defined(&instance.cmdline.user))
        capabilities_pre_setuid();

    if (daemon_user_set(&instance.cmdline.user) < 0)
        return EXIT_FAILURE;

    /* can't change to new (empty) rootfs if we may need to reconnect
       to PostgreSQL eventually */
    // TODO: bind-mount the PostgreSQL socket into the new rootfs
    if (!instance.config->HasCertDatabase())
        isolate_from_filesystem();

    if (daemon_user_defined(&instance.cmdline.user))
        capabilities_post_setuid(cap_keep_list, ARRAY_SIZE(cap_keep_list));

#ifdef __linux
    prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
#endif

    /* main loop */

    if (instance.cmdline.watchdog) {
        /* watchdog */

        all_listeners_event_del(&instance);

        is_watchdog = true;
        instance.launch_worker_event.Init(launch_worker_callback, &instance);
        instance.launch_worker_event.Add(launch_worker_now);
    } else {
        /* this is already the worker process: enable monitors here */
        lb_hmonitor_enable();

        instance.ConnectCertCaches();
    }

    instance.event_base.Dispatch();

    /* cleanup */

    children_shutdown();

    log_global_deinit();

    bulldog_deinit();
    failure_deinit();

    deinit_all_listeners(&instance);
    deinit_all_controls(&instance);

    fb_pool_deinit();

    delete instance.config;

    pool_recycler_clear();

    daemonize_cleanup();
}
<|endoftext|>


<commit_before>// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/gtest/gtest.h>
#include <vespa/searchcommon/attribute/attributecontent.h>
#include <vespa/searchlib/attribute/attributefactory.h>
#include <vespa/searchlib/attribute/attributevector.hpp>
#include <vespa/searchlib/attribute/integerbase.h>
#include <vespa/searchlib/attribute/stringbase.h>

#include <vespa/log/log.h>
LOG_SETUP("enum_attribute_compaction_test");

using search::IntegerAttribute;
using search::StringAttribute;
using search::AttributeVector;
using search::attribute::Config;
using search::attribute::BasicType;
using search::attribute::CollectionType;
using EnumHandle = search::attribute::IAttributeVector::EnumHandle;

template <typename VectorType> struct TestData;

template <>
struct TestData<IntegerAttribute> {
    using BufferType = search::attribute::IntegerContent;
    using CheckType = int32_t;
    static constexpr BasicType::Type basic_type = BasicType::INT32;
    static int32_t make_value(uint32_t doc_id, uint32_t idx) { return doc_id * 10 + idx; }
    static int32_t as_add(int32_t value) { return value; }
    static int32_t make_undefined_value() { return std::numeric_limits<int32_t>::min(); }
};

template <>
struct TestData<StringAttribute> {
    using BufferType = search::attribute::ConstCharContent;
    using CheckType = std::string;
    static constexpr BasicType::Type basic_type = BasicType::STRING;
    static std::string make_value(uint32_t doc_id, uint32_t idx) {
        uint32_t combined = doc_id * 10 + idx;
        vespalib::asciistream s;
        if (doc_id == 2 && idx == 0) {
            // Longer string will be stored in a different buffer
            s << "bb345678901234";
        } else {
            s << combined;
        }
        return s.str();
    }
    static const char *as_add(const std::string &value) { return value.c_str(); }
    static std::string make_undefined_value() { return std::string(); }
};

class CompactionTestBase : public ::testing::TestWithParam<CollectionType::Type> {
public:
    std::shared_ptr<AttributeVector> _v;

    CompactionTestBase()
        : _v()
    {
    }
    void SetUp() override;
    virtual BasicType get_basic_type() const = 0;
    CollectionType get_collection_type() const noexcept { return GetParam(); }
    void addDocs(uint32_t num_docs);
    uint32_t count_changed_enum_handles(const std::vector<EnumHandle> &handles, uint32_t stride);
};

void
CompactionTestBase::SetUp()
{
    Config cfg(get_basic_type(), get_collection_type());
    cfg.setFastSearch(true);
    _v = search::AttributeFactory::createAttribute("test", cfg);
}

void
CompactionTestBase::addDocs(uint32_t num_docs)
{
    uint32_t start_doc;
    uint32_t end_doc;
    _v->addDocs(start_doc, end_doc, num_docs);
    for (uint32_t doc = start_doc; doc <= end_doc; ++doc) {
        _v->clearDoc(doc);
    }
    _v->commit();
}

uint32_t
CompactionTestBase::count_changed_enum_handles(const std::vector<EnumHandle> &handles, uint32_t stride)
{
    uint32_t changed = 0;
    for (uint32_t doc_id = 0; doc_id < handles.size(); doc_id += stride) {
        if (_v->getEnum(doc_id) != handles[doc_id]) {
            ++changed;
        }
    }
    return changed;
}

template <typename VectorType>
class CompactionTest : public CompactionTestBase
{
public:
    CompactionTest();
    void set_values(uint32_t doc_id);
    void check_values(uint32_t doc_id);
    void check_cleared_values(uint32_t doc_id);
    void test_enum_store_compaction();
    BasicType get_basic_type() const override { return TestData<VectorType>::basic_type; }
};

template <typename VectorType>
CompactionTest<VectorType>::CompactionTest()
    : CompactionTestBase()
{
}

template <typename VectorType>
void
CompactionTest<VectorType>::set_values(uint32_t doc_id)
{
    using MyTestData = TestData<VectorType>;
    auto &typed_v = dynamic_cast<VectorType &>(*_v);
    _v->clearDoc(doc_id);
    if (_v->hasMultiValue()) {
        EXPECT_TRUE(typed_v.append(doc_id, MyTestData::as_add(MyTestData::make_value(doc_id, 0)), 1));
        EXPECT_TRUE(typed_v.append(doc_id, MyTestData::as_add(MyTestData::make_value(doc_id, 1)), 1));
    } else {
        EXPECT_TRUE(typed_v.update(doc_id, MyTestData::as_add(MyTestData::make_value(doc_id, 0))));
    }
    _v->commit();
}

template <typename VectorType>
void
CompactionTest<VectorType>::check_values(uint32_t doc_id)
{
    using MyTestData = TestData<VectorType>;
    using CheckType = typename MyTestData::CheckType;
    typename MyTestData::BufferType buffer;
    buffer.fill(*_v, doc_id);
    if (_v->hasMultiValue()) {
        EXPECT_EQ(2u, buffer.size());
        EXPECT_EQ(CheckType(buffer[0]), MyTestData::make_value(doc_id, 0));
        EXPECT_EQ(CheckType(buffer[1]), MyTestData::make_value(doc_id, 1));
    } else {
        EXPECT_EQ(1u, buffer.size());
        EXPECT_EQ(CheckType(buffer[0]), MyTestData::make_value(doc_id, 0));
    }
}

template <typename VectorType>
void
CompactionTest<VectorType>::check_cleared_values(uint32_t doc_id)
{
    using MyTestData = TestData<VectorType>;
    using CheckType = typename MyTestData::CheckType;
    typename MyTestData::BufferType buffer;
    buffer.fill(*_v, doc_id);
    if (_v->hasMultiValue()) {
        EXPECT_EQ(0u, buffer.size());
    } else {
        EXPECT_EQ(1u, buffer.size());
        EXPECT_EQ(CheckType(buffer[0]), MyTestData::make_undefined_value());
    }
}

template <typename VectorType>
void
CompactionTest<VectorType>::test_enum_store_compaction()
{
    constexpr size_t DEAD_BYTES_SLACK = 0x10000u;
    constexpr uint32_t canary_stride = 256;
    uint32_t dead_limit = DEAD_BYTES_SLACK / 8;
    uint32_t doc_count = dead_limit * 3;
    if (_v->hasMultiValue() || std::is_same_v<VectorType,StringAttribute>) {
        doc_count /= 2;
    }
    std::vector<EnumHandle> enum_handles;
    addDocs(doc_count);
    enum_handles.emplace_back(_v->getEnum(0));
    uint32_t doc_id;
    for (doc_id = 1; doc_id < doc_count; ++doc_id) {
        set_values(doc_id);
        enum_handles.emplace_back(_v->getEnum(doc_id));
    }
    uint32_t last_cleared_doc_id = 0;
    for (doc_id = 1; doc_id < doc_count; doc_id += 2) {
        _v->clearDoc(doc_id);
        _v->commit(true);
        enum_handles[doc_id] = enum_handles[0];
        last_cleared_doc_id = doc_id;
        if (count_changed_enum_handles(enum_handles, canary_stride) != 0) {
            LOG(info, "Detected enum store compaction at doc_id %u", doc_id);
            break;
        }
    }
    EXPECT_LT(doc_id, doc_count);
    uint32_t changed_enum_handles = count_changed_enum_handles(enum_handles, 1);
    LOG(info, "%u enum handles changed", changed_enum_handles);
    EXPECT_LT(0u, changed_enum_handles);
    for (doc_id = 1; doc_id < doc_count; ++doc_id) {
        if ((doc_id % 2) == 0 || doc_id > last_cleared_doc_id) {
            check_values(doc_id);
        } else {
            check_cleared_values(doc_id);
        }
    }
}

using IntegerCompactionTest = CompactionTest<IntegerAttribute>;

TEST_P(IntegerCompactionTest, compact)
{
    test_enum_store_compaction();
}

INSTANTIATE_TEST_CASE_P(IntegerCompactionTestSet, IntegerCompactionTest, ::testing::Values(CollectionType::SINGLE, CollectionType::ARRAY, CollectionType::WSET));

using StringCompactionTest = CompactionTest<StringAttribute>;

TEST_P(StringCompactionTest, compact)
{
    test_enum_store_compaction();
}

INSTANTIATE_TEST_CASE_P(StringCompactionTestSet, StringCompactionTest, ::testing::Values(CollectionType::SINGLE, CollectionType::ARRAY, CollectionType::WSET));

GTEST_MAIN_RUN_ALL_TESTS()
<commit_msg>Style fix (method naming).<commit_after>// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/gtest/gtest.h>
#include <vespa/searchcommon/attribute/attributecontent.h>
#include <vespa/searchlib/attribute/attributefactory.h>
#include <vespa/searchlib/attribute/attributevector.hpp>
#include <vespa/searchlib/attribute/integerbase.h>
#include <vespa/searchlib/attribute/stringbase.h>

#include <vespa/log/log.h>
LOG_SETUP("enum_attribute_compaction_test");

using search::IntegerAttribute;
using search::StringAttribute;
using search::AttributeVector;
using search::attribute::Config;
using search::attribute::BasicType;
using search::attribute::CollectionType;
using EnumHandle = search::attribute::IAttributeVector::EnumHandle;

template <typename VectorType> struct TestData;

template <>
struct TestData<IntegerAttribute> {
    using BufferType = search::attribute::IntegerContent;
    using CheckType = int32_t;
    static constexpr BasicType::Type basic_type = BasicType::INT32;
    static int32_t make_value(uint32_t doc_id, uint32_t idx) { return doc_id * 10 + idx; }
    static int32_t as_add(int32_t value) { return value; }
    static int32_t make_undefined_value() { return std::numeric_limits<int32_t>::min(); }
};

template <>
struct TestData<StringAttribute> {
    using BufferType = search::attribute::ConstCharContent;
    using CheckType = std::string;
    static constexpr BasicType::Type basic_type = BasicType::STRING;
    static std::string make_value(uint32_t doc_id, uint32_t idx) {
        uint32_t combined = doc_id * 10 + idx;
        vespalib::asciistream s;
        if (doc_id == 2 && idx == 0) {
            // Longer string will be stored in a different buffer
            s << "bb345678901234";
        } else {
            s << combined;
        }
        return s.str();
    }
    static const char *as_add(const std::string &value) { return value.c_str(); }
    static std::string make_undefined_value() { return std::string(); }
};

class CompactionTestBase : public ::testing::TestWithParam<CollectionType::Type> {
public:
    std::shared_ptr<AttributeVector> _v;

    CompactionTestBase()
        : _v()
    {
    }
    void SetUp() override;
    virtual BasicType get_basic_type() const = 0;
    CollectionType get_collection_type() const noexcept { return GetParam(); }
    void add_docs(uint32_t num_docs);
    uint32_t count_changed_enum_handles(const std::vector<EnumHandle> &handles, uint32_t stride);
};

void
CompactionTestBase::SetUp()
{
    Config cfg(get_basic_type(), get_collection_type());
    cfg.setFastSearch(true);
    _v = search::AttributeFactory::createAttribute("test", cfg);
}

void
CompactionTestBase::add_docs(uint32_t num_docs)
{
    uint32_t start_doc;
    uint32_t end_doc;
    _v->addDocs(start_doc, end_doc, num_docs);
    for (uint32_t doc = start_doc; doc <= end_doc; ++doc) {
        _v->clearDoc(doc);
    }
    _v->commit();
}

uint32_t
CompactionTestBase::count_changed_enum_handles(const std::vector<EnumHandle> &handles, uint32_t stride)
{
    uint32_t changed = 0;
    for (uint32_t doc_id = 0; doc_id < handles.size(); doc_id += stride) {
        if (_v->getEnum(doc_id) != handles[doc_id]) {
            ++changed;
        }
    }
    return changed;
}

template <typename VectorType>
class CompactionTest : public CompactionTestBase
{
public:
    CompactionTest();
    void set_values(uint32_t doc_id);
    void check_values(uint32_t doc_id);
    void check_cleared_values(uint32_t doc_id);
    void test_enum_store_compaction();
    BasicType get_basic_type() const override { return TestData<VectorType>::basic_type; }
};

template <typename VectorType>
CompactionTest<VectorType>::CompactionTest()
    : CompactionTestBase()
{
}

template <typename VectorType>
void
CompactionTest<VectorType>::set_values(uint32_t doc_id)
{
    using MyTestData = TestData<VectorType>;
    auto &typed_v = dynamic_cast<VectorType &>(*_v);
    _v->clearDoc(doc_id);
    if (_v->hasMultiValue()) {
        EXPECT_TRUE(typed_v.append(doc_id, MyTestData::as_add(MyTestData::make_value(doc_id, 0)), 1));
        EXPECT_TRUE(typed_v.append(doc_id, MyTestData::as_add(MyTestData::make_value(doc_id, 1)), 1));
    } else {
        EXPECT_TRUE(typed_v.update(doc_id, MyTestData::as_add(MyTestData::make_value(doc_id, 0))));
    }
    _v->commit();
}

template <typename VectorType>
void
CompactionTest<VectorType>::check_values(uint32_t doc_id)
{
    using MyTestData = TestData<VectorType>;
    using CheckType = typename MyTestData::CheckType;
    typename MyTestData::BufferType buffer;
    buffer.fill(*_v, doc_id);
    if (_v->hasMultiValue()) {
        EXPECT_EQ(2u, buffer.size());
        EXPECT_EQ(CheckType(buffer[0]), MyTestData::make_value(doc_id, 0));
        EXPECT_EQ(CheckType(buffer[1]), MyTestData::make_value(doc_id, 1));
    } else {
        EXPECT_EQ(1u, buffer.size());
        EXPECT_EQ(CheckType(buffer[0]), MyTestData::make_value(doc_id, 0));
    }
}

template <typename VectorType>
void
CompactionTest<VectorType>::check_cleared_values(uint32_t doc_id)
{
    using MyTestData = TestData<VectorType>;
    using CheckType = typename MyTestData::CheckType;
    typename MyTestData::BufferType buffer;
    buffer.fill(*_v, doc_id);
    if (_v->hasMultiValue()) {
        EXPECT_EQ(0u, buffer.size());
    } else {
        EXPECT_EQ(1u, buffer.size());
        EXPECT_EQ(CheckType(buffer[0]), MyTestData::make_undefined_value());
    }
}

template <typename VectorType>
void
CompactionTest<VectorType>::test_enum_store_compaction()
{
    constexpr size_t DEAD_BYTES_SLACK = 0x10000u;
    constexpr uint32_t canary_stride = 256;
    uint32_t dead_limit = DEAD_BYTES_SLACK / 8;
    uint32_t doc_count = dead_limit * 3;
    if (_v->hasMultiValue() || std::is_same_v<VectorType,StringAttribute>) {
        doc_count /= 2;
    }
    std::vector<EnumHandle> enum_handles;
    add_docs(doc_count);
    enum_handles.emplace_back(_v->getEnum(0));
    uint32_t doc_id;
    for (doc_id = 1; doc_id < doc_count; ++doc_id) {
        set_values(doc_id);
        enum_handles.emplace_back(_v->getEnum(doc_id));
    }
    uint32_t last_cleared_doc_id = 0;
    for (doc_id = 1; doc_id < doc_count; doc_id += 2) {
        _v->clearDoc(doc_id);
        _v->commit(true);
        enum_handles[doc_id] = enum_handles[0];
        last_cleared_doc_id = doc_id;
        if (count_changed_enum_handles(enum_handles, canary_stride) != 0) {
            LOG(info, "Detected enum store compaction at doc_id %u", doc_id);
            break;
        }
    }
    EXPECT_LT(doc_id, doc_count);
    uint32_t changed_enum_handles = count_changed_enum_handles(enum_handles, 1);
    LOG(info, "%u enum handles changed", changed_enum_handles);
    EXPECT_LT(0u, changed_enum_handles);
    for (doc_id = 1; doc_id < doc_count; ++doc_id) {
        if ((doc_id % 2) == 0 || doc_id > last_cleared_doc_id) {
            check_values(doc_id);
        } else {
            check_cleared_values(doc_id);
        }
    }
}

using IntegerCompactionTest = CompactionTest<IntegerAttribute>;

TEST_P(IntegerCompactionTest, compact)
{
    test_enum_store_compaction();
}

INSTANTIATE_TEST_CASE_P(IntegerCompactionTestSet, IntegerCompactionTest, ::testing::Values(CollectionType::SINGLE, CollectionType::ARRAY, CollectionType::WSET));

using StringCompactionTest = CompactionTest<StringAttribute>;

TEST_P(StringCompactionTest, compact)
{
    test_enum_store_compaction();
}

INSTANTIATE_TEST_CASE_P(StringCompactionTestSet, StringCompactionTest, ::testing::Values(CollectionType::SINGLE, CollectionType::ARRAY, CollectionType::WSET));

GTEST_MAIN_RUN_ALL_TESTS()
<|endoftext|>


<commit_before>/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2017 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 *******************************************************************************/

#include "miopen/solver.hpp"
#include "miopen/env.hpp"

MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_RXS)
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_RXS_WRW)
/// \todo Detect at runtime and remove this var:
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_SRAM_EDC_DISABLED)

/// \return v rounded up (towards +inf) to the nearest multiple of m.
/// Defined for positive values only.
static inline int Ceiling(const int v, const int m)
{
    assert(m > 0 && v >= 0);
    if(v % m != 0)
    {
        return (v / m + 1) * m;
    }
    return v;
}

/// \return Value equivalent to ceil(x/y).
/// Defined for positive values only.
static inline int CeilDiv(const int x, const int y)
{
    assert(y > 0);
    return Ceiling(x, y) / y;
}

/// \return Value equivalent to floor(x/y).
/// Defined for positive values only.
static inline int FloorDiv(const int x, const int y)
{
    assert(x >= 0 && y > 0);
    return x / y;
}

static inline bool IsShaderContraintsMet(const int R,
                                         const int S,
                                         const int R_stride,
                                         const int S_stride,
                                         const int C,
                                         const int K,
                                         const int H,
                                         const int W,
                                         const int OH,
                                         const int OW,
                                         const int N,
                                         const miopen::ConvolutionContext& params,
                                         const bool fp16,
                                         const unsigned filter_tile_size)
{
    static const unsigned TILE    = filter_tile_size;
    static const unsigned TILE_X2 = TILE * 2;
    // Calculate padded filter size first.
    // If stride = 1: if S <= 3 it is padded to 3,
    // otherwise S is padded to smallest 6*n for some integer n
    // If stride = 2: S is always padded to smallest 6*n for some integer n
    int padded_S = 0;
    if(S_stride == 1)
    {
        if(S <= TILE)
        {
            padded_S = TILE;
        }
        else
        {
            padded_S = Ceiling(S, TILE_X2);
        }
    }
    else
    {
        padded_S = Ceiling(S, TILE_X2);
    }
    // If stride = 1: R is always padded to smallest 3*m for some integer m
    // If stride = 2: if R % 6 ==1 then R is padded to smallest 3*m for some
    // integer m, otherwise R is padded to smallest 6*m for some integer m
    int padded_R = 0;
    if(R_stride == 1)
    {
        padded_R = Ceiling(R, TILE);
    }
    else
    {
        if(R % TILE_X2 == 1)
        {
            padded_R = Ceiling(R, TILE);
        }
        else
        {
            padded_R = Ceiling(R, TILE_X2);
        }
    }
    // Check C restrictions:
    // For FP16, all C restrictions shall be multipled by 2.
    // This implicitly introduces restriction that C must be even.
    if(fp16 && C % 2 != 0)
    {
        return false;
    }
    // If stride == 1 and S <= 3 then C needs to be even, otherwise not
    if(S_stride == 1 && S <= TILE && C % (fp16 ? 4 : 2) != 0)
    {
        return false;
    }
    const bool is_dilated_stride_2 = (params.direction.IsBackwardData() && S_stride != 1);
    if(fp16)
    {
        if(is_dilated_stride_2)
        {
            if(C % 4 != 0)
                return false;
            // In dilation mode with stride== 2 the following should be satisfied:
            // C * (ceil(R/6) + floor((R+4)/6)) * ceil(S/6) >= 18*2 (fp16)
            const int k = CeilDiv(R, TILE_X2) + FloorDiv((R + TILE + 1), TILE_X2);
            const int l = CeilDiv(S, TILE_X2);
            if(C * k * l < 18 * 2)
                return false;
        }
        if(padded_R * padded_S * C < TILE * TILE * 18 * 2)
            return false;
    }
    else
    {
        // 9_0_14 readme: Additional limitations in the dilated case are R> 1 and  C %2==0
        if(is_dilated_stride_2)
        {
            if(!(R > 1))
                return false;
            if(!(C % 2 == 0))
                return false;
        }
        // If the padded_R x padded_S filter size from above is 3*k x 3*l
        // or (special case for dilated with stride 2) 3*k x 6*l, then
        // it should be that k*l*C  >=18
        assert(padded_R % TILE == 0 && padded_S % (is_dilated_stride_2 ? TILE_X2 : TILE) == 0);
        const int k = padded_R / TILE;
        const int l = padded_S / (is_dilated_stride_2 ? TILE_X2 : TILE);
        if(k * l * C < 18)
            return false;
    }
    // Padding for bwd data shall not be negative.
    if(params.direction.IsBackwardData() || params.direction.IsBackwardWrW())
    {
        if(!(0 <= params.GetBackwardPadW() && params.GetBackwardPadW() < std::pow(2, 16)))
            return false;
        if(!(0 <= params.GetBackwardPadH() && params.GetBackwardPadH() < std::pow(2, 16)))
            return false;
    }
    const auto grid_workgroup_count_x = params.GetStream().GetMaxComputeUnits();
    assert(params.weights_layout.length() == 0);
    // clang-format off
    // Check implementation limits.
    return N < std::pow(2, 16)
        && C < std::pow(2, 16)
        && K < std::pow(2, 16)
        && H < std::pow(2, 16)
        && W < std::pow(2, 16)
        && OH < std::pow(2, 16)
        && OW < std::pow(2, 16)
        && params.pad_w < std::pow(2, 16)
        && params.pad_h < std::pow(2, 16)
        && S < std::pow(2, 16)
        && R < std::pow(2, 16)
        && grid_workgroup_count_x < std::pow(2, 16)
        && (C * H * W) <= std::pow(2, 28)
        && (K * OH * OW) <= std::pow(2, 28)
        && (K * R * S) <= std::pow(2, 28)
        && (C * R * S) <= std::pow(2, 28);
    // clang-format on
}

namespace miopen {
namespace solver {

bool ConvBinWinogradRxS::IsApplicable(const ConvolutionContext& params) const
{
    if(!(params.IsFp32() || params.IsFp16()))
        return false;
    if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_RXS{}))
        return false;
    if(params.direction.IsBackwardWrW())
    {
        if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_RXS_WRW{}))
            return false;
        if(!(params.IsFp32() && params.kernel_stride_w == 1 && params.kernel_stride_h == 1))
            return false; // WrW is only for fp32 and no stride for now.
    }

    const bool fp16 = params.IsFp16();
    if(fp16 || params.direction.IsBackwardWrW())
    { // These are supplied in asm source format.
        if(!params.use_asm_kernels)
            return false;
    }
    else
    { // fp32 kernels are in binary format.
        if(!params.use_binaries)
            return false;
    }

    const auto name = params.GetStream().GetDeviceName();
    // clang-format off
    if (fp16 || params.direction.IsBackwardWrW())
    {
        if (! ((name == "gfx906" || name == "gfx900") && params.rmv == rocm_meta_version::AMDHSA_1_0))
            return false;
    }
    else
    {
        if (! ((name == "gfx803" && (params.rmv == rocm_meta_version::V3 || params.rmv == rocm_meta_version::AMDHSA_1_0))
            || (name == "gfx900" && (params.rmv == rocm_meta_version::V3 || params.rmv == rocm_meta_version::AMDHSA_1_0))
            || (name == "gfx906" && params.rmv == rocm_meta_version::AMDHSA_1_0)))
            return false;
    }

    if (! (params.kernel_stride_w <= 2 // -u inp_u 1 or 2
        && params.kernel_stride_w == params.kernel_stride_h
        && params.kernel_dilation_w == 1
        && params.kernel_dilation_h == 1
        && params.bias == 0
        && params.group_counts == 1
        && params.in_layout == "NCHW"))
        return false;
    // clang-format on

    if(params.direction.IsBackwardWrW())
    {
        return IsShaderContraintsMet(params.in_height,
                                     params.in_width,
                                     params.kernel_dilation_h,
                                     params.kernel_dilation_w,
                                     params.batch_sz, // N
                                     params.n_inputs, // K
                                     params.out_height,
                                     params.out_width,
                                     params.kernel_size_h,
                                     params.kernel_size_w,
                                     params.n_outputs, // C
                                     params,
                                     fp16,
                                     2);
    }
    else
    {
        return IsShaderContraintsMet(params.kernel_size_h, // RxS
                                     params.kernel_size_w,
                                     params.kernel_stride_h,
                                     params.kernel_stride_w,
                                     params.n_inputs,  // C
                                     params.n_outputs, // K
                                     params.in_height, // HxW
                                     params.in_width,
                                     params.out_height, // OHxOW
                                     params.out_width,
                                     params.batch_sz, // N
                                     params,
                                     fp16,
                                     3);
    }
}

ConvSolution ConvBinWinogradRxS::GetSolution(const ConvolutionContext& params) const
{
    ConvSolution result;
    const auto n_groups = params.GetStream().GetMaxComputeUnits();
    const auto name     = params.GetStream().GetDeviceName();
    KernelInfo kernel;

    kernel.g_wk.push_back(512 * n_groups);
    kernel.g_wk.push_back(1);
    kernel.g_wk.push_back(1);

    kernel.l_wk.push_back(512);
    kernel.l_wk.push_back(1);
    kernel.l_wk.push_back(1);

    if(params.IsFp16())
    {
        kernel.kernel_name = "sp3AsmConvRxSU";
        kernel.kernel_file = "Conv_Winograd_";
        if(miopen::IsEnabled(MIOPEN_DEBUG_SRAM_EDC_DISABLED{}))
            kernel.kernel_file += "v13_3_12";
        else
            kernel.kernel_file += "v14_3_3";
        kernel.kernel_file += "_fp16dot_stride";

        if(params.kernel_stride_w == 2)
        {
            if(params.direction.IsForward())
                kernel.kernel_file += "2_dec";
            else
                kernel.kernel_file += "2_dil";
        }
        else
        {
            kernel.kernel_file += "1";
        }
        kernel.kernel_file += ".s";
    }
    else if(params.direction.IsBackwardWrW())
    {
        kernel.kernel_name = "sp3AsmConvRxSU";
        kernel.kernel_file = "Conv_Winograd_v16_3_0_stride1.s";
    }
    else
    {
        kernel.kernel_name = "sp3AsmConvRxSU";
        kernel.kernel_file = "conv_3x3_wheel_alpha_v9_0_15";

        if(params.kernel_stride_w == 2)
        {
            if(params.direction.IsForward())
                kernel.kernel_file += "_stride_2_dec";
            else
                kernel.kernel_file += "_stride_2_dil";
        }

        kernel.kernel_file += ("_" + name);

        if(params.rmv == rocm_meta_version::V3)
            kernel.kernel_file += "_m30";
        else if(params.rmv == rocm_meta_version::AMDHSA_1_0)
            kernel.kernel_file += "_md10";
        else
            MIOPEN_THROW("ConvBinWinogradRxS: Unsupported metadata version.");

        kernel.kernel_file += ".so";
    }
    result.construction_params.push_back(kernel);
    return result;
}
} // namespace solver
} // namespace miopen
<commit_msg>Fix #1543 (disable fp16 Winograd for gfx900) (#1561)<commit_after>/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2017 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 *******************************************************************************/

#include "miopen/solver.hpp"
#include "miopen/env.hpp"

MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_RXS)
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_AMD_WINOGRAD_RXS_WRW)
/// \todo Detect at runtime and remove this var:
MIOPEN_DECLARE_ENV_VAR(MIOPEN_DEBUG_SRAM_EDC_DISABLED)

/// \return v rounded up (towards +inf) to the nearest multiple of m.
/// Defined for positive values only.
static inline int Ceiling(const int v, const int m)
{
    assert(m > 0 && v >= 0);
    if(v % m != 0)
    {
        return (v / m + 1) * m;
    }
    return v;
}

/// \return Value equivalent to ceil(x/y).
/// Defined for positive values only.
static inline int CeilDiv(const int x, const int y)
{
    assert(y > 0);
    return Ceiling(x, y) / y;
}

/// \return Value equivalent to floor(x/y).
/// Defined for positive values only.
static inline int FloorDiv(const int x, const int y)
{
    assert(x >= 0 && y > 0);
    return x / y;
}

static inline bool IsShaderContraintsMet(const int R,
                                         const int S,
                                         const int R_stride,
                                         const int S_stride,
                                         const int C,
                                         const int K,
                                         const int H,
                                         const int W,
                                         const int OH,
                                         const int OW,
                                         const int N,
                                         const miopen::ConvolutionContext& params,
                                         const bool fp16,
                                         const unsigned filter_tile_size)
{
    static const unsigned TILE    = filter_tile_size;
    static const unsigned TILE_X2 = TILE * 2;
    // Calculate padded filter size first.
    // If stride = 1: if S <= 3 it is padded to 3,
    // otherwise S is padded to smallest 6*n for some integer n
    // If stride = 2: S is always padded to smallest 6*n for some integer n
    int padded_S = 0;
    if(S_stride == 1)
    {
        if(S <= TILE)
        {
            padded_S = TILE;
        }
        else
        {
            padded_S = Ceiling(S, TILE_X2);
        }
    }
    else
    {
        padded_S = Ceiling(S, TILE_X2);
    }
    // If stride = 1: R is always padded to smallest 3*m for some integer m
    // If stride = 2: if R % 6 ==1 then R is padded to smallest 3*m for some
    // integer m, otherwise R is padded to smallest 6*m for some integer m
    int padded_R = 0;
    if(R_stride == 1)
    {
        padded_R = Ceiling(R, TILE);
    }
    else
    {
        if(R % TILE_X2 == 1)
        {
            padded_R = Ceiling(R, TILE);
        }
        else
        {
            padded_R = Ceiling(R, TILE_X2);
        }
    }
    // Check C restrictions:
    // For FP16, all C restrictions shall be multipled by 2.
    // This implicitly introduces restriction that C must be even.
    if(fp16 && C % 2 != 0)
    {
        return false;
    }
    // If stride == 1 and S <= 3 then C needs to be even, otherwise not
    if(S_stride == 1 && S <= TILE && C % (fp16 ? 4 : 2) != 0)
    {
        return false;
    }
    const bool is_dilated_stride_2 = (params.direction.IsBackwardData() && S_stride != 1);
    if(fp16)
    {
        if(is_dilated_stride_2)
        {
            if(C % 4 != 0)
                return false;
            // In dilation mode with stride== 2 the following should be satisfied:
            // C * (ceil(R/6) + floor((R+4)/6)) * ceil(S/6) >= 18*2 (fp16)
            const int k = CeilDiv(R, TILE_X2) + FloorDiv((R + TILE + 1), TILE_X2);
            const int l = CeilDiv(S, TILE_X2);
            if(C * k * l < 18 * 2)
                return false;
        }
        if(padded_R * padded_S * C < TILE * TILE * 18 * 2)
            return false;
    }
    else
    {
        // 9_0_14 readme: Additional limitations in the dilated case are R> 1 and  C %2==0
        if(is_dilated_stride_2)
        {
            if(!(R > 1))
                return false;
            if(!(C % 2 == 0))
                return false;
        }
        // If the padded_R x padded_S filter size from above is 3*k x 3*l
        // or (special case for dilated with stride 2) 3*k x 6*l, then
        // it should be that k*l*C  >=18
        assert(padded_R % TILE == 0 && padded_S % (is_dilated_stride_2 ? TILE_X2 : TILE) == 0);
        const int k = padded_R / TILE;
        const int l = padded_S / (is_dilated_stride_2 ? TILE_X2 : TILE);
        if(k * l * C < 18)
            return false;
    }
    // Padding for bwd data shall not be negative.
    if(params.direction.IsBackwardData() || params.direction.IsBackwardWrW())
    {
        if(!(0 <= params.GetBackwardPadW() && params.GetBackwardPadW() < std::pow(2, 16)))
            return false;
        if(!(0 <= params.GetBackwardPadH() && params.GetBackwardPadH() < std::pow(2, 16)))
            return false;
    }
    const auto grid_workgroup_count_x = params.GetStream().GetMaxComputeUnits();
    assert(params.weights_layout.length() == 0);
    // clang-format off
    // Check implementation limits.
    return N < std::pow(2, 16)
        && C < std::pow(2, 16)
        && K < std::pow(2, 16)
        && H < std::pow(2, 16)
        && W < std::pow(2, 16)
        && OH < std::pow(2, 16)
        && OW < std::pow(2, 16)
        && params.pad_w < std::pow(2, 16)
        && params.pad_h < std::pow(2, 16)
        && S < std::pow(2, 16)
        && R < std::pow(2, 16)
        && grid_workgroup_count_x < std::pow(2, 16)
        && (C * H * W) <= std::pow(2, 28)
        && (K * OH * OW) <= std::pow(2, 28)
        && (K * R * S) <= std::pow(2, 28)
        && (C * R * S) <= std::pow(2, 28);
    // clang-format on
}

namespace miopen {
namespace solver {

bool ConvBinWinogradRxS::IsApplicable(const ConvolutionContext& params) const
{
    if(!(params.IsFp32() || params.IsFp16()))
        return false;
    if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_RXS{}))
        return false;
    if(params.direction.IsBackwardWrW())
    {
        if(miopen::IsDisabled(MIOPEN_DEBUG_AMD_WINOGRAD_RXS_WRW{}))
            return false;
        if(!(params.IsFp32() && params.kernel_stride_w == 1 && params.kernel_stride_h == 1))
            return false; // WrW is only for fp32 and no stride for now.
    }

    const bool fp16 = params.IsFp16();
    if(fp16 || params.direction.IsBackwardWrW())
    { // These are supplied in asm source format.
        if(!params.use_asm_kernels)
            return false;
    }
    else
    { // fp32 Fwd/Bwd tile=3 kernels are in binary format.
        if(!params.use_binaries)
            return false;
    }

    const auto name = params.GetStream().GetDeviceName();
    // clang-format off
    if (fp16)
    {
        if (! (name == "gfx906" && params.rmv == rocm_meta_version::AMDHSA_1_0))
            return false;
    }
    else
    {
        if (params.direction.IsBackwardWrW())
        {
            if (! ((name == "gfx900" && params.rmv == rocm_meta_version::AMDHSA_1_0)
                || (name == "gfx906" && params.rmv == rocm_meta_version::AMDHSA_1_0)))
                return false;
        }
        else
        {
            if (! ((name == "gfx803" && (params.rmv == rocm_meta_version::V3 || params.rmv == rocm_meta_version::AMDHSA_1_0))
                || (name == "gfx900" && (params.rmv == rocm_meta_version::V3 || params.rmv == rocm_meta_version::AMDHSA_1_0))
                || (name == "gfx906" && params.rmv == rocm_meta_version::AMDHSA_1_0)))
                return false;
        }
    }

    if (! (params.kernel_stride_w <= 2 // -u inp_u 1 or 2
        && params.kernel_stride_w == params.kernel_stride_h
        && params.kernel_dilation_w == 1
        && params.kernel_dilation_h == 1
        && params.bias == 0
        && params.group_counts == 1
        && params.in_layout == "NCHW"))
        return false;
    // clang-format on

    if(params.direction.IsBackwardWrW())
    {
        return IsShaderContraintsMet(params.in_height,
                                     params.in_width,
                                     params.kernel_dilation_h,
                                     params.kernel_dilation_w,
                                     params.batch_sz, // N
                                     params.n_inputs, // K
                                     params.out_height,
                                     params.out_width,
                                     params.kernel_size_h,
                                     params.kernel_size_w,
                                     params.n_outputs, // C
                                     params,
                                     fp16,
                                     2);
    }
    else
    {
        return IsShaderContraintsMet(params.kernel_size_h, // RxS
                                     params.kernel_size_w,
                                     params.kernel_stride_h,
                                     params.kernel_stride_w,
                                     params.n_inputs,  // C
                                     params.n_outputs, // K
                                     params.in_height, // HxW
                                     params.in_width,
                                     params.out_height, // OHxOW
                                     params.out_width,
                                     params.batch_sz, // N
                                     params,
                                     fp16,
                                     3);
    }
}

ConvSolution ConvBinWinogradRxS::GetSolution(const ConvolutionContext& params) const
{
    ConvSolution result;
    const auto n_groups = params.GetStream().GetMaxComputeUnits();
    const auto name     = params.GetStream().GetDeviceName();
    KernelInfo kernel;

    kernel.g_wk.push_back(512 * n_groups);
    kernel.g_wk.push_back(1);
    kernel.g_wk.push_back(1);

    kernel.l_wk.push_back(512);
    kernel.l_wk.push_back(1);
    kernel.l_wk.push_back(1);

    if(params.IsFp16())
    {
        kernel.kernel_name = "sp3AsmConvRxSU";
        kernel.kernel_file = "Conv_Winograd_";
        if(miopen::IsEnabled(MIOPEN_DEBUG_SRAM_EDC_DISABLED{}))
            kernel.kernel_file += "v13_3_12";
        else
            kernel.kernel_file += "v14_3_3";
        kernel.kernel_file += "_fp16dot_stride";

        if(params.kernel_stride_w == 2)
        {
            if(params.direction.IsForward())
                kernel.kernel_file += "2_dec";
            else
                kernel.kernel_file += "2_dil";
        }
        else
        {
            kernel.kernel_file += "1";
        }
        kernel.kernel_file += ".s";
    }
    else if(params.direction.IsBackwardWrW())
    {
        kernel.kernel_name = "sp3AsmConvRxSU";
        kernel.kernel_file = "Conv_Winograd_v16_3_0_stride1.s";
    }
    else
    {
        kernel.kernel_name = "sp3AsmConvRxSU";
        kernel.kernel_file = "conv_3x3_wheel_alpha_v9_0_15";

        if(params.kernel_stride_w == 2)
        {
            if(params.direction.IsForward())
                kernel.kernel_file += "_stride_2_dec";
            else
                kernel.kernel_file += "_stride_2_dil";
        }

        kernel.kernel_file += ("_" + name);

        if(params.rmv == rocm_meta_version::V3)
            kernel.kernel_file += "_m30";
        else if(params.rmv == rocm_meta_version::AMDHSA_1_0)
            kernel.kernel_file += "_md10";
        else
            MIOPEN_THROW("ConvBinWinogradRxS: Unsupported metadata version.");

        kernel.kernel_file += ".so";
    }
    result.construction_params.push_back(kernel);
    return result;
}
} // namespace solver
} // namespace miopen
<|endoftext|>


<commit_before>#ifndef STAN_IO_STAN_CSV_READER_HPP
#define STAN_IO_STAN_CSV_READER_HPP

#include <boost/algorithm/string.hpp>
#include <stan/math/prim.hpp>
#include <istream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <ctype.h>

namespace stan {
namespace io {

// FIXME: should consolidate with the options from
// the command line in stan::lang
struct stan_csv_metadata {
  int stan_version_major;
  int stan_version_minor;
  int stan_version_patch;

  std::string model;
  std::string data;
  std::string init;
  size_t chain_id;
  size_t seed;
  bool random_seed;
  size_t num_samples;
  size_t num_warmup;
  bool save_warmup;
  size_t thin;
  bool append_samples;
  std::string algorithm;
  std::string engine;
  int max_depth;

  stan_csv_metadata()
      : stan_version_major(0),
        stan_version_minor(0),
        stan_version_patch(0),
        model(""),
        data(""),
        init(""),
        chain_id(1),
        seed(0),
        random_seed(false),
        num_samples(0),
        num_warmup(0),
        save_warmup(false),
        thin(0),
        append_samples(false),
        algorithm(""),
        engine(""),
        max_depth(10) {}
};

struct stan_csv_adaptation {
  double step_size;
  Eigen::MatrixXd metric;

  stan_csv_adaptation() : step_size(0), metric(0, 0) {}
};

struct stan_csv_timing {
  double warmup;
  double sampling;

  stan_csv_timing() : warmup(0), sampling(0) {}
};

struct stan_csv {
  stan_csv_metadata metadata;
  std::vector<std::string> header;
  stan_csv_adaptation adaptation;
  Eigen::MatrixXd samples;
  stan_csv_timing timing;
};

/**
 * Reads from a Stan output csv file.
 */
class stan_csv_reader {
 public:
  stan_csv_reader() {}
  ~stan_csv_reader() {}

  static bool read_metadata(std::istream& in, stan_csv_metadata& metadata,
                            std::ostream* out) {
    std::stringstream ss;
    std::string line;

    if (in.peek() != '#')
      return false;
    while (in.peek() == '#') {
      std::getline(in, line);
      ss << line << '\n';
    }
    ss.seekg(std::ios_base::beg);

    char comment;
    std::string lhs;

    std::string name;
    std::string value;

    while (ss.good()) {
      ss >> comment;
      std::getline(ss, lhs);

      size_t equal = lhs.find("=");
      if (equal != std::string::npos) {
        name = lhs.substr(0, equal);
        boost::trim(name);
        value = lhs.substr(equal + 1, lhs.size());
        boost::trim(value);
        boost::replace_first(value, " (Default)", "");
      } else {
        if (lhs.compare(" data") == 0) {
          ss >> comment;
          std::getline(ss, lhs);

          size_t equal = lhs.find("=");
          if (equal != std::string::npos) {
            name = lhs.substr(0, equal);
            boost::trim(name);
            value = lhs.substr(equal + 2, lhs.size());
            boost::replace_first(value, " (Default)", "");
          }

          if (name.compare("file") == 0)
            metadata.data = value;

          continue;
        }
      }

      if (name.compare("stan_version_major") == 0) {
        std::stringstream(value) >> metadata.stan_version_major;
      } else if (name.compare("stan_version_minor") == 0) {
        std::stringstream(value) >> metadata.stan_version_minor;
      } else if (name.compare("stan_version_patch") == 0) {
        std::stringstream(value) >> metadata.stan_version_patch;
      } else if (name.compare("model") == 0) {
        metadata.model = value;
      } else if (name.compare("num_samples") == 0) {
        std::stringstream(value) >> metadata.num_samples;
      } else if (name.compare("num_warmup") == 0) {
        std::stringstream(value) >> metadata.num_warmup;
      } else if (name.compare("save_warmup") == 0) {
        std::stringstream(value) >> metadata.save_warmup;
      } else if (name.compare("thin") == 0) {
        std::stringstream(value) >> metadata.thin;
      } else if (name.compare("id") == 0) {
        std::stringstream(value) >> metadata.chain_id;
      } else if (name.compare("init") == 0) {
        metadata.init = value;
        boost::trim(metadata.init);
      } else if (name.compare("seed") == 0) {
        std::stringstream(value) >> metadata.seed;
        metadata.random_seed = false;
      } else if (name.compare("append_samples") == 0) {
        std::stringstream(value) >> metadata.append_samples;
      } else if (name.compare("algorithm") == 0) {
        metadata.algorithm = value;
      } else if (name.compare("engine") == 0) {
        metadata.engine = value;
      } else if (name.compare("max_depth") == 0) {
        std::stringstream(value) >> metadata.max_depth;
      }
    }
    if (ss.good() == true)
      return false;

    return true;
  }  // read_metadata

  static bool read_header(std::istream& in, std::vector<std::string>& header,
                          std::ostream* out, bool prettify_name = true) {
    std::string line;

    if (!isalpha(in.peek()))
      return false;

    std::getline(in, line);
    std::stringstream ss(line);

    header.resize(std::count(line.begin(), line.end(), ',') + 1);
    int idx = 0;
    while (ss.good()) {
      std::string token;
      std::getline(ss, token, ',');
      boost::trim(token);

      int pos = token.find('.');
      if (pos > 0 && prettify_name) {
        token.replace(pos, 1, "[");
        std::replace(token.begin(), token.end(), '.', ',');
        token += "]";
      }
      header[idx++] = token;
    }
    return true;
  }

  static bool read_adaptation(std::istream& in, stan_csv_adaptation& adaptation,
                              std::ostream* out) {
    std::stringstream ss;
    std::string line;
    int lines = 0;

    if (in.peek() != '#' || in.good() == false)
      return false;

    while (in.peek() == '#') {
      std::getline(in, line);
      ss << line << std::endl;
      lines++;
    }
    ss.seekg(std::ios_base::beg);

    if (lines < 4)
      return false;

    char comment;  // Buffer for comment indicator, #

    // Skip first two lines
    std::getline(ss, line);

    // Stepsize
    std::getline(ss, line, '=');
    boost::trim(line);
    ss >> adaptation.step_size;

    // Metric parameters
    std::getline(ss, line);
    std::getline(ss, line);
    std::getline(ss, line);

    int rows = lines - 3;
    int cols = std::count(line.begin(), line.end(), ',') + 1;
    adaptation.metric.resize(rows, cols);

    for (int row = 0; row < rows; row++) {
      std::stringstream line_ss;
      line_ss.str(line);
      line_ss >> comment;

      for (int col = 0; col < cols; col++) {
        std::string token;
        std::getline(line_ss, token, ',');
        boost::trim(token);
        std::stringstream(token) >> adaptation.metric(row, col);
      }
      std::getline(ss, line);  // Read in next line
    }

    if (ss.good())
      return false;
    else
      return true;
  }

  static bool read_samples(std::istream& in, Eigen::MatrixXd& samples,
                           stan_csv_timing& timing, std::ostream* out) {
    std::stringstream ss;
    std::string line;

    int rows = 0;
    int cols = -1;

    if (in.peek() == '#' || in.good() == false)
      return false;

    while (in.good()) {
      bool comment_line = (in.peek() == '#');
      bool empty_line = (in.peek() == '\n');

      std::getline(in, line);

      if (empty_line)
        continue;
      if (!line.length())
        break;

      if (comment_line) {
        if (line.find("(Warm-up)") != std::string::npos) {
          int left = 17;
          int right = line.find(" seconds");
          double warmup;
          std::stringstream(line.substr(left, right - left)) >> warmup;
          timing.warmup += warmup;
        } else if (line.find("(Sampling)") != std::string::npos) {
          int left = 17;
          int right = line.find(" seconds");
          double sampling;
          std::stringstream(line.substr(left, right - left)) >> sampling;
          timing.sampling += sampling;
        }
      } else {
        ss << line << '\n';
        int current_cols = std::count(line.begin(), line.end(), ',') + 1;
        if (cols == -1) {
          cols = current_cols;
        } else if (cols != current_cols) {
          if (out)
            *out << "Error: expected " << cols << " columns, but found "
                 << current_cols << " instead for row " << rows + 1
                 << std::endl;
          return false;
        }
        rows++;
      }

      in.peek();
    }

    ss.seekg(std::ios_base::beg);

    if (rows > 0) {
      samples.resize(rows, cols);
      for (int row = 0; row < rows; row++) {
        std::getline(ss, line);
        std::stringstream ls(line);
        for (int col = 0; col < cols; col++) {
          std::getline(ls, line, ',');
          boost::trim(line);
          std::stringstream(line) >> samples(row, col);
        }
      }
    }
    return true;
  }

  /**
   * Parses the file.
   *
   * @param[in] in input stream to parse
   * @param[out] out output stream to send messages
   */
  static stan_csv parse(std::istream& in, std::ostream* out) {
    stan_csv data;

    if (!read_metadata(in, data.metadata, out)) {
      if (out)
        *out << "Warning: non-fatal error reading metadata" << std::endl;
    }

    if (!read_header(in, data.header, out)) {
      if (out)
        *out << "Error: error reading header" << std::endl;
      throw std::invalid_argument("Error with header of input file in parse");
    }

    if (!read_adaptation(in, data.adaptation, out)) {
      if (out)
        *out << "Warning: non-fatal error reading adapation data" << std::endl;
    }

    data.timing.warmup = 0;
    data.timing.sampling = 0;

    if (!read_samples(in, data.samples, data.timing, out)) {
      if (out)
        *out << "Warning: non-fatal error reading samples" << std::endl;
    }

    return data;
  }
};

}  // namespace io

}  // namespace stan

#endif
<commit_msg>changes per code review<commit_after>#ifndef STAN_IO_STAN_CSV_READER_HPP
#define STAN_IO_STAN_CSV_READER_HPP

#include <boost/algorithm/string.hpp>
#include <stan/math/prim.hpp>
#include <istream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <ctype>

namespace stan {
namespace io {

// FIXME: should consolidate with the options from
// the command line in stan::lang
struct stan_csv_metadata {
  int stan_version_major;
  int stan_version_minor;
  int stan_version_patch;

  std::string model;
  std::string data;
  std::string init;
  size_t chain_id;
  size_t seed;
  bool random_seed;
  size_t num_samples;
  size_t num_warmup;
  bool save_warmup;
  size_t thin;
  bool append_samples;
  std::string algorithm;
  std::string engine;
  int max_depth;

  stan_csv_metadata()
      : stan_version_major(0),
        stan_version_minor(0),
        stan_version_patch(0),
        model(""),
        data(""),
        init(""),
        chain_id(1),
        seed(0),
        random_seed(false),
        num_samples(0),
        num_warmup(0),
        save_warmup(false),
        thin(0),
        append_samples(false),
        algorithm(""),
        engine(""),
        max_depth(10) {}
};

struct stan_csv_adaptation {
  double step_size;
  Eigen::MatrixXd metric;

  stan_csv_adaptation() : step_size(0), metric(0, 0) {}
};

struct stan_csv_timing {
  double warmup;
  double sampling;

  stan_csv_timing() : warmup(0), sampling(0) {}
};

struct stan_csv {
  stan_csv_metadata metadata;
  std::vector<std::string> header;
  stan_csv_adaptation adaptation;
  Eigen::MatrixXd samples;
  stan_csv_timing timing;
};

/**
 * Reads from a Stan output csv file.
 */
class stan_csv_reader {
 public:
  stan_csv_reader() {}
  ~stan_csv_reader() {}

  static bool read_metadata(std::istream& in, stan_csv_metadata& metadata,
                            std::ostream* out) {
    std::stringstream ss;
    std::string line;

    if (in.peek() != '#')
      return false;
    while (in.peek() == '#') {
      std::getline(in, line);
      ss << line << '\n';
    }
    ss.seekg(std::ios_base::beg);

    char comment;
    std::string lhs;

    std::string name;
    std::string value;

    while (ss.good()) {
      ss >> comment;
      std::getline(ss, lhs);

      size_t equal = lhs.find("=");
      if (equal != std::string::npos) {
        name = lhs.substr(0, equal);
        boost::trim(name);
        value = lhs.substr(equal + 1, lhs.size());
        boost::trim(value);
        boost::replace_first(value, " (Default)", "");
      } else {
        if (lhs.compare(" data") == 0) {
          ss >> comment;
          std::getline(ss, lhs);

          size_t equal = lhs.find("=");
          if (equal != std::string::npos) {
            name = lhs.substr(0, equal);
            boost::trim(name);
            value = lhs.substr(equal + 2, lhs.size());
            boost::replace_first(value, " (Default)", "");
          }

          if (name.compare("file") == 0)
            metadata.data = value;

          continue;
        }
      }

      if (name.compare("stan_version_major") == 0) {
        std::stringstream(value) >> metadata.stan_version_major;
      } else if (name.compare("stan_version_minor") == 0) {
        std::stringstream(value) >> metadata.stan_version_minor;
      } else if (name.compare("stan_version_patch") == 0) {
        std::stringstream(value) >> metadata.stan_version_patch;
      } else if (name.compare("model") == 0) {
        metadata.model = value;
      } else if (name.compare("num_samples") == 0) {
        std::stringstream(value) >> metadata.num_samples;
      } else if (name.compare("num_warmup") == 0) {
        std::stringstream(value) >> metadata.num_warmup;
      } else if (name.compare("save_warmup") == 0) {
        std::stringstream(value) >> metadata.save_warmup;
      } else if (name.compare("thin") == 0) {
        std::stringstream(value) >> metadata.thin;
      } else if (name.compare("id") == 0) {
        std::stringstream(value) >> metadata.chain_id;
      } else if (name.compare("init") == 0) {
        metadata.init = value;
        boost::trim(metadata.init);
      } else if (name.compare("seed") == 0) {
        std::stringstream(value) >> metadata.seed;
        metadata.random_seed = false;
      } else if (name.compare("append_samples") == 0) {
        std::stringstream(value) >> metadata.append_samples;
      } else if (name.compare("algorithm") == 0) {
        metadata.algorithm = value;
      } else if (name.compare("engine") == 0) {
        metadata.engine = value;
      } else if (name.compare("max_depth") == 0) {
        std::stringstream(value) >> metadata.max_depth;
      }
    }
    if (ss.good() == true)
      return false;

    return true;
  }  // read_metadata

  static bool read_header(std::istream& in, std::vector<std::string>& header,
                          std::ostream* out, bool prettify_name = true) {
    std::string line;

    if (!std::isalpha(in.peek()))
      return false;

    std::getline(in, line);
    std::stringstream ss(line);

    header.resize(std::count(line.begin(), line.end(), ',') + 1);
    int idx = 0;
    while (ss.good()) {
      std::string token;
      std::getline(ss, token, ',');
      boost::trim(token);

      int pos = token.find('.');
      if (pos > 0 && prettify_name) {
        token.replace(pos, 1, "[");
        std::replace(token.begin(), token.end(), '.', ',');
        token += "]";
      }
      header[idx++] = token;
    }
    return true;
  }

  static bool read_adaptation(std::istream& in, stan_csv_adaptation& adaptation,
                              std::ostream* out) {
    std::stringstream ss;
    std::string line;
    int lines = 0;

    if (in.peek() != '#' || in.good() == false)
      return false;

    while (in.peek() == '#') {
      std::getline(in, line);
      ss << line << std::endl;
      lines++;
    }
    ss.seekg(std::ios_base::beg);

    if (lines < 4)
      return false;

    char comment;  // Buffer for comment indicator, #

    // Skip first two lines
    std::getline(ss, line);

    // Stepsize
    std::getline(ss, line, '=');
    boost::trim(line);
    ss >> adaptation.step_size;

    // Metric parameters
    std::getline(ss, line);
    std::getline(ss, line);
    std::getline(ss, line);

    int rows = lines - 3;
    int cols = std::count(line.begin(), line.end(), ',') + 1;
    adaptation.metric.resize(rows, cols);

    for (int row = 0; row < rows; row++) {
      std::stringstream line_ss;
      line_ss.str(line);
      line_ss >> comment;

      for (int col = 0; col < cols; col++) {
        std::string token;
        std::getline(line_ss, token, ',');
        boost::trim(token);
        std::stringstream(token) >> adaptation.metric(row, col);
      }
      std::getline(ss, line);  // Read in next line
    }

    if (ss.good())
      return false;
    else
      return true;
  }

  static bool read_samples(std::istream& in, Eigen::MatrixXd& samples,
                           stan_csv_timing& timing, std::ostream* out) {
    std::stringstream ss;
    std::string line;

    int rows = 0;
    int cols = -1;

    if (in.peek() == '#' || in.good() == false)
      return false;

    while (in.good()) {
      bool comment_line = (in.peek() == '#');
      bool empty_line = (in.peek() == '\n');

      std::getline(in, line);

      if (empty_line)
        continue;
      if (!line.length())
        break;

      if (comment_line) {
        if (line.find("(Warm-up)") != std::string::npos) {
          int left = 17;
          int right = line.find(" seconds");
          double warmup;
          std::stringstream(line.substr(left, right - left)) >> warmup;
          timing.warmup += warmup;
        } else if (line.find("(Sampling)") != std::string::npos) {
          int left = 17;
          int right = line.find(" seconds");
          double sampling;
          std::stringstream(line.substr(left, right - left)) >> sampling;
          timing.sampling += sampling;
        }
      } else {
        ss << line << '\n';
        int current_cols = std::count(line.begin(), line.end(), ',') + 1;
        if (cols == -1) {
          cols = current_cols;
        } else if (cols != current_cols) {
          if (out)
            *out << "Error: expected " << cols << " columns, but found "
                 << current_cols << " instead for row " << rows + 1
                 << std::endl;
          return false;
        }
        rows++;
      }

      in.peek();
    }

    ss.seekg(std::ios_base::beg);

    if (rows > 0) {
      samples.resize(rows, cols);
      for (int row = 0; row < rows; row++) {
        std::getline(ss, line);
        std::stringstream ls(line);
        for (int col = 0; col < cols; col++) {
          std::getline(ls, line, ',');
          boost::trim(line);
          std::stringstream(line) >> samples(row, col);
        }
      }
    }
    return true;
  }

  /**
   * Parses the file.
   *
   * @param[in] in input stream to parse
   * @param[out] out output stream to send messages
   */
  static stan_csv parse(std::istream& in, std::ostream* out) {
    stan_csv data;

    if (!read_metadata(in, data.metadata, out)) {
      if (out)
        *out << "Warning: non-fatal error reading metadata" << std::endl;
    }

    if (!read_header(in, data.header, out)) {
      if (out)
        *out << "Error: error reading header" << std::endl;
      throw std::invalid_argument("Error with header of input file in parse");
    }

    if (!read_adaptation(in, data.adaptation, out)) {
      if (out)
        *out << "Warning: non-fatal error reading adapation data" << std::endl;
    }

    data.timing.warmup = 0;
    data.timing.sampling = 0;

    if (!read_samples(in, data.samples, data.timing, out)) {
      if (out)
        *out << "Warning: non-fatal error reading samples" << std::endl;
    }

    return data;
  }
};

}  // namespace io

}  // namespace stan

#endif
<|endoftext|>


<commit_before>// ConsoleRPG.cpp : Defines the entry point for the console application.
// Created by Nick Mullally a.k.a. thakyZ / Nire Inicana
//

#include "stdafx.h"
#include <iostream>
#include <cstdlib>
#include "time.h"
using namespace std;

// Roll the dice.
int diceRoll(int qty, int sides)
{
	int subTotal = 0; // Dice addition subtotal.
	// Roll the dice.

	// Loop to roll the dice
	for (int i = 0; i < qty; i++)
	{
		int currentRoll = rand() % sides + 1;
		subTotal += currentRoll;
	}

	return subTotal;
}

// Race Choices
enum RACE { HUMAN, ELF, DARKELF, ANGEL, MONGREL, SHAMANI, NIBELUNG, UNDEAD };

// Class Choices
enum OCC { FIGHTER, CLERIC, THEIF, BARD, ROUGE, TINKER, MAGE };

// Stats Tree
struct ATTRIBUTES
{
	unsigned int strength;
	unsigned int faith;
	unsigned int dexterity;
	unsigned int insperation;
	unsigned int cleverness;
	unsigned int focus;
	unsigned int hp, hpMax;
	unsigned int mp, mpMax;
};

void displayStats(ATTRIBUTES atts)
{
	cout << " +---------------------+ \n";
	cout << " |  Attributes:        | \n";
	cout << " +---------------------+ \n";
	cout << "\n";
	cout << "   Strength:      " << atts.strength << "\n";
	cout << "   Faith:         " << atts.faith << "\n";
	cout << "   Dexterity:     " << atts.dexterity << "\n";
	cout << "   Insperation:   " << atts.insperation << "\n";
	cout << "   Cleverness:    " << atts.cleverness << "\n";
	cout << "   Focus:         " << atts.focus << "\n";
	cout << "\n\n";
}

// Startup
int _tmain(int argc, _TCHAR* argv[])
{
	srand(time(NULL));

	RACE inputRace; // The race that is chosen.
	char inputs; // The inputs.
	ATTRIBUTES tempStats; // The stats the player gets by random.
	bool reroll = true; // The bool for rerolling.
	OCC inputClass; // The class that is chosen.
	bool retry = true;

	system("cls");

	cout << "Welcome to Zoro\n";

	while (reroll == true)
	{
		cout << "Please Select a Race:\n";
		cout << "[H]uman [E]lf [D]ark elf [A]ngel [M]ongrel [S]hamani [N]ibelung [U]ndead\n";

		// The input for the chosen race.
		cin >> inputs;

		while (retry == true)
		{
			retry == false;

			switch (inputs)
			{
				case 'h':
				case 'H':
					cout << "Human\n";
					inputRace = HUMAN;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(3, 6);
					retry = false;
					break;
				case 'e':
				case 'E':
					cout << "Elf\n";
					inputRace = ELF;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(2, 6);
					tempStats.focus = diceRoll(4, 6);
					break;
				case 'd':
				case 'D':
					cout << "Dark Elf\n";
					inputRace = DARKELF;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(4, 6);
					tempStats.insperation = diceRoll(2, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				case 'a':
				case 'A':
					cout << "Angel\n";
					inputRace = ANGEL;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(4, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				case 'm':
				case 'M':
					cout << "Mongrel\n";
					inputRace = MONGREL;
					tempStats.strength = diceRoll(4, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(2, 6);
					break;
				case 's':
				case 'S':
					cout << "Shamani\n";
					inputRace = SHAMANI;
					tempStats.strength = diceRoll(2, 6);
					tempStats.faith = diceRoll(4, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				case 'n':
				case 'N':
					cout << "Nibelung\n";
					inputRace = NIBELUNG;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(2, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(4, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				case 'u':
				case 'U':
					cout << "Undead\n";
					inputRace = UNDEAD;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(2, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				default:
					cout << "Please input a vaild race.\n";
					retry = true;
					break;
			}
		}

		displayStats(tempStats);

		cout << "Reroll? [Y]es [N]o\n";

		cin >> inputs;

		reroll = false;

		if (inputs == 'y' || inputs == 'Y')
		{
			reroll = true;
		}
	}

	reroll = true;

	while (reroll) // While reroll == true
	{
		reroll = false;

		cout << "Please select a class\n";
		cout << "[F]ighter [C]leric [T]heif [B]ard [R]ouge [M]age\n";

		cin >> inputs;

		switch (inputs)
		{
			case 'f':
			case 'F':
				inputClass = FIGHTER;
				cout << "Fighter\n";
				break;
			case 'c':
			case 'C':
				inputClass = CLERIC;
				cout << "Cleric\n";
				break;
			case 't':
			case 'T':
				inputClass = THEIF;
				cout << "Theif\n";
				break;
			case 'b':
			case 'B':
				inputClass = BARD;
				cout << "Bard\n";
				break;
			case 'r':
			case 'R':
				inputClass = ROUGE;
				cout << "Rouge\n";
				break;
			case 'm':
			case 'M':
				inputClass = MAGE;
				cout << "Mage\n";
				break;
			default:
				cout << "Please input a valid class.\n";
				reroll = true;
				break;
		}
	}

	return 0;
}<commit_msg>Fixed some alignment in the displayStats function<commit_after>// ConsoleRPG.cpp : Defines the entry point for the console application.
// Created by Nick Mullally a.k.a. thakyZ / Nire Inicana
//

#include "stdafx.h"
#include <iostream>
#include <cstdlib>
#include "time.h"
using namespace std;

// Roll the dice.
int diceRoll(int qty, int sides)
{
	int subTotal = 0; // Dice addition subtotal.
	// Roll the dice.

	// Loop to roll the dice
	for (int i = 0; i < qty; i++)
	{
		int currentRoll = rand() % sides + 1;
		subTotal += currentRoll;
	}

	return subTotal;
}

// Race Choices
enum RACE { HUMAN, ELF, DARKELF, ANGEL, MONGREL, SHAMANI, NIBELUNG, UNDEAD };

// Class Choices
enum OCC { FIGHTER, CLERIC, THEIF, BARD, ROUGE, TINKER, MAGE };

// Stats Tree
struct ATTRIBUTES
{
	unsigned int strength;
	unsigned int faith;
	unsigned int dexterity;
	unsigned int insperation;
	unsigned int cleverness;
	unsigned int focus;
	unsigned int hp, hpMax;
	unsigned int mp, mpMax;
};

void displayStats(ATTRIBUTES atts)
{
	cout << " +---------------------+ \n";
	cout << " | Attributes:         | \n";
	cout << " +---------------------+ \n";
	cout << "\n";
	cout << "   Strength:      " << atts.strength << "\n";
	cout << "   Faith:         " << atts.faith << "\n";
	cout << "   Dexterity:     " << atts.dexterity << "\n";
	cout << "   Insperation:   " << atts.insperation << "\n";
	cout << "   Cleverness:    " << atts.cleverness << "\n";
	cout << "   Focus:         " << atts.focus << "\n";
	cout << "\n\n";
}

// Startup
int _tmain(int argc, _TCHAR* argv[])
{
	srand(time(NULL));

	RACE inputRace; // The race that is chosen.
	char inputs; // The inputs.
	ATTRIBUTES tempStats; // The stats the player gets by random.
	bool reroll = true; // The bool for rerolling.
	OCC inputClass; // The class that is chosen.
	bool retry = true;

	system("cls");

	cout << "Welcome to Zoro\n";

	while (reroll == true)
	{
		cout << "Please Select a Race:\n";
		cout << "[H]uman [E]lf [D]ark elf [A]ngel [M]ongrel [S]hamani [N]ibelung [U]ndead\n";

		// The input for the chosen race.
		cin >> inputs;

		while (retry == true)
		{
			retry == false;

			switch (inputs)
			{
				case 'h':
				case 'H':
					cout << "Human\n";
					inputRace = HUMAN;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(3, 6);
					retry = false;
					break;
				case 'e':
				case 'E':
					cout << "Elf\n";
					inputRace = ELF;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(2, 6);
					tempStats.focus = diceRoll(4, 6);
					break;
				case 'd':
				case 'D':
					cout << "Dark Elf\n";
					inputRace = DARKELF;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(4, 6);
					tempStats.insperation = diceRoll(2, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				case 'a':
				case 'A':
					cout << "Angel\n";
					inputRace = ANGEL;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(4, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				case 'm':
				case 'M':
					cout << "Mongrel\n";
					inputRace = MONGREL;
					tempStats.strength = diceRoll(4, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(2, 6);
					break;
				case 's':
				case 'S':
					cout << "Shamani\n";
					inputRace = SHAMANI;
					tempStats.strength = diceRoll(2, 6);
					tempStats.faith = diceRoll(4, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(3, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				case 'n':
				case 'N':
					cout << "Nibelung\n";
					inputRace = NIBELUNG;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(2, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(4, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				case 'u':
				case 'U':
					cout << "Undead\n";
					inputRace = UNDEAD;
					tempStats.strength = diceRoll(3, 6);
					tempStats.faith = diceRoll(3, 6);
					tempStats.dexterity = diceRoll(3, 6);
					tempStats.insperation = diceRoll(3, 6);
					tempStats.cleverness = diceRoll(2, 6);
					tempStats.focus = diceRoll(3, 6);
					break;
				default:
					cout << "Please input a vaild race.\n";
					retry = true;
					break;
			}
		}

		displayStats(tempStats);

		cout << "Reroll? [Y]es [N]o\n";

		cin >> inputs;

		reroll = false;

		if (inputs == 'y' || inputs == 'Y')
		{
			reroll = true;
		}
	}

	reroll = true;

	while (reroll) // While reroll == true
	{
		reroll = false;

		cout << "Please select a class\n";
		cout << "[F]ighter [C]leric [T]heif [B]ard [R]ouge [M]age\n";

		cin >> inputs;

		switch (inputs)
		{
			case 'f':
			case 'F':
				inputClass = FIGHTER;
				cout << "Fighter\n";
				break;
			case 'c':
			case 'C':
				inputClass = CLERIC;
				cout << "Cleric\n";
				break;
			case 't':
			case 'T':
				inputClass = THEIF;
				cout << "Theif\n";
				break;
			case 'b':
			case 'B':
				inputClass = BARD;
				cout << "Bard\n";
				break;
			case 'r':
			case 'R':
				inputClass = ROUGE;
				cout << "Rouge\n";
				break;
			case 'm':
			case 'M':
				inputClass = MAGE;
				cout << "Mage\n";
				break;
			default:
				cout << "Please input a valid class.\n";
				reroll = true;
				break;
		}
	}

	return 0;
}<|endoftext|>