mktime函数性能分析

2023-05-16

mktime函数性能分析

1月 02, 2019 in Linux环境高级编程

mktime函数性能分析

mktime是一个将break-down时间(struct tm)转化为日历时间(time_t)的转换函数。它的转换与struct tm中的 tm_wday、tm_yday无关,当进行转换时,mktime会通过struct tm的其他成员重新矫正该值。若struct tm中的成员是非法的的,mktime将会自动校正,如2018-12-32 00:00:00,矫正后为2019-01-01 00:00:00。若给定struct tm不能转换为日历时间,则mktime返回-1。—-man mktime

1. 背景

背景:最近工作中遇到一个奇怪的问题,在将原先在32位机器上编译的程序放在64位机器上重新编译之后,然后放到IDC机器运行,发现性能降了100倍左右。在经过性能分析和查阅相关资料后发现,是由于mktime使用不当导致。


   

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29


   

#include <iostream>

#include <string>

#include <sys/time.h>

#include <unistd.h>

using namespace std;

#define PrintTime(id) struct timeval __now1105##id;\

gettimeofday(&__now1105##id, 0);

#define PrintTimeDone(id) struct timeval __now21105##id; \

gettimeofday(&__now21105##id, 0); \

printf("timer_%s spend time:%d us\n",#id,(__now21105##id.tv_sec-__now1105##id.tv_sec)* 1000000 + (__now21105##id.tv_usec-__now1105##id.tv_usec));

static void get_time(const std::string& time_str)

{

struct tm temp_tm;

strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &temp_tm);

PrintTime(mktime);

time_t temp = mktime(&temp_tm);

PrintTimeDone(mktime);

}

int main()

{

for (int i = 0; i < 10; i++)

get_time("2018-12-27 00:00:00");

return 0;

}

32位机器(i686)编译运行结果:


   

timer_mktime spend time:51 us

timer_mktime spend time:4 us

timer_mktime spend time:2 us

timer_mktime spend time:3 us

timer_mktime spend time:3 us

timer_mktime spend time:3 us

timer_mktime spend time:3 us

timer_mktime spend time:2 us

timer_mktime spend time:2 us

timer_mktime spend time:2 us

64位机器(x86_64 )编译运行结果:


   

timer_mktime spend time:181 us

timer_mktime spend time:156 us

timer_mktime spend time:138 us

timer_mktime spend time:138 us

timer_mktime spend time:137 us

timer_mktime spend time:145 us

timer_mktime spend time:143 us

timer_mktime spend time:138 us

timer_mktime spend time:138 us

timer_mktime spend time:145 us

造成上述问题的原因究竟是什么呢?

2. 源码分析mktime性能

2.1 mktime源码:

mktime.c


   

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242


   

/* Convert *TP to a time_t value. */

time_t

mktime (struct tm *tp)

{

#ifdef _LIBC

/* POSIX.1 8.1.1 requires that whenever mktime() is called, the

time zone names contained in the external variable 'tzname' shall

be set as if the tzset() function had been called. */

__tzset ();

#endif

return __mktime_internal (tp, __localtime_r, &localtime_offset);

}

/* Convert *TP to a time_t value, inverting

the monotonic and mostly-unit-linear conversion function CONVERT.

Use *OFFSET to keep track of a guess at the offset of the result,

compared to what the result would be for UTC without leap seconds.

If *OFFSET's guess is correct, only one CONVERT call is needed.

This function is external because it is used also by timegm.c. */

time_t

__mktime_internal (struct tm *tp,

struct tm *(*convert) (const time_t *, struct tm *),

time_t *offset)

{

time_t t, gt, t0, t1, t2;

struct tm tm;

/* The maximum number of probes (calls to CONVERT) should be enough

to handle any combinations of time zone rule changes, solar time,

leap seconds, and oscillations around a spring-forward gap.

POSIX.1 prohibits leap seconds, but some hosts have them anyway. */

int remaining_probes = 6;

/* Time requested. Copy it in case CONVERT modifies *TP; this can

occur if TP is localtime's returned value and CONVERT is localtime. */

int sec = tp->tm_sec;

int min = tp->tm_min;

int hour = tp->tm_hour;

int mday = tp->tm_mday;

int mon = tp->tm_mon;

int year_requested = tp->tm_year;

int isdst = tp->tm_isdst;

/* 1 if the previous probe was DST. */

int dst2;

/* Ensure that mon is in range, and set year accordingly. */

int mon_remainder = mon % 12;

int negative_mon_remainder = mon_remainder < 0;

int mon_years = mon / 12 - negative_mon_remainder;

long_int lyear_requested = year_requested;

long_int year = lyear_requested + mon_years;

/* The other values need not be in range:

the remaining code handles minor overflows correctly,

assuming int and time_t arithmetic wraps around.

Major overflows are caught at the end. */

/* Calculate day of year from year, month, and day of month.

The result need not be in range. */

int mon_yday = ((__mon_yday[leapyear (year)]

[mon_remainder + 12 * negative_mon_remainder])

- 1);

long_int lmday = mday;

long_int yday = mon_yday + lmday;

time_t guessed_offset = *offset;

int sec_requested = sec;

if (LEAP_SECONDS_POSSIBLE)

{

/* Handle out-of-range seconds specially,

since ydhms_tm_diff assumes every minute has 60 seconds. */

if (sec < 0)

sec = 0;

if (59 < sec)

sec = 59;

}

/* Invert CONVERT by probing. First assume the same offset as last

time. */

t0 = ydhms_diff (year, yday, hour, min, sec,

EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset);

if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)

{

/* time_t isn't large enough to rule out overflows, so check

for major overflows. A gross check suffices, since if t0

has overflowed, it is off by a multiple of TIME_T_MAX -

TIME_T_MIN + 1. So ignore any component of the difference

that is bounded by a small value. */

/* Approximate log base 2 of the number of time units per

biennium. A biennium is 2 years; use this unit instead of

years to avoid integer overflow. For example, 2 average

Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,

which is 63113904 seconds, and rint (log2 (63113904)) is

26. */

int ALOG2_SECONDS_PER_BIENNIUM = 26;

int ALOG2_MINUTES_PER_BIENNIUM = 20;

int ALOG2_HOURS_PER_BIENNIUM = 14;

int ALOG2_DAYS_PER_BIENNIUM = 10;

int LOG2_YEARS_PER_BIENNIUM = 1;

int approx_requested_biennia =

(SHR (year_requested, LOG2_YEARS_PER_BIENNIUM)

- SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)

+ SHR (mday, ALOG2_DAYS_PER_BIENNIUM)

+ SHR (hour, ALOG2_HOURS_PER_BIENNIUM)

+ SHR (min, ALOG2_MINUTES_PER_BIENNIUM)

+ (LEAP_SECONDS_POSSIBLE

? 0

: SHR (sec, ALOG2_SECONDS_PER_BIENNIUM)));

int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM);

int diff = approx_biennia - approx_requested_biennia;

int approx_abs_diff = diff < 0 ? -1 - diff : diff;

/* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously

gives a positive value of 715827882. Setting a variable

first then doing math on it seems to work.

(ghazi@caip.rutgers.edu) */

time_t time_t_max = TIME_T_MAX;

time_t time_t_min = TIME_T_MIN;

time_t overflow_threshold =

(time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;

if (overflow_threshold < approx_abs_diff)

{

/* Overflow occurred. Try repairing it; this might work if

the time zone offset is enough to undo the overflow. */

time_t repaired_t0 = -1 - t0;

approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);

diff = approx_biennia - approx_requested_biennia;

approx_abs_diff = diff < 0 ? -1 - diff : diff;

if (overflow_threshold < approx_abs_diff)

return -1;

guessed_offset += repaired_t0 - t0;

t0 = repaired_t0;

}

}

