• 1 前言
  • 2 WQ_POWER_EFFICIENT的处理
  • 3 分配workqueue的内存
    • 3.1 workqueue和pool workqueue的关系
    • 3.2 workqueue attribute
    • 3.3 unbound workqueue和NUMA之间的联系
  • 4 初始化workqueue的成员
  • 5 分配pool workqueue的内存并建立workqueue和pool workqueue的关系
  • 6 应用新的attribute到workqueue中
    • 6.1 健康检查
    • 6.2 分配内存并初始化
    • 6.3 如何为unbound workqueue的pool workqueue寻找对应的线程池?
    • 6.4 给各个node分配pool workqueue并初始化
    • 6.5 安装

1 前言

本文主要以__alloc_workqueue_key函数为主线,描述CMWQ中的创建一个workqueue实例的代码过程。

2 WQ_POWER_EFFICIENT的处理

__alloc_workqueue_key函数的一开始有如下的代码:

if ((flags & WQ_POWER_EFFICIENT) && wq_power_efficient) flags |= WQ_UNBOUND;

在kernel中,有两种线程池,一种是线程池是per cpu的,也就是说,系统中有多少个cpu,就会创建多少个线程池,cpu x上的线程池创建的worker线程也只会运行在cpu x上。另外一种是unbound thread pool,该线程池创建的worker线程可以调度到任意的cpu上去。由于cache locality的原因,per cpu的线程池的性能会好一些,但是对power saving有一些影响。设计往往如此,workqueue需要在performance和power saving之间平衡,想要更好的性能,那么最好让一个cpu上的worker thread来处理work,这样的话,cache命中率会比较高,性能会更好。但是,从电源管理的角度来看,最好的策略是让idle状态的cpu尽可能的保持idle,而不是反复idle,working,idle again。

我们来一个例子辅助理解上面的内容。在t1时刻,work被调度到CPU A上执行,t2时刻work执行完毕,CPU A进入idle,t3时刻有一个新的work需要处理,这时候调度work到那个CPU会好些呢?是处于working状态的CPU B还是处于idle状态的CPU A呢?如果调度到CPU A上运行,那么,由于之前处理过work,其cache内容新鲜热辣,处理起work当然是得心应手,速度很快,但是,这需要将CPU A从idle状态中唤醒。选择CPU B呢就不存在将CPU 从idle状态唤醒,从而获取power saving方面的好处。

了解了上面的基础内容之后,我们再来检视per cpu thread pool和unbound thread pool。当workqueue收到一个要处理的work,如果该workqueue是unbound类型的话,那么该work由unbound thread pool处理并把调度该work去哪一个CPU执行这样的策略交给系统的调度器模块来完成,对于scheduler而言,它会考虑CPU core的idle状态,从而尽可能的让CPU保持在idle状态,从而节省了功耗。因此,如果一个workqueue有WQ_UNBOUND这样的flag,则说明该workqueue上挂入的work处理是考虑到power saving的。如果workqueue没有WQ_UNBOUND flag,则说明该workqueue是per cpu的,这时候,调度哪一个CPU core运行worker thread来处理work已经不是scheduler可以控制的了,这样,也就间接影响了功耗。

有两个参数可以控制workqueue在performance和power saving之间的平衡:

1、各个workqueue需要通过WQ_POWER_EFFICIENT来标记自己在功耗方面的属性

2、系统级别的内核参数workqueue.power_efficient。

使用workqueue的用户知道自己在电源管理方面的特点,如果该workqueue在unbound的时候会极大的降低功耗,那么就需要加上WQ_POWER_EFFICIENT的标记。这时候,如果没有标记WQ_UNBOUND,那么缺省workqueue会创建per cpu thread pool来处理work。不过,也可以通过workqueue.power_efficient这个内核参数来修改workqueue的行为:

#ifdef CONFIG_WQ_POWER_EFFICIENT_DEFAULT 
static bool wq_power_efficient = true; 
#else 
static bool wq_power_efficient; 
#endifmodule_param_named(power_efficient, wq_power_efficient, bool, 0444);

如果wq_power_efficient设定为true,那么WQ_POWER_EFFICIENT的标记的workqueue就会强制按照unbound workqueue来处理,即使没有标记WQ_UNBOUND。

3 分配workqueue的内存

