Muduo库之StringPiece、Time

StringPiece

StringPiece.h 文件中,声明了两个类类型,一个是 StringArg,另一个是 StringPiece,前者用于在传递函数参数时同时兼容 C 风格的字符串(const char*)和 C++ 风格的字符串(std::string)。而后者则可用于构建同时兼任这两种风格字符串的实例对象。

如果函数参数使用 const std::string& 作为形参,尽管可以同时兼容两种风格的字符串,但是当传入一个很长的 char* 时,会生成一个很大的 string 对象,其开销较大。而且使用 std::string 时不可避免会带来很多不必要的拷贝,拷贝多了必然影响性能。因此在很多高性能 C++ 框架的实现中,都会使用 StringPiece 作为 std::string 类型的 wrapper,该类只持有目标字符串的指针,而避免额外的拷贝,同时也保证了兼容性。当仅仅读取字符串的值,可以使用该类型。

常见的实现如下:

因为 StringPiece 不控制字符串的声明周期,因此调用方要保证在 StringPiece 的生命周期内,其指向的字符串是始终有效的。

如果想使用 StringPiece 作为 hash 的 key,需要自定义哈希函数,可以参考 Chromium 实现。

由于 StringPiece 只持有目标指针,所以为 POD(Plain Old Data) 类型,同时拥有 trival 构造函数,因此可以定义 __type_traits 以指示 STL 采用更为高效的算法实现。

如果至少满足下面 3 条中的一条,则该类类型中含有 non-trival 函数:

  1. 显示定义了 ctor、copy、assignment、dtor;
  2. 类中具有非静态非 POD 类型的数据成员;
  3. 有基类;

例如:

// 没有显式定义 ctor、copy、assignment、dtor 所以均为 trival
class T1 {
  int a; // POD类型
};

// 没有显式定义 copy、assignment、dtor 所以均为 trival
class T2 {
  T1() {} // 显示定义 ctor,为 non-trival ctor
  int a;  // POD
  std::string b; // 非POD
};

如果某个类中都是 trival ctor/dtor/copy/assignment 函数,对这个类进行构造、析构、拷贝、赋值时可以采用最有效率的方法,即直接采用内存操作,例如 malloc()memcpy() 等以提高性能。

因此使用 __type_traits 对于 StringPiece 来说,在使用 STL 容器时,可以让其析构函数什么都不做,提高程序效率。

而在 C++ 11 中,有了 =delete=default 关键字,如果使用这两个关键字,那么该函数就为 trival 类型,编译器会为其做出进一步优化。相对于空函数体而言,则更具有优势。

对于如下测试代码:

#include <iostream>
#include <vector>
using namespace std;

class TestA {
public:
  TestA() {}
  ~TestA() {}

private:
  int a;
};

class TestB {
public:
  TestB() = default;
  ~TestB() = default;

private:
  int a;
};

#define CLASS_NUM 10000000

int main() {
  clock_t start, end;
  start = clock();
  TestA* a = new TestA[CLASS_NUM];
  delete[] a;
  end = clock();
  cout << "non-trival: " << (double)(end - start) / CLOCKS_PER_SEC << endl;

  start = clock();
  TestB* b = new TestB[CLASS_NUM];
  delete[] b;
  end = clock();
  cout << "trival: " << (double)(end - start) / CLOCKS_PER_SEC << endl;
  return 0;
}

两个类类型中都只含有一个 POD 类型 int,分别在堆上生成 1000 0000 个对象,然后进行销毁,所耗费的时间如下:

non-trival: 0.205
trival:     0.025

时间相关

Timestamp

Timestamp.h 文件中声明了时间戳相关的类类型 Timestamp。它继承自三个父类:

class Timestamp : public muduo::copyable,
                  public boost::equality_comparable<Timestamp>,
                  public boost::less_than_comparable<Timestamp>

其中,muduo::copyable 表明该类可生成拷贝构造函数和赋值构造函数,boost::equality_compareableboost::less_than_comparable 则用于快速重载比较运算符。

该类类型用于生成程序运行过程中的时间戳,例如 "2022-01-01 12:56:47:01.000000",它使用 UTC 时间来进行计算,精度为微秒。

在 Linux 下,有如下几个获取时间的函数:

  • time(2): 返回自公元1970年1月1日的UTC时间,从0时0分0秒算起到现在所经过的秒数;
  • ftime(3): 已废弃;
  • clock_gettime(2): 精度高,系统开销大;
  • gettimeofday(2): 精度达到微秒,系统开销较小,适用于网络编程

类中仅有一个成员变量 microSecondsSinceEpoch_ 用来表示自 Epoch 时间起到现在所经过的微秒数,用初始值 0 来表述无效值。

类中含有一个无参构造函数和静态函数 invalid() 可以返回一个无效的时间戳对象,用于特殊应用。

如果我们使用 time(2) 获取了自 Epoch 以来的时间(精度为秒),可以使用 fromUnixTime() 重载函数返回一个 Timestamp 对象。

通过 microSecondsSinceEpoch()secondsSinceEpoch() 函数可以分别获得自 Epoch 以来的微秒数和秒数。

函数 toString() 则用于将秒数、微秒数转换为 std::string 类型,在其实现中,使用到了 PRId64 关键字,它的目的是为了同时支持 32 位系统和 64 位系统。在 32 位系统中表示为 long long int,在 64 位系统中则表示为 long int

而函数 toFormattedString() 函数则用于将时间戳转换为人类可以理解的格式化后的字符串形式,格式化后的形式为 "yyyymmdd hh:mm:ss.zzzzzz"。

TimeZone

TimeZone 类是为了方便时区之间和时令之间的转换。便于在分布式系统中输出日志。

Date

Date.h 文件定义了类类型 Date,用于表示格里高利历,即常说的公历

类内部只有一个成员变量 julianDayNumber_,它指的是儒略日,即自公元前 4713 年 1 月 1 日到现在所经过的具体的天数。

类内部定义了结构体 YearMonthDay,用来存储年、月、日:

struct YearMonthDay {
  int year;  // [1900..2500]
  int month; // [1..12]
  int day;   // [1..31]
};
posted @ 2022-10-14 13:50  Leaos  阅读(82)  评论(0编辑  收藏  举报