/* Repeatedly use the error to improve the guess. */

for (t = t1 = t2 = t0, dst2 = 0;

(gt = guess_time_tm (year, yday, hour, min, sec, &t,

ranged_convert (convert, &t, &tm)),

t != gt);

t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0)

if (t == t1 && t != t2

&& (tm.tm_isdst < 0

|| (isdst < 0

? dst2 <= (tm.tm_isdst != 0)

: (isdst != 0) != (tm.tm_isdst != 0))))

/* We can't possibly find a match, as we are oscillating

between two values. The requested time probably falls

within a spring-forward gap of size GT - T. Follow the common

practice in this case, which is to return a time that is GT - T

away from the requested time, preferring a time whose

tm_isdst differs from the requested value. (If no tm_isdst

was requested and only one of the two values has a nonzero

tm_isdst, prefer that value.) In practice, this is more

useful than returning -1. */

goto offset_found;

else if (--remaining_probes == 0)

return -1;

/* We have a match. Check whether tm.tm_isdst has the requested

value, if any. */

if (isdst_differ (isdst, tm.tm_isdst))

{

/* tm.tm_isdst has the wrong value. Look for a neighboring

time with the right value, and use its UTC offset.

Heuristic: probe the adjacent timestamps in both directions,

looking for the desired isdst. This should work for all real

time zone histories in the tz database. */

/* Distance between probes when looking for a DST boundary. In

tzdata2003a, the shortest period of DST is 601200 seconds

(e.g., America/Recife starting 2000-10-08 01:00), and the

shortest period of non-DST surrounded by DST is 694800

seconds (Africa/Tunis starting 1943-04-17 01:00). Use the

minimum of these two values, so we don't miss these short

periods when probing. */

int stride = 601200;

/* The longest period of DST in tzdata2003a is 536454000 seconds

(e.g., America/Jujuy starting 1946-10-01 01:00). The longest

period of non-DST is much longer, but it makes no real sense

to search for more than a year of non-DST, so use the DST

max. */

int duration_max = 536454000;

/* Search in both directions, so the maximum distance is half

the duration; add the stride to avoid off-by-1 problems. */

int delta_bound = duration_max / 2 + stride;

int delta, direction;

for (delta = stride; delta < delta_bound; delta += stride)

for (direction = -1; direction <= 1; direction += 2)

if (time_t_int_add_ok (t, delta * direction))

{

time_t ot = t + delta * direction;

struct tm otm;

ranged_convert (convert, &ot, &otm);

if (! isdst_differ (isdst, otm.tm_isdst))

{

/* We found the desired tm_isdst.

Extrapolate back to the desired time. */

t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm);

ranged_convert (convert, &t, &tm);

goto offset_found;

}

}

}