if (flags & WQ_UNBOUND) tbl_size = nr_node_ids * sizeof(wq->numa_pwq_tbl[0]); ---only for unbound workqueuewq = kzalloc(sizeof(*wq) + tbl_size, GFP_KERNEL);if (flags & WQ_UNBOUND) { wq->unbound_attrs = alloc_workqueue_attrs(GFP_KERNEL); --only for unbound workqueue }

代码很简单,与其要解释代码,不如来解释一些基本概念。

3.1 workqueue和pool workqueue的关系

我们先给出一个简化版本的workqueue_struct定义,如下:

struct workqueue_struct { struct list_head    pwqs;  struct list_head    list;struct pool_workqueue __percpu *cpu_pwqs;  -----指向per cpu的pool workqueue struct pool_workqueue __rcu *numa_pwq_tbl[]; ----指向per node的pool workqueue 
};

这里涉及2个数据结构:workqueue_struct和pool_workqueue,为何如此处理呢?我们知道,在CMWQ中,workqueue和thread pool没有严格的一一对应关系了,因此,系统中的workqueue们共享一组thread pool,因此,workqueue中的成员包括两个类别:global类型和per thread pool类型的,我们把那些per thread pool类型的数据集合起来就形成了pool_workqueue的定义。

挂入workqueue的work终究需要worker pool中的某个worker thread来处理,也就是说,workqueue要和系统中那些共享的worker thread pool进行连接,这是通过pool_workqueue(该数据结构会包含一个指向worker pool的指针)的数据结构来管理的。和这个workqueue相关的pool_workqueue被挂入一个链表,链表头就是workqueue_struct中的pwqs成员。

和旧的workqueue机制一样,系统维护了一个所有workqueue的list,list head定义如下:

static LIST_HEAD(workqueues);

workqueue_struct中的list成员就是挂入这个链表的节点。

workqueue有两种:unbound workqueue和per cpu workqueue。对于per cpu类型,cpu_pwqs指向了一组per cpu的pool_workqueue数据结构,用来维护workqueue和per cpu thread pool之间的关系。每个cpu都有两个thread pool,normal和高优先级的线程池,到底cpu_pwqs指向哪一个pool_workqueue(worker thread)是和workqueue的flag相关,如果标有WQ_HIGHPRI,那么cpu_pwqs指向高优先级的线程池。unbound workqueue对应的pool_workqueue和workqueue属性相关,我们在下一节描述。

3.2 workqueue attribute

挂入workqueue的work终究是需要worker线程来处理,针对worker线程有下面几个考量点(我们称之attribute):

(1)该worker线程的优先级

(2)该worker线程运行在哪一个CPU上

(3)如果worker线程可以运行在多个CPU上,且这些CPU属于不同的NUMA node,那么是否在所有的NUMA node中都可以获取良好的性能。

对于per-CPU的workqueue,2和3不存在问题,哪个cpu上queue的work就在哪个cpu上执行,由于只能在一个确定的cpu上执行,因此起NUMA的node也是确定的(一个CPU不可能属于两个NUMA node)。置于优先级,per-CPU的workqueue使用WQ_HIGHPRI来标记。综上所述,per-CPU的workqueue不需要单独定义一个workqueue attribute,这也是为何在workqueue_struct中只有unbound_attrs这个成员来记录unbound workqueue的属性。

unbound workqueue由于不绑定在具体的cpu上,可以运行在系统中的任何一个cpu,直觉上似乎系统中有一个unbound thread pool就OK了,不过让一个thread pool创建多种属性的worker线程是一个好的设计吗?本质上,thread pool应该创建属性一样的worker thread。因此,我们通过workqueue属性来对unbound workqueue进行分类,workqueue属性定义如下:

struct workqueue_attrs { int            nice;        /* nice level */ cpumask_var_t        cpumask;    /* allowed CPUs */ bool            no_numa;    /* disable NUMA affinity */ 
};

nice是一个和thread优先级相关的属性,nice越低则优先级越高。cpumask是该workqueue挂入的work允许在哪些cpu上运行。no_numa是一个和NUMA affinity相关的设定。

3.3 unbound workqueue和NUMA之间的联系

