性能文章>【全网首发】心心念念的优化完成了,虽然不是很完美>

【全网首发】心心念念的优化完成了,虽然不是很完美原创

330657

你好,我是雨乐!

核心模块有个功能点,一直以来想着将其优化掉(虽然在线上稳定运行了这么多年),要么没时间,要么懒的搞,一拖再拖。期间也想了各种方案,无奈不是很完美,恰好吴老师进群了,随向有着20多年经验的吴老师进行了请教,也跟A总,E总等进行了讨论,慢慢的也有了优化思路,于是用了大概一天的时间,基于这几个大佬的方案,进行了优化。

需求

项目中有这样一个需求,根据一个类别名以及其对应的类型,创建对应的数据结构。需求很简单吧。。。

优化前的版本,先创建一个配置,然后程序启动的时候,加载跟配置,然后根据配置内容进行相应的操作。

配置如下:

config.json

{
"phone_brand":"string",
"gender":"int"
}

解析如下:

// parse config.json
if (pare("config.json").failed()) {
  return;
}
for (const auto & item : parse.elements()) {
  auto name = item.first();
  auto type = item.second();
  if (type == "string") {
    mp[name] = std::make_shared<Session<std::string>>(name);
  } else if (type == "int") {
    mp[name] = std::make_shared<Session<int>>(name);
  }
}

其实,说实话,如果没有洁癖的话,这段代码也不是不可行😁。也线上稳定运行了几年,不过,多少感觉这种方式很傻瓜,就像要求写一个算法,而自己实现了一个冒泡一样,虽然功能满足,但方案并不优雅。

方案一: typelist

既然需求是根据字符串类型来创建对应的数据类型,那么不妨把各种数据类型结合起来,而支持多种数据类型的,对于这种多类型的,第一时间想到了std::variant和std::tuple,不过因为std::variant使用上的限制以及实现本功能的话需要增加很多判断代码,所以最终选择了std::tuple来实现:

using types = std::tuple<int, double, std::string, int64_t>;

template<std::size_t N>
using StrType = typename std::tuple_element<N, types>::type;

constexpr bool strings_equal(const char* a, const char* b) {
    return *a == *b && (*a == '\0' || strings_equal(a + 1, b + 1));
}

constexpr std::size_t getIdx(const char* name) 
{
    return strings_equal(name, "int") ? 0:
           strings_equal(name, "double") ? 1:
           strings_equal(name, "std::string") ? 2:
           strings_equal(name, "int64_t") ? 3 : 4;
}

int main() {
    if (pare("config.json").failed()) {
      return;
    }
    for (const auto & item : parse.elements()) {
      auto name = item.first();
      auto type = item.second();
      mp[name] = std::make_shared<Session<StrType<getIdx(type)>>>(name);
    }
}

在上述中,依然采取配置文件的方式,创建了一个支持int、string等类型的std::tuple,并通过getIdx和strings_equal来获取该类型在tuple中的index,进而创建相应的类型。

其实,如果把该方案跟现有实现(第一个)相比较的话,并没有变得多优雅,反而多了很多代码。。。

方案二: reflection

其实,这种需求从概念上讲,应该是reflection,中文称为反射,众所周知C++标准委员会那帮人不食人间烟火,也一直没有将反射纳入标准。这块也在群里进行了讨论,也聊了java中反射的实现机制和其弊端。其间,吴老师也发表了相关意见,原来对于反射这块,于13年就专门成立了反射研究组,只是一直没达到能进标准的共识。具体可以参考静态反射

如果对需求进行重构的话,我的需求也比较简单,就是一个struct,里面有各种变量,需要实现一个功能,就是获取struct中的变量list以及对应的变量内容:

struct Config {
  int a;
  std::string b;
};

void fun() {
  Config cfg;
  std::vector<Filed> fileds = GetFileds(cfg);
  for (const auto & item : fileds) {
    auto name = item.name();
    auto c = std::make_shared<item::type>();
    // do other sth
  }
}

于是在gayhub上也调研了实现,没有一个特别满意的方法,因为项目中大量用到了pb,所以借助pb的反射功能来进行实现:

message Config {
int32 a;
string b;
}

Config category;
const google::protobuf::Descriptor* desc = category.GetDescriptor();
std::vector<const google::protobuf::FieldDescriptor *> vfd;

for (int i = 0; i <  desc->field_count(); ++i) {
    const google::protobuf::FieldDescriptor* fd = desc->field(i);
    const auto &category_name = fd->name();
    switch(fd->cpp_type()) {
      case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
        auto name = item.name();
        auto c = std::make_shared<std::string>();
        break;
      }
      case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
        auto name = item.name();
        auto c = std::make_shared<uint32_t>();
        break;
      }
      default:
    break;
  }
}

上面这块借助于pb也基本满足了需求,唯一不足的是需要有这个switch case,需要将pb的type转换成cpp支持的type,不过不过怎么说,也比现在线上的方式要优雅的多。

今天的文章就到这,我们下期见!

你好,我是雨乐,从业十二年有余,历经过传统行业网络研发、互联网推荐引擎研发,目前在广告行业从业8年。 目前任职某互联网公司高级技术专家一职,负责广告引擎的架构和研发。

 

点赞收藏
分类:标签:
高性能架构探索

公众号《高性能架构探索》

请先登录,查看5条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

【全网首发】聊聊C语言中的malloc申请内存的内部原理

【全网首发】聊聊C语言中的malloc申请内存的内部原理

7
5