offset_found:

*offset = guessed_offset + t - t0;

if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)

{

/* Adjust time to reflect the tm_sec requested, not the normalized value.

Also, repair any damage from a false match due to a leap second. */

int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec;

if (! time_t_int_add_ok (t, sec_requested))

return -1;

t1 = t + sec_requested;

if (! time_t_int_add_ok (t1, sec_adjustment))

return -1;

t2 = t1 + sec_adjustment;

if (! convert (&t2, &tm))

return -1;

t = t2;

}

*tp = tm;

return t;

}

 

tzset.c


   

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85


   

void

__tzset (void)

{

__libc_lock_lock (tzset_lock);

tzset_internal (1, 1);

if (!__use_tzfile)

{

/* Set `tzname'. */

__tzname[0] = (char *) tz_rules[0].name;

__tzname[1] = (char *) tz_rules[1].name;

}

__libc_lock_unlock (tzset_lock);

}

weak_alias (__tzset, tzset)

/* Interpret the TZ envariable. */

static void

internal_function

tzset_internal (int always, int explicit)

{

static int is_initialized;

const char *tz;

if (is_initialized && !always)

return;

is_initialized = 1;

/* Examine the TZ environment variable. */

tz = getenv ("TZ");

if (tz == NULL && !explicit)

/* Use the site-wide default. This is a file name which means we

would not see changes to the file if we compare only the file

name for change. We want to notice file changes if tzset() has

been called explicitly. Leave TZ as NULL in this case. */

tz = TZDEFAULT;

if (tz && *tz == '\0')

/* User specified the empty string; use UTC explicitly. */

tz = "Universal";

/* A leading colon means "implementation defined syntax".

We ignore the colon and always use the same algorithm:

try a data file, and if none exists parse the 1003.1 syntax. */

if (tz && *tz == ':')

++tz;

/* Check whether the value changed since the last run. */

if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)