UMA系统中,所有的processor看到的内存都是一样的,访问速度也是一样,无所谓local or remote,因此,内核线程如果要分配内存,那么也是无所谓,统一安排即可。在NUMA系统中,不同的一个或者一组cpu看到的memory是不一样的,我们假设node 0中有CPU A和B,node 1中有CPU C和D,如果运行在CPU A上内核线程现在要迁移到CPU C上的时候,悲剧发生了:该线程在A CPU创建并运行的时候,分配的内存是node 0中的memory,这些memory是local的访问速度很快,当迁移到CPU C上的时候,原来local memory变成remote,性能大大降低。因此,unbound workqueue需要引入NUMA的考量点。

NUMA是内存管理的范畴,本文不会深入描述,我们暂且放开NUMA,先思考这样的一个问题:一个确定属性的unbound workqueue需要几个线程池?看起来一个就够了,毕竟workqueue的属性已经确定了,一个线程池创建相同属性的worker thread就行了。但是我们来看一个例子:假设workqueue的work是可以在node 0中的CPU A和B,以及node 1中CPU C和D上处理,如果只有一个thread pool,那么就会存在worker thread在不同node之间的迁移问题。为了解决这个问题,实际上unbound workqueue实际上是创建了per node的pool_workqueue(thread pool)

当然,是否使用per node的pool workqueue用户是可以通过下面的参数进行设定的:

(1)workqueue attribute中的no_numa成员

(2)通过workqueue.disable_numa这个参数,disable所有workqueue的numa affinity的支持。

static bool wq_disable_numa; 
module_param_named(disable_numa, wq_disable_numa, bool, 0444);

4 初始化workqueue的成员

va_start(args, lock_name); 
vsnprintf(wq->name, sizeof(wq->name), fmt, args);-----set workqueue name 
va_end(args);max_active = max_active ?: WQ_DFL_ACTIVE; 
max_active = wq_clamp_max_active(max_active, flags, wq->name); 
wq->flags = flags; 
wq->saved_max_active = max_active; 
mutex_init(&wq->mutex); 
atomic_set(&wq->nr_pwqs_to_flush, 0); 
INIT_LIST_HEAD(&wq->pwqs); 
INIT_LIST_HEAD(&wq->flusher_queue); 
INIT_LIST_HEAD(&wq->flusher_overflow); 
INIT_LIST_HEAD(&wq->maydays);lockdep_init_map(&wq->lockdep_map, lock_name, key, 0); 
INIT_LIST_HEAD(&wq->list);

除了max active,没有什么要说的,代码都简单而且直观。如果用户没有设定max active(或者说max active等于0),那么系统会给出一个缺省的设定。系统定义了两个最大值WQ_MAX_ACTIVE(512)和WQ_UNBOUND_MAX_ACTIVE(和cpu数目有关,最大值是cpu数目乘以4,当然也不能大于WQ_MAX_ACTIVE),分别限定per cpu workqueue和unbound workqueue的最大可以创建的worker thread的数目。wq_clamp_max_active可以将max active限制在一个确定的范围内。

5 分配pool workqueue的内存并建立workqueue和pool workqueue的关系

这部分的代码主要涉及alloc_and_link_pwqs函数,如下:

static int alloc_and_link_pwqs(struct workqueue_struct *wq) 
{ bool highpri = wq->flags & WQ_HIGHPRI;----normal or high priority? int cpu, ret;if (!(wq->flags & WQ_UNBOUND)) {-----per cpu workqueue的处理 wq->cpu_pwqs = alloc_percpu(struct pool_workqueue);for_each_possible_cpu(cpu) {-----逐个cpu进行设定 struct pool_workqueue *pwq =    per_cpu_ptr(wq->cpu_pwqs, cpu); struct worker_pool *cpu_pools = per_cpu(cpu_worker_pools, cpu);init_pwq(pwq, wq, &cpu_pools[highpri]);  link_pwq(pwq);----上面两行代码用来建立workqueue、pool wq和thread pool之间的关系 } return 0; } else if (wq->flags & __WQ_ORDERED) {-----ordered unbound workqueue的处理 ret = apply_workqueue_attrs(wq, ordered_wq_attrs[highpri]); return ret; } else {-----unbound workqueue的处理 return apply_workqueue_attrs(wq, unbound_std_wq_attrs[highpri]); } 
}

通过alloc_percpu可以为每一个cpu分配一个pool_workqueue的memory。每个pool_workqueue都有一个对应的worker thread pool,对于per-CPU workqueue,它是静态定义的,如下:

static DEFINE_PER_CPU_SHARED_ALIGNED(struct worker_pool [NR_STD_WORKER_POOLS], cpu_worker_pools);

init_pwq函数初始化pool_workqueue,最重要的是设定其对应的workqueue和worker pool。link_pwq主要是将pool_workqueue挂入它所属的workqueue的链表中。对于unbound workqueue,apply_workqueue_attrs完成分配pool workqueue并建立workqueue和pool workqueue的关系。

6 应用新的attribute到workqueue中

unbound workqueue有两种,一种是normal type,另外一种是ordered type,这种workqueue上的work是严格按照顺序执行的,不存在并发问题。ordered unbound workqueue的行为类似过去的single thread workqueue。但是,无论那种类型的unbound workqueue都使用apply_workqueue_attrs来建立workqueue、pool wq和thread pool之间的关系。

6.1 健康检查

if (WARN_ON(!(wq->flags & WQ_UNBOUND))) return -EINVAL;if (WARN_ON((wq->flags & __WQ_ORDERED) && !list_empty(&wq->pwqs))) return -EINVAL;

只有unbound类型的workqueue才有attribute,才可以apply attributes。对于ordered类型的unbound workqueue,属于它的pool workqueue(worker thread pool)只能有一个,否则无法限制work是按照顺序执行。

6.2 分配内存并初始化

pwq_tbl = kzalloc(nr_node_ids * sizeof(pwq_tbl[0]), GFP_KERNEL); 
new_attrs = alloc_workqueue_attrs(GFP_KERNEL); 
tmp_attrs = alloc_workqueue_attrs(GFP_KERNEL); 
copy_workqueue_attrs(new_attrs, attrs); 
cpumask_and(new_attrs->cpumask, new_attrs->cpumask, cpu_possible_mask); 
copy_workqueue_attrs(tmp_attrs, new_attrs);

pwq_tbl数组用来保存unbound workqueue各个node的pool workqueue的指针,new_attrs和tmp_attrs都是一些计算workqueue attribute的中间变量,开始的时候设定为用户传入的workqueue的attribute。

6.3 如何为unbound workqueue的pool workqueue寻找对应的线程池?

具体的代码在get_unbound_pool函数中。本节不描述具体的代码,只说明基本原理,大家可以自行阅读代码。

per cpu的workqueue的pool workqueue对应的线程池也是per cpu的,每个cpu有两个线程池(normal和high priority),因此将pool workqueue和thread pool对应起来是非常简单的事情。对于unbound workqueue,对应关系没有那么直接,如果属性相同,多个unbound workqueue的pool workqueue可能对应一个thread pool。

系统使用哈希表来保存所有的unbound worker thread pool,定义如下:

static DEFINE_HASHTABLE(unbound_pool_hash, UNBOUND_POOL_HASH_ORDER);

在创建unbound workqueue的时候,pool workqueue对应的worker thread pool需要在这个哈希表中搜索,如果有相同属性的worker thread pool的话,那么就不需要创建新的线程池,代码如下:

hash_for_each_possible(unbound_pool_hash, pool, hash_node, hash) { if (wqattrs_equal(pool->attrs, attrs)) { ----检查属性是否相同 pool->refcnt++; return pool; -------在哈希表找到适合的unbound线程池 } 
}

如果没有相同属性的thread pool,那么需要创建一个并挂入哈希表。

6.4 给各个node分配pool workqueue并初始化

在进入代码之前,先了解一些基础知识。缺省情况下,挂入unbound workqueue的works最好是考虑NUMA Affinity,这样可以获取更好的性能。当然,实际上用户可以通过workqueue.disable_numa这个内核参数来关闭这个特性,这时候,系统需要一个default pool workqueue(workqueue_struct的dfl_pwq成员),所有的per node的pool workqueue指针都是执行default pool workqueue。

workqueue.disable_numa是enable的情况下是否不需要default pool workqueue了呢?也不是,我们举一个简单的例子,一个系统的构成是这样的:node 0中有CPU A和B,node 1中有CPU C和D,node 2中有CPU E和F,假设workqueue的attribute规定work只能在CPU A 和C上运行,那么在node 0和node 1中创建自己的pool workqueue是ok的,毕竟node 0中有CPU A,node 1中有CPU C,该node创建的worker thread可以在A或者C上运行。但是对于node 2节点,没有任何的CPU允许处理该workqueue的work,在这种情况下,没有必要为node 2建立自己的pool workqueue,而是使用default pool workqueue。

