PipeWire 音频设计与实现分析三——日志子系统
日志子系统
PipeWire 的日志子系统的设计分为多个层次。PipeWire 用 struct spa_log 对象描述日志组件,用 struct spa_log_methods 对象描述日志组件打印各层级日志的多个方法。PipeWire 为日志子系统添加了 topic 机制,不同文件中的日志按功能以不同的 topic 来打印,以方便针对 topic 的日志分类处理。PipeWire 用 struct spa_log_topic 对象描述日志 topic。日志组件的接口名为SPA_TYPE_INTERFACE_Log Spa:Pointer:Interface:Log。这些结构定义 (位于 pipewire/spa/include/spa/support/log.h) 如下:
#define SPA_LOG_TOPIC_DEFAULT NULLenum spa_log_level {SPA_LOG_LEVEL_NONE = 0,SPA_LOG_LEVEL_ERROR,SPA_LOG_LEVEL_WARN,SPA_LOG_LEVEL_INFO,SPA_LOG_LEVEL_DEBUG,SPA_LOG_LEVEL_TRACE,
};/*** The Log interface*/
#define SPA_TYPE_INTERFACE_Log SPA_TYPE_INFO_INTERFACE_BASE "Log"struct spa_log {/** the version of this log. This can be used to expand this* structure in the future */
#define SPA_VERSION_LOG 0struct spa_interface iface;/*** Logging level, everything above this level is not logged*/enum spa_log_level level;
};/*** \struct spa_log_topic** Identifier for a topic. Topics are string-based filters that logically* group messages together. An implementation may decide to filter different* topics on different levels, for example the "protocol" topic may require* debug level TRACE while the "core" topic defaults to debug level INFO.** spa_log_topics require a spa_log_methods version of 1 or higher.*/
struct spa_log_topic {
#define SPA_VERSION_LOG_TOPIC 0/** the version of this topic. This can be used to expand this* structure in the future */uint32_t version;/** The string identifier for the topic */const char *topic;/** Logging level set for this topic */enum spa_log_level level;/** False if this topic follows the \ref spa_log level */bool has_custom_level;
};struct spa_log_methods {
#define SPA_VERSION_LOG_METHODS 1uint32_t version;/*** Log a message with the given log level.** \note If compiled with this header, this function is only called* for implementations of version 0. For versions 1 and above, see* logt() instead.** \param log a spa_log* \param level a spa_log_level* \param file the file name* \param line the line number* \param func the function name* \param fmt printf style format* \param ... format arguments*/void (*log) (void *object,enum spa_log_level level,const char *file,int line,const char *func,const char *fmt, ...) SPA_PRINTF_FUNC(6, 7);/*** Log a message with the given log level.** \note If compiled with this header, this function is only called* for implementations of version 0. For versions 1 and above, see* logtv() instead.** \param log a spa_log* \param level a spa_log_level* \param file the file name* \param line the line number* \param func the function name* \param fmt printf style format* \param args format arguments*/void (*logv) (void *object,enum spa_log_level level,const char *file,int line,const char *func,const char *fmt,va_list args) SPA_PRINTF_FUNC(6, 0);/*** Log a message with the given log level for the given topic.** \note Callers that do not use topic-based logging (version 0), the \a* topic is NULL** \param log a spa_log* \param level a spa_log_level* \param topic the topic for this message, may be NULL* \param file the file name* \param line the line number* \param func the function name* \param fmt printf style format* \param ... format arguments** \since 1*/void (*logt) (void *object,enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt, ...) SPA_PRINTF_FUNC(7, 8);/*** Log a message with the given log level for the given topic.** \note For callers that do not use topic-based logging (version 0),* the \a topic is NULL** \param log a spa_log* \param level a spa_log_level* \param topic the topic for this message, may be NULL* \param file the file name* \param line the line number* \param func the function name* \param fmt printf style format* \param args format arguments** \since 1*/void (*logtv) (void *object,enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt,va_list args) SPA_PRINTF_FUNC(7, 0);/*** Initializes a \ref spa_log_topic to the correct logging level.** \since 1*/void (*topic_init) (void *object, struct spa_log_topic *topic);
};
PipeWire 的 SPA 日志系统提供了许多宏,来以不同的日志等级和 topic,向 spa_log 打印日志,这些宏定义 (位于 pipewire/spa/include/spa/support/log.h) 如下:
/* Unused, left for backwards compat */
#define spa_log_level_enabled(l,lev) ((l) && (l)->level >= (lev))#define spa_log_level_topic_enabled(l,topic,lev) \
({ \struct spa_log *_log = l; \enum spa_log_level _lev = _log ? _log->level : SPA_LOG_LEVEL_NONE; \struct spa_log_topic *_t = (struct spa_log_topic *)topic; \if (_t && _t->has_custom_level) \_lev = _t->level; \_lev >= lev; \
})/* Transparently calls to version 0 log if v1 is not supported */
#define spa_log_logt(l,lev,topic,...) \
({ \struct spa_log *_l = l; \struct spa_interface *_if = &_l->iface; \if (SPA_UNLIKELY(spa_log_level_topic_enabled(_l, topic, lev))) { \if (!spa_interface_call(_if, \struct spa_log_methods, logt, 1, \lev, topic, \__VA_ARGS__)) \spa_interface_call(_if, \struct spa_log_methods, log, 0, \lev, __VA_ARGS__); \} \
})/* Transparently calls to version 0 logv if v1 is not supported */
#define spa_log_logtv(l,lev,topic,...) \
({ \struct spa_log *_l = l; \struct spa_interface *_if = &_l->iface; \if (SPA_UNLIKELY(spa_log_level_topic_enabled(_l, topic, lev))) { \if (!spa_interface_call(_if, \struct spa_log_methods, logtv, 1, \lev, topic, \__VA_ARGS__)) \spa_interface_call(_if, \struct spa_log_methods, logv, 0, \lev, __VA_ARGS__); \} \
})#define spa_log_log(l,lev,...) \spa_log_logt(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__)#define spa_log_logv(l,lev,...) \spa_log_logtv(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__)#define spa_log_error(l,...) spa_log_log(l,SPA_LOG_LEVEL_ERROR,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_log_warn(l,...) spa_log_log(l,SPA_LOG_LEVEL_WARN,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_log_info(l,...) spa_log_log(l,SPA_LOG_LEVEL_INFO,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_log_debug(l,...) spa_log_log(l,SPA_LOG_LEVEL_DEBUG,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_log_trace(l,...) spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__)#define spa_logt_error(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_ERROR,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_logt_warn(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_WARN,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_logt_info(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_INFO,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_logt_debug(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_DEBUG,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_logt_trace(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_TRACE,t,__FILE__,__LINE__,__func__,__VA_ARGS__)#ifndef FASTPATH
#define spa_log_trace_fp(l,...) spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__)
#else
#define spa_log_trace_fp(l,...)
#endif
这些宏最终通过 struct spa_log_methods 的方法打印日志。
PipeWire 的代码通常通过另一组日志宏 (也称为 pipewire 日志宏) 打印日志,如 pw_log_info() 和 pw_log_warn(),这组宏定义 (位于 pipewire/src/pipewire/log.h) 如下:
/** Log a message for a topic */
void
pw_log_logt(enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line, const char *func,const char *fmt, ...) SPA_PRINTF_FUNC(6, 7);/** Log a message for a topic */
void
pw_log_logtv(enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line, const char *func,const char *fmt, va_list args) SPA_PRINTF_FUNC(6, 0);/** Log a message for the default topic */
void
pw_log_log(enum spa_log_level level,const char *file,int line, const char *func,const char *fmt, ...) SPA_PRINTF_FUNC(5, 6);/** Log a message for the default topic */
void
pw_log_logv(enum spa_log_level level,const char *file,int line, const char *func,const char *fmt, va_list args) SPA_PRINTF_FUNC(5, 0);. . . . . .
/** Check if a loglevel is enabled */
#define pw_log_level_enabled(lev) (pw_log_level >= (lev))
#define pw_log_topic_enabled(lev,t) ((t) && (t)->has_custom_level ? (t)->level >= (lev) : pw_log_level_enabled((lev)))#define pw_logtv(lev,topic,fmt,ap) \
({ \if (SPA_UNLIKELY(pw_log_topic_enabled(lev,topic))) \pw_log_logtv(lev,topic,__FILE__,__LINE__,__func__,fmt,ap); \
})#define pw_logt(lev,topic,...) \
({ \if (SPA_UNLIKELY(pw_log_topic_enabled(lev,topic))) \pw_log_logt(lev,topic,__FILE__,__LINE__,__func__,__VA_ARGS__); \
})#define pw_log(lev,...) pw_logt(lev,PW_LOG_TOPIC_DEFAULT,__VA_ARGS__)#define pw_log_error(...) pw_log(SPA_LOG_LEVEL_ERROR,__VA_ARGS__)
#define pw_log_warn(...) pw_log(SPA_LOG_LEVEL_WARN,__VA_ARGS__)
#define pw_log_info(...) pw_log(SPA_LOG_LEVEL_INFO,__VA_ARGS__)
#define pw_log_debug(...) pw_log(SPA_LOG_LEVEL_DEBUG,__VA_ARGS__)
#define pw_log_trace(...) pw_log(SPA_LOG_LEVEL_TRACE,__VA_ARGS__)#define pw_logt_error(t,...) pw_logt(SPA_LOG_LEVEL_ERROR,t,__VA_ARGS__)
#define pw_logt_warn(t,...) pw_logt(SPA_LOG_LEVEL_WARN,t,__VA_ARGS__)
#define pw_logt_info(t,...) pw_logt(SPA_LOG_LEVEL_INFO,t,__VA_ARGS__)
#define pw_logt_debug(t,...) pw_logt(SPA_LOG_LEVEL_DEBUG,t,__VA_ARGS__)
#define pw_logt_trace(t,...) pw_logt(SPA_LOG_LEVEL_TRACE,t,__VA_ARGS__)#ifndef FASTPATH
#define pw_log_trace_fp(...) pw_log(SPA_LOG_LEVEL_TRACE,__VA_ARGS__)
#else
#define pw_log_trace_fp(...)
#endif
这组宏是对一组日志函数的封装。这组日志函数定义 (位于 pipewire/src/pipewire/log.c) 如下:
SPA_LOG_IMPL(default_log);#define DEFAULT_LOG_LEVEL SPA_LOG_LEVEL_WARNSPA_EXPORT
enum spa_log_level pw_log_level = DEFAULT_LOG_LEVEL;static struct spa_log *global_log = &default_log.log;. . . . . .
/** Log a message for the given topic* \param level the log level* \param topic the topic* \param file the file this message originated from* \param line the line number* \param func the function* \param fmt the printf style format* \param ... printf style arguments to log**/
SPA_EXPORT
void
pw_log_logt(enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt, ...)
{if (SPA_UNLIKELY(pw_log_topic_enabled(level, topic))) {va_list args;va_start(args, fmt);spa_log_logtv(global_log, level, topic, file, line, func, fmt, args);va_end(args);}
}/** Log a message for the given topic with va_list* \param level the log level* \param topic the topic* \param file the file this message originated from* \param line the line number* \param func the function* \param fmt the printf style format* \param args a va_list of arguments**/
SPA_EXPORT
void
pw_log_logtv(enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt,va_list args)
{spa_log_logtv(global_log, level, topic, file, line, func, fmt, args);
}/** Log a message for the default topic with va_list* \param level the log level* \param file the file this message originated from* \param line the line number* \param func the function* \param fmt the printf style format* \param args a va_list of arguments**/
SPA_EXPORT
void
pw_log_logv(enum spa_log_level level,const char *file,int line,const char *func,const char *fmt,va_list args)
{pw_log_logtv(level, PW_LOG_TOPIC_DEFAULT, file, line, func, fmt, args);
}/** Log a message for the default topic* \param level the log level* \param file the file this message originated from* \param line the line number* \param func the function* \param fmt the printf style format* \param ... printf style arguments to log**/
SPA_EXPORT
void
pw_log_log(enum spa_log_level level,const char *file,int line,const char *func,const char *fmt, ...)
{va_list args;va_start(args, fmt);pw_log_logtv(level, PW_LOG_TOPIC_DEFAULT, file, line, func, fmt, args);va_end(args);
}
这组日志函数,主要封装了对 SPA 日志宏的调用。当然在调用 SPA 日志宏之前,会先检查要打印的日志的日志等级,按照日志等级设置是否需要打印。此外,日志组件实现 spa_log 取全局日志组件。
PipeWire 代码通过 pipewire 日志宏或 pipewire 日志函数打印日志。
PipeWire 的日志组件实现有多个,默认的日志组件实现 (位于 pipewire/spa/include/spa/support/log-impl.h) 如下:
static inline SPA_PRINTF_FUNC(7, 0) void spa_log_impl_logtv(void *object,enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt,va_list args)
{static const char * const levels[] = { "-", "E", "W", "I", "D", "T" };const char *basename = strrchr(file, '/');char text[512], location[1024];char topicstr[32] = {0};if (basename)basename += 1; /* skip '/' */elsebasename = file; /* use whole string if no '/' is found */if (topic && topic->topic)snprintf(topicstr, sizeof(topicstr), " %s ", topic->topic);vsnprintf(text, sizeof(text), fmt, args);snprintf(location, sizeof(location), "[%s]%s[%s:%i %s()] %s\n",levels[level],topicstr,basename, line, func, text);fputs(location, stderr);
}static inline SPA_PRINTF_FUNC(7,8) void spa_log_impl_logt(void *object,enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt, ...)
{va_list args;va_start(args, fmt);spa_log_impl_logtv(object, level, topic, file, line, func, fmt, args);va_end(args);
}static inline SPA_PRINTF_FUNC(6, 0) void spa_log_impl_logv(void *object,enum spa_log_level level,const char *file,int line,const char *func,const char *fmt,va_list args)
{spa_log_impl_logtv(object, level, NULL, file, line, func, fmt, args);
}static inline SPA_PRINTF_FUNC(6,7) void spa_log_impl_log(void *object,enum spa_log_level level,const char *file,int line,const char *func,const char *fmt, ...)
{va_list args;va_start(args, fmt);spa_log_impl_logv(object, level, file, line, func, fmt, args);va_end(args);
}static inline void spa_log_impl_topic_init(void *object, struct spa_log_topic *topic)
{/* noop */
}#define SPA_LOG_IMPL_DEFINE(name) \
struct { \struct spa_log log; \struct spa_log_methods methods; \
} name#define SPA_LOG_IMPL_INIT(name) \{ { { SPA_TYPE_INTERFACE_Log, SPA_VERSION_LOG, \SPA_CALLBACKS_INIT(&name.methods, &name) }, \SPA_LOG_LEVEL_INFO, }, \{ SPA_VERSION_LOG_METHODS, \spa_log_impl_log, \spa_log_impl_logv, \spa_log_impl_logt, \spa_log_impl_logtv, \} }#define SPA_LOG_IMPL(name) \SPA_LOG_IMPL_DEFINE(name) = SPA_LOG_IMPL_INIT(name)
日志组件实现定义 struct spa_log_methods 对象及其各个方法,以及 struct spa_log 对象。日志组件的使用者通过 struct spa_log 对象的 iface.cb.funcs 字段获得它实现的 struct spa_log_methods 对象,获得它提供的各个打印日志的方法。
这个默认实现将日志打印到标准输出。日志组件实现更常见的是从支持库插件或 journal 插件加载。
PipeWire 通过一个全局对象维护默认使用的日志组件实现,并提供一些接口来操作 (位于 pipewire/src/pipewire/log.c) 该对象,如:
SPA_EXPORT
void pw_log_set(struct spa_log *log)
{global_log = log ? log : &default_log.log;global_log->level = pw_log_level;
}bool pw_log_is_default(void)
{return global_log == &default_log.log;
}/** Get the global log interface* \return the global log*/
SPA_EXPORT
struct spa_log *pw_log_get(void)
{return global_log;
}/** Set the global log level* \param level the new log level*/
SPA_EXPORT
void pw_log_set_level(enum spa_log_level level)
{pw_log_level = level;global_log->level = level;
}
PipeWire 还提供接口初始化 (位于 pipewire/src/pipewire/log.c) 日志 topic,如:
SPA_EXPORT
struct spa_log_topic *PW_LOG_TOPIC_DEFAULT;PW_LOG_TOPIC_STATIC(log_topic, "pw.log"); /* log topic for this file here */
PW_LOG_TOPIC(log_buffers, "pw.buffers");
PW_LOG_TOPIC(log_client, "pw.client");
PW_LOG_TOPIC(log_conf, "pw.conf");
PW_LOG_TOPIC(log_context, "pw.context");
PW_LOG_TOPIC(log_core, "pw.core");
PW_LOG_TOPIC(log_data_loop, "pw.data-loop");
PW_LOG_TOPIC(log_device, "pw.device");
PW_LOG_TOPIC(log_factory, "pw.factory");
PW_LOG_TOPIC(log_filter, "pw.filter");
PW_LOG_TOPIC(log_global, "pw.global");
PW_LOG_TOPIC(log_link, "pw.link");
PW_LOG_TOPIC(log_loop, "pw.loop");
PW_LOG_TOPIC(log_main_loop, "pw.main-loop");
PW_LOG_TOPIC(log_mem, "pw.mem");
PW_LOG_TOPIC(log_metadata, "pw.metadata");
PW_LOG_TOPIC(log_module, "pw.module");
PW_LOG_TOPIC(log_node, "pw.node");
PW_LOG_TOPIC(log_port, "pw.port");
PW_LOG_TOPIC(log_properties, "pw.props");
PW_LOG_TOPIC(log_protocol, "pw.protocol");
PW_LOG_TOPIC(log_proxy, "pw.proxy");
PW_LOG_TOPIC(log_resource, "pw.resource");
PW_LOG_TOPIC(log_stream, "pw.stream");
PW_LOG_TOPIC(log_thread_loop, "pw.thread-loop");
PW_LOG_TOPIC(log_work_queue, "pw.work-queue");PW_LOG_TOPIC(PW_LOG_TOPIC_DEFAULT, "default");. . . . . .void
pw_log_init(void)
{PW_LOG_TOPIC_INIT(PW_LOG_TOPIC_DEFAULT);PW_LOG_TOPIC_INIT(log_buffers);PW_LOG_TOPIC_INIT(log_client);PW_LOG_TOPIC_INIT(log_conf);PW_LOG_TOPIC_INIT(log_context);PW_LOG_TOPIC_INIT(log_core);PW_LOG_TOPIC_INIT(log_data_loop);PW_LOG_TOPIC_INIT(log_device);PW_LOG_TOPIC_INIT(log_factory);PW_LOG_TOPIC_INIT(log_filter);PW_LOG_TOPIC_INIT(log_global);PW_LOG_TOPIC_INIT(log_link);PW_LOG_TOPIC_INIT(log_loop);PW_LOG_TOPIC_INIT(log_main_loop);PW_LOG_TOPIC_INIT(log_mem);PW_LOG_TOPIC_INIT(log_metadata);PW_LOG_TOPIC_INIT(log_module);PW_LOG_TOPIC_INIT(log_node);PW_LOG_TOPIC_INIT(log_port);PW_LOG_TOPIC_INIT(log_properties);PW_LOG_TOPIC_INIT(log_protocol);PW_LOG_TOPIC_INIT(log_proxy);PW_LOG_TOPIC_INIT(log_resource);PW_LOG_TOPIC_INIT(log_stream);PW_LOG_TOPIC_INIT(log_thread_loop);PW_LOG_TOPIC_INIT(log_topic);PW_LOG_TOPIC_INIT(log_work_queue);
}
各个需要打印日志的源文件,定义 PW_LOG_TOPIC_DEFAULT 宏选择所要用的日志 topic,如:
PW_LOG_TOPIC_EXTERN(log_context);
#define PW_LOG_TOPIC_DEFAULT log_context
则 PipeWire 打印日志的方法,分为如下四层:
- PipeWire 日志打印宏。许多代码会直接用它们打印日志。封装 PipeWire 日志打印函数。
- PipeWire 日志打印函数。部分代码会直接用它们打印日志。封装 SPA 日志打印宏,通过全局日志打印组件打印日志。
- SPA 日志打印宏。插件代码会直接用它们打印日志。通过日志打印组件实现打印日志。
- 日志打印组件实现。
全局日志打印组件管理:通过一个全局对象维护,提供接口来设置和获取。
日志打印组件实现:有一个内置的实现,但大多通过插件实现,如支持库插件和 journal 插件。
相关文章:
PipeWire 音频设计与实现分析三——日志子系统
日志子系统 PipeWire 的日志子系统的设计分为多个层次。PipeWire 用 struct spa_log 对象描述日志组件,用 struct spa_log_methods 对象描述日志组件打印各层级日志的多个方法。PipeWire 为日志子系统添加了 topic 机制,不同文件中的日志按功能以不同的…...
TypeScript vs. JavaScript:技术对比与核心差异解析
引言 在 Web 前端开发领域,JavaScript(JS)长期占据主导地位,但随着项目复杂度的提升,开发者逐渐面临维护性差、协作困难等问题。TypeScript(TS)作为 JavaScript 的超集,通过静态类型…...
关于 @Autowired 和 @Value 使用 private 字段的警告问题分析与解决方案
问题背景 在使用 Spring 框架进行开发时,我们经常会使用 Autowired 和 Value 注解来进行依赖注入和属性值注入。然而,当我们将这些注解应用于 private 字段时,IDE(如 IntelliJ IDEA)可能会显示警告信息,提…...
MySQL 进阶 面经级
会用数据库,找大厂工作是远远不够的。 本人2025美团暑期AI面试好几个MySQL场景问题不会答,已脏面评。遂在此整理学习! 文章目录 分片分区分区语法范围分区 (RANGE Partitioning)列表分区(LIST Partitionin…...
《C奥林匹斯宝典:基础篇 - 重载函数》
一、重载函数 (一)函数模板重载 详细解析:函数模板提供了一种通用的函数定义方式,可针对不同类型进行实例化。当存在函数模板与普通函数、其他函数模板同名时,就构成了函数模板重载。编译器在编译阶段,依…...
【408--考研复习笔记】计算机网络----知识点速览
目录 一、计算机网络体系结构 1.计算机网络的定义与功能: 2.网络体系结构相关概念: 3.OSI 七层模型与 TCP/IP 模型: 4.通信方式与交换技术: 电路交换 报文交换 分组交换 5.端到端通信和点到点通信: 6.计算机…...
TiDB 可观测性解读(二)丨算子执行信息性能诊断案例分享
导读 可观测性已经成为分布式系统成功运行的关键组成部分。如何借助多样、全面的数据,让架构师更简单、高效地定位问题、分析问题、解决问题,已经成为业内的一个技术焦点。本系列文章将深入解读 TiDB 的关键参数,帮助大家更好地观测系统的状…...
15:00开始面试,15:08就出来了,问的问题有点变态。。。
从小厂出来,没想到在另一家公司又寄了。 到这家公司开始上班,加班是每天必不可少的,看在钱给的比较多的份上,就不太计较了。没想到8月一纸通知,所有人不准加班,加班费不仅没有了,薪资还要降40%…...
蓝桥杯准备(前缀和差分)
import java.util.Scanner; public class qianzhuihe {public static void main(String[] args) {int N,M;Scanner scnew Scanner(System.in);Nsc.nextInt();Msc.nextInt();int []treesnew int[N1];//设为N1的意义,防止越界int []prefixSumnew int[N1];for(int i1;i…...
试用thymeleaf引入vue-element-admin(一)
作为后端程序员,一直使用springbootbootstarp做管理系统,对前端不是太了解,现在感觉bootstarp的admin ui一直不得劲,想切换成前端使用较多的ui,费了老鼻子劲。 我的目的不是前后端分离,而是一个人全栈&…...
Minimind 训练一个自己专属语言模型
发现了一个宝藏项目, 宣传是完全从0开始,仅用3块钱成本 2小时!即可训练出仅为25.8M的超小语言模型MiniMind,最小版本体积是 GPT-3 的 17000,做到最普通的个人GPU也可快速训练 https://github.com/jingyaogong/minimi…...
C++11QT复习 (七)
智能指针雏形 **Day7-1 智能指针雏形:独占语义与共享语义****1. 独占语义与共享语义****1.1 Circle 类:示例类** **2. 拷贝构造:独占语义(Unique Ownership)****2.1 代码解析** **3. 拷贝构造:共享语义&…...
STM32八股【5】----- TIM定时器
1. TIM定时器分类 STM32 的定时器主要分为以下几类: 高级定时器(Advanced TIM,TIM1/TIM8) 具备 PWM 生成、死区控制、互补输出等高级功能,适用于电机控制和功率转换应用。通用定时器(General-purpose TIM…...
单元测试之Arrange-Act-Assert(简称AAA)
Arrange-Act-Assert(简称AAA)是一种编写单元测试的标准模式,具有清晰的结构和明确的步骤,有助于提高测试的可读性、可维护性和可扩展性。以下是对每个步骤的详细说明: 1. Arrange(准备阶段) 在…...
厘米级定位赋能智造升级:品铂科技UWB技术驱动工厂全流程自动化与效能跃升”
在智能制造中的核心价值体现在高精度定位、流程优化、安全管理等多个维度,具体应用如下: 一、核心技术与定位能力 厘米级高精度定位 UWB技术通过纳秒级窄脉冲信号(带宽超500MHz)实现高时间分辨率,结合…...
C++刷题(四):vector
📝前言说明: 本专栏主要记录本人的基础算法学习以及刷题记录,使用语言为C。 每道题我会给出LeetCode上的题号(如果有题号),题目,以及最后通过的代码。没有题号的题目大多来自牛客网。对于题目的…...
学习记录706@微信小程序+springboot项目 真机测试 WebSocket错误: {errMsg: Invalid HTTP status.}连接不上
我微信小程序springboot项目 真机测试 websocket 总是报错 WebSocket错误: {errMsg: Invalid HTTP status.},总是连接不上,但是开发者工具测试就没有问题。 最后解决方案是编码token,之前是没有编码直接拼接的,原因不详。 consol…...
【虚拟仪器技术】Labview虚拟仪器技术应用教程习题参考答案[13页]
目录 第1章 第2章 第3章 第4章 第5章 第6章 第7章 第8章 第1章 1. 简述虚拟仪器概念。 参考答案:虚拟仪器是借助于强大的计算机软件和硬件环境的支持,建立虚拟的测试仪器面板,完成仪器的控制、数…...
【工作梳理】怎么把f12里面的东西导入到postman
postman左上角导入 结果:...
UE5学习笔记 FPS游戏制作34 触发器切换关卡
文章目录 搭建关卡制作触发器传送门显示加载界面 搭建关卡 首先搭建两个关卡,每个关卡里至少要有一个角色 制作触发器传送门 1 新建一个蓝图,父类为actor,命名为portal(传送门) 2 为portal添加一个staticMesh&#…...
智谱大模型(ChatGLM3)PyCharm的调试指南
前言 最近在看一本《ChatGLM3大模型本地化部署、应用开发和微调》,本文就是讨论ChatGLM3在本地的初步布设。(模型文件来自魔塔社区) 1、建立Pycharm工程 采用的Python版本为3.11 2、安装对应的包 2.1、安装modelscope包 pip install model…...
新专栏预告 《AI大模型应知应会短平快系列100篇》 - 整体规划设计
做个预告,为系统化梳理AI大模型的发展脉络,并为普及AI素养做一点贡献,特给自己制定了一个小目标,3个月内完成交稿。 AI大模型应知应会短平快系列100篇 - 整体规划设计 一、基础知识模块(20篇) 1.1 大模型…...
SwanLab Slack通知插件:让AI训练状态同步更及时
在AI模型训练的过程中,开发者常常面临一个难题:如何及时跟踪训练状态?无论是实验超参数的调整、关键指标的变化,还是意外中断的告警,传统的监控方式往往依赖手动刷新日志或反复检查终端,这不仅效率低下&…...
收集 的 JavaScript 数组方法表格
这个表格可以作为数组方法的快速参考指南 方法名对应版本功能原数组是否改变返回值类型concat()ES5-合并数组,并返回合并之后的新数组nArrayjoin()ES5-使用分隔符,将数组转为字符串并返回nStringpop()ES5-删除最后一位,并返回删除的数据yAny…...
操作系统高频(六)linux内核
操作系统高频(六)linux内核 1.内核态,用户态的区别⭐⭐⭐ 内核态和用户态的区别主要在于权限和安全性。 权限:内核态拥有最高的权限,可以访问和执行所有的系统指令和资源,而用户态的权限相对较低&#x…...
位置编码汇总 # 持续更新
看了那么多还没有讲特别好的,GPT老师讲的不错关于三角函数编码。 一、 手撕transformer常用三角位置编码 GPT说:“低维度的编码(例如,第一个维度)可以捕捉到大的位置差异,而高维度的编码则可以捕捉到小的细…...
电阻(Resistor)详解
一、电阻的定义与核心作用 电阻是电子电路中用于 限制电流、分压、调节信号电平、消耗功率 的基础被动元件,其阻值(Resistance)单位为欧姆(Ω)。其核心作用可归纳为: 限流保护:防止元器件过电流…...
DaVinci Resolve19.1下载:达芬奇调色中文版+安装步骤
如大家所了解的,DaVinci Resolve中文名为达芬奇,是一款专业视频编辑与调色软件。它最初以调色功能闻名,但经过多年发展,已扩展为一套完整的后期制作解决方案,涵盖了剪辑、视觉特效、动态图形和音频后期制作等多个模块。…...
文件IO 2
补充一些用到前面没提到的方法 isDirectory()方法,检查一个对象是否是文件夹,是true不是false isFile()方法,检测一个对象是否为文件,是true不是false 文件的读写操作实践 上一篇大致讲了文件读写操作的基本操作,下面是实践时…...
【Word】批注一键导出:VBA 宏
📌 VBA 宏代码实现 下面是完整的 VBA 代码,支持: 自动创建新文档,并将当前 Word 文档的所有批注导出。批注格式清晰,包括编号、作者、日期和批注内容。智能检测,如果当前文档没有批注,则提示用…...