/* No change, simply return. */

return;

if (tz == NULL)

/* No user specification; use the site-wide default. */

tz = TZDEFAULT;

tz_rules[0].name = NULL;

tz_rules[1].name = NULL;

/* Save the value of `tz'. */

free (old_tz);

old_tz = tz ? __strdup (tz) : NULL;

/* Try to read a data file. */

__tzfile_read (tz, 0, NULL);

if (__use_tzfile)

return;

/* No data file found. Default to UTC if nothing specified. */

if (tz == NULL || *tz == '\0'

|| (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0))

{

memset (tz_rules, '\0', sizeof tz_rules);

tz_rules[0].name = tz_rules[1].name = "UTC";

if (J0 != 0)

tz_rules[0].type = tz_rules[1].type = J0;

tz_rules[0].change = tz_rules[1].change = (time_t) -1;

update_vars ();

return;

}

__tzset_parse_tz (tz);

}

 

2.2 源码分析结论

从mktime的源码实现中可以看出,mktime的大致执行流程如下:

  • 首先通过调用__tzset()对时区进行设置
    • 若TZ环境变量为NULL,尝试调用__tzfile_read读取文件中的时区信息
    • 只需要首次设置,若改变或为NULL重新设置
  • 然后调用__mktime_internal进行时间转换
    • 检查系统的tm_isdst(是否为夏令时)和传入的struct tm的tm_isdst,若两个不一致(isdst_differ),则进行矫正tm_isdst

从上源码可以得出结论:影响mktime性能的两个因素主要包括两方面,一是TZ设置是否为NULL,二是传入的struct tm参数的tm_isdst成员与系统的是否一致。

3. 实验分析mktime性能

3.1 TZ对mktime的性能影响

3.1.1 实验程序


   

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23


   

void test_TZ_effect(int count)

{

struct tm temp_tm;

temp_tm.tm_isdst = 0;

std::string time_str = "2019-01-02 00:00:00";

strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &temp_tm);

setenv("TZ", "Asia/Shanghai", 0);

PrintTime(TZ_SET);

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(TZ_SET);

unsetenv("TZ");

PrintTime(TZ_UNSET);

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(TZ_UNSET);

}

3.1.2 实验结果:


   

./a.out 10000

32位机器实验结果:

timer_TZ_SET spend time:3945 us

timer_TZ_UNSET spend time:14332 us

64位机器实验结果:

timer_TZ_SET spend time:2566 us

timer_TZ_UNSET spend time:17459 us

3.1.3 实验结果分析

从源码分析可以知道,当TZ为NULL时,将会尝试读取文件,故不设置TZ时性能较低,约为设置的1/6

3.2 tm_isdst对mktime性能影响

3.2.1 实验程序


   

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55


   

void test_TZ_effect(int count)

{

struct tm temp_tm;

std::string time_str = "2019-01-02 00:00:00";

strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &temp_tm);

setenv("TZ", "Asia/Shanghai", 0);

PrintTime(DST_INNER_0);

for (int i = 0; i < count; i++)

{

temp_tm.tm_isdst = 0;

mktime(&temp_tm);

}

PrintTimeDone(DST_INNER_0);

PrintTime(DST_INNER_1);

for (int i = 0; i < count; i++)

{

temp_tm.tm_isdst = 1;

mktime(&temp_tm);

}

PrintTimeDone(DST_INNER_1);