OK,我们来看代码:

dfl_pwq = alloc_unbound_pwq(wq, new_attrs); -----分配default pool workqueuefor_each_node(node) { ----遍历node if (wq_calc_node_cpumask(attrs, node, -1, tmp_attrs->cpumask)) { ---是否使用default pool wq pwq_tbl[node] = alloc_unbound_pwq(wq, tmp_attrs); ---该node使用自己的pool wq } else { dfl_pwq->refcnt++; pwq_tbl[node] = dfl_pwq; ----该node使用default pool wq } 
}

值得一提的是wq_calc_node_cpumask这个函数,这个函数会根据该node的cpu情况以及workqueue attribute中的cpumask成员来更新tmp_attrs->cpumask,因此,在pwq_tbl[node] = alloc_unbound_pwq(wq, tmp_attrs); 这行代码中,为该node分配pool workqueue对应的线程池的时候,去掉了本node中不存在的cpu。例如node 0中有CPU A和B,workqueue的attribute规定work只能在CPU A 和C上运行,那么创建node 0上的pool workqueue以及对应的worker thread pool的时候,需要删除CPU C,也就是说,node 0上的线程池的属性中的cpumask仅仅支持CPU A了。

6.5 安装

所有的node的pool workqueue及其worker thread pool已经ready,需要安装到workqueue中了:

for_each_node(node) pwq_tbl[node] = numa_pwq_tbl_install(wq, node, pwq_tbl[node]);  link_pwq(dfl_pwq); swap(wq->dfl_pwq, dfl_pwq);

代码非常简单,这里就不细述了。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 8. softirq

    1 前言 2 为何有softirq和tasklet2.1 为何有top half和bottom half 2.2 为何有softirq和tasklet1 前言 对于中断处理而言,linux将其分成了两个部分,一个叫做中断handler(top half),是全程关闭中断的,另外一部分是deferable task(bottom half),属于不那么紧急需要处理的…...

    2024/4/24 9:46:55
  2. 13. workqueue如何处理work

    1 前言 2 用户将一个work挂入workqueue2.1 queue_work_on函数 2.2 __WQ_DRAINING的解释 2.3 选择pool workqueue 2.4 选择worker thread pool 2.5 选择work挂入的队列 2.6 唤醒idle的worker来处理该work3 线程池如何创建worker线程?3.1 per cpu worker pool什么时候创建worker…...

    2024/4/24 9:46:57
  3. 2. IRQ Domain介绍

    1 概述 2 历史 3 接口3.1 向系统注册irq domain 3.2 为irq domain创建映射4 数据结构描述4.1 irq domain的callback接口 4.2 irq domain5 中断相关的Device Tree知识回顾 6 Mapping DB的建立6.1 概述 6.2 interrupt controller初始化的过程中,注册irq domain 6.3 在各个硬件外…...

    2024/5/5 5:24:10
  4. 10. workqueue的基本概念

    1. 前言 2 为何需要workqueue2.1 什么是中断上下文和进程上下文? 2.2 如何判定当前的context? 2.3 为何中断上下文不能sleep? 2.4 为何需要workqueue3 数据抽象3.1 workqueue 3.2 work 3.3 总结4 接口以及内部实现4.1 初始化一个work 4.2 调度一个work执行 4.3 创建workqueu…...

    2024/5/5 6:23:33
  5. 3. IRQ number和中断描述符

    1 前言 2 基本概念2.1 通用中断的代码处理示意图 2.2 中断的打开和关闭 2.3 IRQ number3 中断描述符数据结构3.1 底层irq chip相关的数据结构 3.2 irq chip数据结构 3.3 中断描述符4 初始化相关的中断描述符的接口4.1 静态定义的中断描述符初始化 4.2 使用Radix tree的中断描述…...

    2024/4/24 9:46:51
  6. 5. 驱动申请中断API

    1 前言 2 和中断相关的linux实时性分析以及中断线程化的背景介绍2.1 非抢占式linux内核的实时性 2.2 抢占式linux内核的实时性 2.3 进一步提高linux内核的实时性3 request_threaded_irq的接口规格3.1 输入参数描述 3.2 输出参数描述 3.3 Interrupt type flags4 request_threade…...

    2024/4/24 9:46:50
  7. 9. tasklet[有完整中断(包括top和bottom half)调用示例]

    1 前言 2 为什么需要tasklet?2.1 基本的思考 2.2 对linux中的bottom half机制的思考3 tasklet的基本原理3.1 如何抽象一个tasklet 3.2 系统如何管理tasklet? 3.3 如何定义一个tasklet? 3.4 如何调度一个tasklet 3.5 在什么时机会执行tasklet?1 前言 对于中断处理而言,linu…...

    2024/4/24 9:46:57
  8. 14. 中断唤醒系统流程

    1 前言 2 中断唤醒流程2.1 enable_irq_wake 2.2 Suspend to RAM流程 2.3 resume流程 2.4 system core operations有什么用? 2.5 gic interrupt controller中断执行流程 2.6 dpm_resume_noirq()1 前言 曾几何时,不知道你是否想过外部中断是如何产生的呢?又是如何唤醒系统的呢…...

    2024/4/29 10:33:29
  9. HTTPS工作原理

    当你打开浏览器,访问某个网站,如果网址旁有个小锁,代表访问的网址是安全的,反之不安全。当我们没有看到那个小锁的小图标的时候,需要提高警惕,不要随意输入个人重要的资料。所有的银行和支付相关的网站都是 100% 使用 HTTPS 的。为什么需要 HTTPS?我们为什么需要 HTTPS?…...

    2024/4/24 9:46:47
  10. 开始好好学习 开始写博客吧

    总之开始试着写一下学习博客,希望能有所收获 浑浑噩噩几年,感觉自己没学到什么东西,疫情期间在家学习实在是没有自觉。 咬牙努力下,开始学着写一下博客,记录下接下来的变成学习生活。 博客内容:平时作业学到的内容(当一个笔记本)、前端学习(目标)、课余感兴趣的知识记…...

    2024/4/28 12:48:08
  11. 5月份Github上最热门的开源项目,已经新鲜出炉了!!

    5月份GitHub上最热门的开源项目排行已经出炉啦,一起来看看上榜详情吧: 1,deno https://github.com/denoland/deno Star 61499 Deno 是 V8 上的安全 TypeScript 运行时。部分特性如下:支持 TypeScript 2.8 开箱即用,使用 V8 6.8.275.3 引擎 无 package.json、npm,不追求兼…...

    2024/5/2 16:05:51
  12. Android Studio开发APP过程中发现的优质参考博客

    作为一个应届毕业生,完成了两个APP的创作之后,现在开始管理收藏夹,所以将收藏夹的文章转移到此处。 显然不是全部搜索的文章,在学习过程中你会搜索很多很多很多很多的文章,然后进行信息筛选和综合。这里只是冰山一角,还有一些确实很优秀但是内容比较精炼,被我吃透之后就…...

    2024/4/16 9:47:16
  13. Capsule Network Performance on Complex Data 翻译

    1. 简介近年来,卷积神经网络(CNN)在深度学习领域中发挥了重要作用。事实证明,CNN的变体在跨不同领域的分类任务中非常成功。但是,CNN的主要缺点有两个:无法考虑要素之间的重要空间层次结构以及缺乏旋转不变性[1]。只要在测试数据中存在某个对象的某些关键特征,CNN便会将…...

    2024/4/16 9:47:36
  14. 一文搞懂nmcli不能tab补全和全部使用方法

    文章目录nmcli命令无法tab补全 解决方案nmcli说明注意事项nmcli命令合集查看合集添加网络连接参数介绍合集添加单网卡实例修改网络连接单项参数介绍合集修改已经配置好的参数测试激活禁用删除等介绍合集总结测试实例添加单网卡实例nmcli做一个team双网卡绑定(链路聚合) nmcli…...

    2024/4/16 9:48:13
  15. 由文章中英文字母出现频率分析破解密文

    原题: 使用频率分析法,尝试破译密文: WB WI KJB MK RMIT BMIQ BJ RASHMWK RMVP YJERYRKB MKD WBI IWOKWXWVMKVR MKDIJYR YNIB URYMWK NKRASHMWKRD BJ OWER M VJYSHRBR RASHMKMBWJK JKR CJNHD PMER BJ LR FNMHWXWRD MKD WKISWURD BJ INVP MK RABRKB BPMB PR VJNHD URMVP BP…...

    2024/4/18 5:30:02
  16. C++设计模式——单例模式(Singleton Pattern)

    C++设计模式——单例模式(Singleton Pattern)微信公众号:幼儿园的学霸目录 文章目录C++设计模式——单例模式(Singleton Pattern)目录定义代码示例懒汉模式线程/内存不安全方式智能指针+双检锁模式和智能指针+call_once模式局部静态变量模式饿汉模式总结参考资料 定义 单例模式…...

    2024/4/15 6:03:04
  17. Vue基础学习

    到公司第一天,把快遗忘的vue再看一遍官网的文档。 Vue.js是什么? Vue.js是一套用于构建用户界面的渐进式框架。 声明式渲染 下面展示一些 内联代码片。 <div id="app"> {{mesage}} </div>var app = new Vue({el:#app,data:{message:Hello Vue!} })结果…...

    2024/4/16 9:48:23
  18. 人人都是产品经理2.0-01章摘要总结

    人人都是产品经理2.0-01章摘要总结1.1从一个小故事谈起(伪需求)1.2产品经理的前世今生1.3思维方式与性格特点1.3.1从学生到职场1.3.2从用户到产品经理1.3.3从现象到本质1.3.4还有什么性格特质是加分项1.4产品经理的日常1.5延伸阅读与练习 1.1从一个小故事谈起(伪需求)反映出…...

    2024/4/19 8:56:40
  19. 实验三面向对象(上)

    实验目的: 1.了解类的成员设计。 2.掌握类与对象的关系。 3. 掌握构造方法的重载及调用、掌握static关键字的、this关键字。 4.了解内部类的特点及使用。 实验内容: 1.编写Java应用程序,定义时钟类Clock,包含3个int型成员变量hour,minute,second,分别表示时,分,秒,…...

    2024/4/24 9:46:45
  20. 三、Linux进阶——Apache(web服务器部署)

    1. Apache的作用 在web被访问时通常使用http://方式 http:// 文本传输协议 http:// 超文本传输协议提供软件: Apache:目前主流 nginx:轻量级 stgw:腾讯 jfe:京东 Tengine:阿里巴巴 2. Apache的安装 dnf install httpd.x86_64 -y3. Apache的启用 1、启用Apache,并设定…...

    2024/4/24 9:46:44

最新文章

  1. 深入解析计算机科学中的时间复杂度与空间复杂度:基础概念、计算方法及Python示例代码详解

    文章目录 1. 时间复杂度2. 计算斐波那契数列2.1 递归解法的时间复杂度2.2 动态规划解法的时间复杂度 3. 空间复杂度 理解时间复杂度和空间复杂度是评估和优化算法性能的关键。这篇文章将详细介绍时间复杂度和空间复杂度的基本概念、计算方法&#xff0c;以及通过Python代码示例…...

    2024/5/5 11:17:59
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. C++ //练习 11.14 扩展你在11.2.1节练习(第378页)中编写的孩子姓到名的map,添加一个pair的vector,保存孩子的名和生日。

    C Primer&#xff08;第5版&#xff09; 练习 11.14 练习 11.14 扩展你在11.2.1节练习&#xff08;第378页&#xff09;中编写的孩子姓到名的map&#xff0c;添加一个pair的vector&#xff0c;保存孩子的名和生日。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#x…...

    2024/5/4 2:31:17
  4. 巨控科技新品发布:全方位升级,引领智能控制新纪元

    标签: #巨控科技 #智能控制 #新品发布 #GRM560 #OPC560 #NET400 在智能控制领域&#xff0c;巨控科技始终以其前沿技术和创新产品引领着市场的潮流。近日&#xff0c;巨控科技再次以其行业领先的研发实力&#xff0c;推出了三大系列的新产品&#xff0c;旨在为各行各业提供更…...

    2024/5/5 9:14:44
  5. 24 个Intellij IDEA好用插件

    24 个Intellij IDEA好用插件 一. 安装插件 Codota 代码智能提示插件 只要打出首字母就能联想出一整条语句&#xff0c;这也太智能了&#xff0c;还显示了每条语句使用频率。 原因是它学习了我的项目代码&#xff0c;总结出了我的代码偏好。 Key Promoter X 快捷键提示插件 …...

    2024/5/4 10:14:12
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/4 23:54:56
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/4 23:54:56
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/4 23:55:17
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/4 23:55:16
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/4 18:20:48
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/4 23:55:06
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/4 23:55:01
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57