PrintTime(DST_INNER_NAG1);

for (int i = 0; i < count; i++)

{

temp_tm.tm_isdst = -1;

mktime(&temp_tm);

}

PrintTimeDone(DST_INNER_NAG1);

/************************************/

PrintTime(DST_OUTTER_0);

temp_tm.tm_isdst = 0;

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(DST_OUTTER_0);

PrintTime(DST_OUTTER_1);

temp_tm.tm_isdst = 1;

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(DST_OUTTER_1);

PrintTime(DST_OUTTER_NAG1);

temp_tm.tm_isdst = -1;

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(DST_OUTTER_NAG1);

}

3.2.2 实验结果


   

$ ./a.out 10000

32位实验结果

imer_DST_INNER_0 spend time:3957 us

timer_DST_INNER_1 spend time:1986811 us

timer_DST_INNER_NAG1 spend time:3871 us

timer_DST_OUTTER_0 spend time:3889 us

timer_DST_OUTTER_1 spend time:4042 us

timer_DST_OUTTER_NAG1 spend time:3958 us

64位实验结果

timer_DST_INNER_0 spend time:2622 us

timer_DST_INNER_1 spend time:1408451 us

timer_DST_INNER_NAG1 spend time:2588 us

timer_DST_OUTTER_0 spend time:2534 us

timer_DST_OUTTER_1 spend time:2698 us

timer_DST_OUTTER_NAG1 spend time:2621 us

3.2.3 实验结果分析

从上面可以看出,当tm_isdst与系统夏令时设置不一致的时候,性能为一致时的1/500左右。为什么将tm_isdst设置放在外面的时候性能又高了很多呢?从源码可以看出,当mktime对isdst进行矫正后,将正确的isdst存到了传入的结构体中,所以第二次进来时,isdst与系统一致,不需要矫正。

特别注意:当tm_isdst初始化为-1的时候,mktime设置是否为夏令时时间时失手编译器的设计影响,所以传入isdst=-1可能会得到错误的结果,即非夏令时按照夏令时计算或者夏令时按照非夏令时计算。

4. 背景中出现的问题以及mktime使用注意事项

4.1 背景中问题原因

通过实验程序以及原程序片段分析,运行过程中,在32位机器上的tm_isdst默认值为0,在64位机器上tm_isdst的默认值为1(与系统和内存环境有关,可能出现不同结果,我的机器上上述问题),导致在32位机器上运行时间较短,而且第一次运行时需要设置TZ环境变量,故第一次运行时间为
51us,后面运行时间较短。而64为机器一致运行时间为150us左右。

4.2 mktime使用注意事项

  • 使用前建议设置TZ环境变量,在大量调用操作过程中,有助于将该部分性能提高到5~6倍.
  • mktime函数中传入的tm的tm_isdst参数必须设置为0或1,若设置为-1可能会得到错误的结果,或者使用默认值导致性能大大降低,国内一般设置为0,因为中国不采用夏令时。

5. 参考链接:

https://github.molgen.mpg.de/git-mirror/glibc/blob/20003c49884422da7ffbc459cdeee768a6fee07b/time/mktime.c
https://github.molgen.mpg.de/git-mirror/glibc/blob/20003c49884422da7ffbc459cdeee768a6fee07b/time/tzset.c
https://blog.csdn.net/aquester/article/details/54669264

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mktime函数性能分析 的相关文章

  • 海康摄像头使用RTSP

    1 协议格式 海康威视IP摄像头rtsp协议地址如下 xff1a rtsp username passwd 64 ip port codec channel subtype av stream 主码流 xff1a rtsp admin 12
  • 树莓派串口连接ESP8266

    陈拓 chentuo 64 ms xab ac cn 2020 03 12 2020 03 12 1 概述 ESP8266是物联网行业广泛使用的WiFi模块 xff0c 小巧 功能强大 xff0c 而且价格低廉 通常用电脑进行ESP8266
  • Linux 创建TCP连接流程

    文章目录 Linux创建TCP的步骤服务端客户端TCP建立流程示例代码 Linux创建TCP的步骤 TCP编程需要客户端和服务器两套编码 xff0c 其创建TCP的流程也是不完全一致的 服务端 使用socket函数创建一个套接字使用sets
  • 结构体类型完全归纳

    结构体类型 目录 基本概述 一 结构体类型变量的定义方法及其初始化 1 定义结构体类型变量的方法 2 结构体变量的初始化 二 结构体变量的引用 三 结构体数组 1 定义结构体数组 2 结构体数组应用举例 四 指向结构体变量的指针 1 类型一
  • Ubuntu20.04LTS下安装Intel Realsense D435i驱动与ROS包

    文章目录 目标一 D435i简介二 环境配置三 RealSense的SDK2 0安装四 ROS包安装五 摄像机CV的ROS包节点 六 问题排查 目标 在Ubuntu20 04LTS系统下安装D435i的驱动SDK2和ROS包 xff0c 实
  • C# 调用NationalInstruments的dll报错问题 未能加载文件或程序集

    C 调用NationalInstruments的dll报错问题 问题原因 xff1a dll版本不匹配导致的 xff0c 需要做如下操作解决问题 未能加载文件或程序集 NationalInstruments Common Version 6
  • 需要授权的 API ,必须在请求头中使用 Authorization 字段提供 token 令牌

    需要授权的 API xff0c 必须在请求头中使用 添加字段 需要授权的 API xff0c 必须在请求头中使用 Authorization 字段提供 token 令牌 实现方法 通过 axios 请求拦截器添加 token xff0c 保
  • 关于HTTP解析的一点思考

    原文 似乎已经很久没有提到关于服务器的消息了 xff0c 其实我一直都在写 xff0c 只是有时事情比较多 xff0c 会耽搁一点时间 在使用C重写前 xff0c 我就已经用Dlang实现了近2个版本的HTTP解析器 xff0c 换成C之后
  • Paparazzi UAV Lisa/M2飞控使用说明书

    第一部分 地面站 Paparazzi xff08 简称PPZ xff09 UAV项目起始于2003年 xff0c 由法国民航大学发起的一套软硬件开源无人机项目 xff0c 它提供了一整套完整的无人机软硬件解决方案 PPZ 地面站软件运行在L
  • Anaconda3-2020.02-Windows-x86_64安装及使用步骤

    Conda是一个开源的包 环境管理器 xff0c 可以用于在同一个机器上安装不同版本的软件包及其依赖 xff0c 并能够在不同的环境之间切换 Anaconda包括Conda Python以及一大堆安装好的工具包 xff0c 比如 xff1a
  • vivado 2017.4安装步骤

    目录 xff1a windows安装vivado2017 4 xff1b 虚拟机ubuntu安装vivado 2017 4 xff1b ios安装vivado 一 xff0c windows安装vivado2017 4 xilinx官网下载
  • LINUX C语言TCP客户端和服务器传输结构体数据

    1 xff0c TCP服务器流程 服务器 xff1a 1 创建socket xff0c 使用socket函数 2 准备通信地址 xff0c 使用结构体类型 3 绑定socket和通信地址 xff0c 使用bind函数 4 进行通信 xff0
  • FDC系列电容传感器及FDC2214使用要点

    陈拓 2021 02 21 2021 02 21 1 概述 电容式传感是一种低功耗 低成本且高分辨率的非接触式感测技术 xff0c 适用于从接近检测 手势识别到远程液位感测的各项应用 电容式传感系统中的传感器可以采用任意金属或导体 xff0
  • 卫星数据高动态捕获

    一 xff0c 高动态导航接收终端的现状 早期的扩频通信系统由于受到集成电路水平的限制 xff0c 多采用串行搜索技术 由于串行捕获速度慢 xff0c 耗时长不能满足高动态等环境对速度的要求 xff0c 随着数字信号处理等技术的发展 xff
  • 基于ZYNQ平台的powerlink接口平台搭建

    1 xff0c 搭建powerlink接口所需硬件平台 xff1a Zynq ZC702 board used as openPOWERLINK MN AVNET expander board AES FMC ISMNET G Linux
  • 雷达测距测速测角基本原理

    由雷达发射机产生的电磁波经收发开关后传输给天线 xff0c 由天线将此电磁波定向辐射于大气中 电磁波在大气中以近光速传播 xff0c 如目标恰好位于定向天线的波束内 xff0c 则它将要截取一部分电磁波 目标将被截取的电磁波向各方向散射 x
  • 信号处理之脉冲压缩

    一 xff0c 脉冲压缩的背景 随着飞行技术的飞速发展 xff0c 对雷达的作用距离 分辨能力 测量精度和单值性等性能指标提出越来越高的要求 测距精度和距离分辨力对信号形式的要求是一致的 xff0c 主要取决于信号的频率结构 xff0c 为
  • MTI动目标指示和MTD动目标检测

    MTI 是一种频域滤波器 radar主席的ppt 中说到 xff0c 它是对多组脉冲回波的同一个距离单元加权求和 xff0c 得到一个结果 xff1b 也就是多个输入一个输出 xff1b 相当于一个高通滤波器 xff0c 用来抑制固定目标和
  • 复旦微开发过程中遇到的问题总结(二)

    一 xff0c 将bin文件放到flash中0地址处能识别并且启动吗 xff1f xlinx的放在0地址处可以识别启动 xff0c 我尝试复旦微这个没反应 要用procise生成 xff0c 第一个必须是FSBL out 只能是procis
  • 用链表实现fifo功能缓存和拼接数据功能

    fifo h ifndef LIST QUEUE H define LIST QUEUE H include lt stdio h gt include lt stdlib h gt include 34 xil types h 34 in

随机推荐

  • zynq bootgen配置启动

    一 xff0c Zynq 7000 SoC 启动头文件 0x00 0x1F Arm 矢量表 由 Bootgen 使用虚拟矢量表填充 xff08 Arm 操作代码 0xEAFFFFFE xff0c 即用于捕获未初始化矢量的 branch to
  • 制作四个文件启动的镜像

    一 环境搭建 xff1a vivado2018 3 xff0c petalinux2018 3 xff0c 1 petalinux环境设置 所使用的编译环境需要使用petalinux这个软件 xff0c 第五章Petalinux 的安装 里
  • ubuntu虚拟机更改镜像源(中科大或者阿里云镜像源)

    ubuntu虚拟机更改镜像源 xff08 中科大或者阿里云镜像源 xff09 1 进入终端后 xff0c 编辑源列表文件 xff1a 输入 xff1a sudo vim etc apt sources list 后输入 xff1a i 2
  • 海康威视客户端iVMS-4200连接NVR

    海康威视客户端 iVMS 4200 连接 NVR 陈拓 2021 07 30 2021 08 01 1 概述 iVMS 4200 客户端是一款与网络监控设备配套使用的综合应用软件 xff0c 可满足用户多方面需求 xff0c 如设备管理 人
  • 匿名上位机使用方法分享--总体介绍

    不知不觉 xff0c 匿名科创已经走过了7个年头 xff0c 这里首先要感谢大家这么久以来对匿名的支持与帮助 xff01 匿名为了提供给大家一个更好的调试工具 xff0c 始终在维护开发我们的匿名上位机软件 xff0c 7年时间 xff0c
  • 匿名上位机使用方法分享--高级收码

    匿名上位机总体介绍移步 xff1a https blog csdn net wangjt1988 article details 83684188 本文视频介绍 xff1a https www bilibili com video av35
  • 匿名上位机使用方法分享--波形显示

    匿名上位机总体介绍移步 xff1a https blog csdn net wangjt1988 article details 83684188 波形显示可以说是上位机的功能重点 xff0c 是各种调试 数据分析的有力助手 xff0c 下
  • 匿名数传使用方法分享

    目录 欢迎使用匿名数传模块匿名数传的特点硬件介绍使用介绍指示灯连接匿名飞控建议 欢迎使用匿名数传模块 大家调试各种设备时 xff0c 一般用什么方式呢 xff1f 相比答案大多是上位机 43 串口的方式 如果您还在使用usb转串口芯片然后连
  • 匿名科创--X2212版到手飞套件介绍

    匿名科创到手飞X2212版 xff0c 使用朗宇X2212系列无刷电机 xff0c 配合特制的6mm正反螺纹螺旋桨安装柱 xff0c 可以同时兼容8寸普通螺旋桨和9寸9450自锁螺旋桨 优点 xff1a 可直接使用普通8寸螺旋桨 xff0c
  • vscode最皮实的C++格式化的配置方法

    1 安装C C 43 43 2 在vscode界面 xff0c 按 34 Ctrl 43 34 进入设置界面 xff0c 搜索Format 3 设置保存文件时 xff0c 按格式对代码排版 4 向下拉 xff0c 找到下图选项 xff0c
  • 通过openmv生成apriltag标签

    Apriltag官网提供的tag图片分辨率很低 xff0c 完全无法使用 xff0c 通过openmv生成apriltag标签 生成方法如下 xff1a openmv IDE的下载与安装 openmv官方提供了各种版本的IDE xff0c
  • 串口传输数据错位 的几种解决办法

    1 代码优化等级 2 使用晶振 晶振自身产生时钟信号 xff0c 为各种微处理芯片作时钟参考 无源晶振需要用CPU内部的振荡器信号差接线麻烦石英 gt 陶瓷有源晶振是一个完整的振荡器信号好接线简单灵活性较差 3 使用降低传输速率 xff1f
  • sip 认证分析

    SIP类似Http协议 其认证模式也一样 Http协议 xff08 RFC 2616 xff09 规定可以采用Basic模式和摘要模式 xff08 Digest schema xff09 RFC 2617 专门对两种认证模式做了规定 RFC
  • MicroPython移植

    MicroPython移植 1 目标板 stm32f407zgt6 2 下载移植准备 micropython源码 arm交叉编译工具 sudo apt get install git sudo apt get install gcc arm
  • 了解ESP32睡眠模式及其功耗

    陈拓翻译 2022 05 30 2022 05 30 原文 https lastminuteengineers com esp32 sleep modes power consumption 毫无疑问 xff0c ESP32是许多WiFi
  • 浅谈布隆过滤器

    什么是布隆过滤器 布隆过滤器是一种数据结构 xff0c 比较巧妙的概率型数据结构 xff08 probabilistic data structure xff09 xff0c 特点是高效地插入和查询 xff0c 可以用来告诉你 某样东西一定
  • 浅谈CGI基本原理和底层基本实现

    历史来由 xff1a 早期的Web服务器 xff0c 只能响应浏览器发来的HTTP静态资源的请求 xff0c 并将存储在服务器中的静态资源返回给浏览器 随着Web技术的发展 xff0c 逐渐出现了动态技术 xff0c 但是Web服务器并不能
  • linux的两种共享内存方式---mmap和shmat区别

    linux中的两种共享内存 一种是我们的IPC通信System V版本的共享内存 xff0c 另外的一种就是我们今天提到的存储映射I O xff08 mmap函数 xff09 在说mmap之前我们先说一下普通的读写文件的原理 xff0c 进
  • tcp发送窗口(滑动窗口)、拥塞窗口

    TCP发送窗口拥塞窗口试题分析 题目一 xff1a 来源2015年408计算机综合 试题链接 xff1a https www nowcoder com questionTerminal 3241441c88f04ab58585a187716
  • mktime函数性能分析

    mktime函数性能分析 1月 02 2019 in Linux环境高级编程 mktime函数性能分析 mktime是一个将break down时间 struct tm 转化为日历时间 time t 的转换函数 它的转换与struct tm