1.PostgreSQL-源码学习笔记(5)-索引
2.EasyLogger源码学习笔记(5)
3.程序员一般是记笔记源怎么记自己的编程笔记的?用什么软件记录的?
4.Mobx源码阅读笔记——3. proxy 还是defineProperty,劫持对象行为的码线两个方案
5.记笔记的软件(vnote)
6.EasyLogger源码学习笔记(1)
PostgreSQL-源码学习笔记(5)-索引
索引是数据库中的关键结构,它加速了查询速度,笔记尽管会增加内存和维护成本,源码但效益通常显著。记笔记源在PG中,码线美化易支付网站源码索引类型丰富多样,笔记包括B-Tree、源码Hash、记笔记源GIST、码线SP-GIST、笔记GIN和BGIN。源码所有索引本质上都是记笔记源独立的数据结构,与数据表并存。码线
查询时,笔记没有索引会导致全表扫描,效率低下。创建索引可以快速定位满足条件的元组,显著提升查询性能。PG中的索引操作函数,如pg_am中的注册,为上层模块提供了一致的接口,这些函数封装在IndexAmRoutine和IndexScanDesc中。
B-Tree索引采用Lehman和Yao的算法,每个非根节点有兄弟指针,页面包含"high key",用于快速扫描。PG的B-Tree构建和维护流程涉及BTBuildState、spool、元页信息等结构,包括创建、插入、扫描等操作。
哈希索引在硬盘上实现,支持故障恢复。它的页面结构复杂,包括元页、如何阅读源码函数桶页、溢出页和位图页。插入和扫描索引元组时,需要动态管理元页缓存以提高效率。
GiST和GIN索引提供了更大的灵活性,支持用户自定义索引方法。GiST适用于通用搜索,而GIN专为复合值索引设计,支持全文搜索。它们在创建时需要实现特定的访问方法和函数。
尽管索引维护有成本,但总体上,它们对提高查询速度的价值不可忽视。了解并有效利用索引是数据库优化的重要环节。
EasyLogger源码学习笔记(5)
在EasyLogger源码的学习中,我们了解到日志对象使用了互斥锁以确保同一时刻只有一个线程能进行操作,保证了日志管理的安全性与高效性。
对于异步输出,EasyLogger通过信号量实现了优化。当需要等待执行时,某个线程会被阻塞,以减少CPU的占用。这一特性允许用户单独设置异步输出的日志等级,提高系统的灵活性与可控性。
在文件输出时,使用了信号量集合,其中仅包含一个信号量。这一设计确保了同时只有一个线程能向文件中写入日志,避免了多线程并发写入导致的文件混乱。
日志输出的多样选择体现了EasyLogger的灵活性,无论是输出到文件还是串口,都可以根据需要配置是否采用异步输出,以适应不同的应用场景与性能需求。
此外,sem_post函数用于解锁由semby指定的信号量,执行对特定信号量的燕窩溯源码解锁操作。而semop函数则用于执行一组预先定义的信号量操作,适用于对多个信号量进行原子性操作。
在信号量集合仅包含一个信号量的情况下,使用sem_post函数进行操作可能直接替代使用semop函数。这一设计简化了信号量管理,提高了代码的可读性和效率。
程序员一般是怎么记自己的编程笔记的?用什么软件记录的?
在编程领域,记笔记是提高效率、加深理解、追踪项目进展的关键。本文旨在探讨程序员如何高效地记笔记,以及他们通常使用哪些软件来实现这一目标。市面上的笔记工具琳琅满目,但要找到最适合自己的却并非易事。
小林是本文的主人公,他分享了自己在探索各类笔记工具的过程中,最终选择了语雀作为自己的首选平台。原因在于,语雀充分考虑到程序员的需求,提供了许多专为开发者设计的功能。接下来,我们一同探索语雀中几个小林常用且十分实用的功能。
语雀基于markdown开发,支持markdown语法,这使得习惯于markdown的程序员可以无缝使用。不仅如此,语雀引入了“万能的斜杠”功能,用户只需输入斜杠,就可以轻松访问一系列实用功能,如同哆啦A梦的口袋般便利。
在小林的使用体验中,他特别赞赏语雀对代码块的处理。语雀不仅支持多种编程语言的渲染,还能调整代码块的显示风格,以及自动进行代码缩进,这在有道云笔记中是nft源码定制开发缺失的功能。对于程序员来说,这样的支持极大地提升了编写和阅读代码的体验。
在画图方面,语雀直接在文档中提供画图功能,省去了在其他软件中绘制后再插入的繁琐步骤。对于需要大量使用图形的程序员来说,这一功能极大地提高了效率。语雀支持绘制思维导图、流程图、UML图和自由画板,还允许在文档中直接编辑思维导图内容,插入流程图卡片,并提供画图工具和调色功能。
对于经常与数学公式打交道的程序员,语雀在数学公式输入方面也提供了便利,支持Markdown和katex两种方式,方便调节公式的样式。此外,语雀在文档中插入视频的功能也让人眼前一亮,可以在文档中直接加入B站的视频,为阅读体验增添更多维度。
大纲功能是小林发现的另一个亮点。语雀能够根据文档中的标题层级关系,清晰显示大纲,使程序员能够一目了然地掌握文档内容,通过点击标题即可快速定位到相应部分。这一功能极大提升了查找信息的效率。
语雀还提供分组功能,帮助程序员整理系列文章,构建层次分明的结构,使得系列文章之间的关联性更加清晰。通过分组,程序员可以轻松管理相关文档,增强信息组织的条理性。
小林特别提到,语雀的小牛线指标源码“万能的斜杠”功能和大纲功能,使它超越了传统图文笔记应用,成为一个集制作、音视频接入等多功能于一身的笔记平台。这一特性极大地丰富了笔记的表达方式,让程序员在记笔记的过程中能够更加游刃有余。
除了上述功能,语雀还允许用户打造个人知识库,将各类文章组织成独立的知识库,并可将这些知识库发布到网页上,形成个人博客式的展示。这不仅便于个人回顾和整理知识,也便于与他人分享。
总结而言,语雀以其专为程序员设计的功能、强大的和音视频支持、清晰的文档结构展示以及知识库构建能力,成为了小林以及众多程序员高效记笔记的首选平台。通过“万能的斜杠”功能、制作、音视频接入、大纲功能和知识库构建,语雀为程序员提供了全面的笔记解决方案,极大地提升了编程学习和项目管理的效率。
Mobx源码阅读笔记——3. proxy 还是defineProperty,劫持对象行为的两个方案
这篇文章将深入分析 MobX 的 observableObject 数据类型的源码,同时探讨使用 Proxy 和 Object.defineProperty 这两种实现方案来劫持对象行为的策略。通过分析,我们能够理解 MobX 在创建 observableObject 时是如何同时采用这两种方案,并在创建时决定使用哪一种。
首先,回顾 observableArray 的实现方式,通过 Proxy 代理数组的行为,转发给 ObservableArrayAdministration 来实现响应式修改的逻辑。同样,我们已经讨论过 observableValue 的实现,通过一个特殊的类 ObservableValue 直接使用其方法,无需代理。
对于 observableObject 的实现机制,其特点在于同时采用了上述两种方案,并且在创建时决定使用哪一种。让我们回到文章中提到的工厂方法,其中根据 options.proxy 的值来决定使用哪一种方案。
在 options.proxy 为 false 的情况下,使用第一条路径来实现 observableObject。这通过直接返回 extendObservable 的结果,其中 extendObservable 是一个工具函数,用于向已存在的目标对象添加 observable 属性。属性映射中的所有键值对都会导致目标上生成新的 observable 属性,并且属性映射中的任意 getters 会被转化为计算属性。
这里首先根据 options 参数选择特定的 decorator,这个过程与之前在第一篇文章中通过 options 参数选择特定的 enhancer 类似。实际上,这里的 decorator 起到了类似的作用,甚至在创建 decorator 这个过程本身也需要通过 enhancer 参数。
至于 decorator 和 enhancer 之间的耦合机制,文章中详细解释了 createDecoratorForEnhancer 和 createPropDecorator 函数,通过这些函数我们能够了解到它们是如何将 decorator 和 enhancer 联系起来的。
接下来,文章深入分析了 decorator 的作用机制,包括它如何决定是否立即执行,以及在不立即执行时如何将创建 prop 的相关信息保存下来。通过 initializeInstance 函数,我们了解了如何解决 # 问题,这涉及到如何正确处理那些在创建时未被立即执行的 prop。
最终,通过为 target 对象创建 ObservableObjectAdministration 管理对象,并通过 $mobx 和 target 属性将它们关联起来,我们完成了 observableObject 的创建。如果传入的 properties 不为空,则使用 extendObservableObjectWithProperties 来初始化。这里的代码逻辑相对简单,主要遍历 properties 中的所有键并调用对应的 decorator。
文章还指出,虽然在第一条路径中,使用 Object.defineProperty 重写了 prop 的 getter 和 setter,但在 MobX 4 及以下版本中,使用 Proxy 来实现 observableObject 的逻辑更为常见。Proxy 特性在 ES6 引入后,提供了更强大的能力来劫持对象的行为,不仅限于 getter 和 setter,还包括对象的其他行为。
最后,文章总结了使用 Proxy 方案的优点,包括能够更全面地劫持对象的行为,而不仅仅是属性的 getter 和 setter。Proxy 方案在实现双向绑定时,能够提供更灵活和强大的功能。同时,文章也提到了两种方案的局限性,尤其是在处理对象属性的可观察性方面,Proxy 方案在某些情况下可能更具优势。
记笔记的软件(vnote)
今天,我们将探讨如何在Ubuntu系统中安装并使用一款名为Vnote的开源跨平台记笔记软件。Vnote不仅免费,而且功能强大,是许多用户在编写文章、学习和工作中进行笔记记录的首选。如果你对Markdown语法不熟悉,没关系,Vnote提供了详细的使用指南,帮助用户在边记笔记边学习的过程中逐步掌握其魅力。
在开始介绍Vnote之前,让我们先了解一下AppImage格式的Linux软件包。AppImage是一种方便的Linux应用打包方式,它允许用户在无需安装额外软件的情况下直接运行应用,类似于Windows中的绿色软件。通过AppImage,开发者可以一次性打包程序,实现跨平台兼容,覆盖主流桌面系统。
接下来,我们将演示如何通过AppImage格式在Ubuntu系统中安装Vnote笔记软件。首先,访问Vnote GitHub页面下载AppImage格式的最新版本(目前为V2.3)。下载完成后,将文件保存到易于访问的位置,如创建一个名为“soft”的文件夹,并将AppImage文件移动至该文件夹内。通过终端或文件管理器中的快捷方式给该文件添加执行权限。
为了方便日常使用,我们还可以将Vnote添加到Ubuntu的Dock中。使用终端执行以下命令:`cd /usr/share/applications/`,然后创建或编辑Vnote的桌面快捷方式`sudo gedit vnote.desktop`。在文本编辑器中输入相应的配置信息,包括图标路径和执行命令,确保桌面快捷方式能够指向正确的AppImage文件。保存并退出文本编辑器后,重启Dock或使用快捷键在Dock中搜索Vnote,即可将其固定在Dock上,便于快速访问。
对于那些喜欢探索和自定义的用户,还提供了一种高级安装方法:编译源码安装。然而,考虑到篇幅限制,这里不再详细介绍编译过程,建议直接参考Vnote开发者提供的详细构建文档。通过阅读原文链接,用户可以跟随文档指导,完成从源码到可执行程序的完整构建过程,从而获得更深入的了解和自定义权限。
Vnote以其用户友好性、强大的Markdown支持和免费的特性,为Ubuntu用户提供了高效便捷的笔记记录工具。通过上述安装指南,无论是初学者还是高级用户,都能轻松上手,充分利用Vnote的强大功能,提高学习和工作效率。
EasyLogger源码学习笔记(1)
在编程中,预处理器通过宏定义执行特定的逻辑。使用`#ifdef`和`#else`可以实现条件编译。当`#ifdef _XXXX`中的标识符_XXXX被`#define`命令定义时,编译器将执行`#ifdef`后的程序段1,否则执行`#else`后的程序段2。`#ifndef _XXXX`则表示如果标识符未被定义,则执行程序段1,反之执行程序段2。
ANSI C宏提供了多种实用信息,如`__DATE__`返回当前日期,`__TIME__`返回当前时间,`__FILE__`包含当前文件名,`__LINE__`包含当前行号。`__STDC__`常量用于判断程序是否遵循ANSI C标准。`__FUNCTION__`宏在预编译时返回所在函数的名称。
宏参数的处理可以通过`#`将参数变为字符串,使用`##`将两个宏参数连接起来。`__VA_ARGS__`是一个可变参数宏,需配合`define`使用,将宏左侧的`..`内容原样复制到右侧。
`#if defined`和`#if !defined`在功能上相似,都用于判断宏是否定义。`#error`指令在编译时生成错误消息并停止编译,用于警告开发者。
`extern`关键字用于引用其他文件中的函数或全局变量。例如`extern ElogErrCode elog_port_init(void);`声明了一个名为`elog_port_init`的外部函数,调用时需要指明返回值类型和参数。
在多线程编程中,使用`sched_param`结构来管理线程调度参数。`sem_t`表示信号量,用于实现互斥和同步。`pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);`设置进程调度策略为实时轮转调度。
`SCHED_OTHER`默认分时调度策略,`SCHED_FIFO`采用先进先出策略,而`SCHED_RR`是`SCHED_FIFO`的增强版,提供实时轮转功能。使用`sched_get_priority_max(int policy);`和`sched_get_priority_min(int policy);`函数可以获取线程可设置的最高和最低优先级,其中策略参数即上述三种调度策略的宏定义。
`pthread_attr_setschedparam(&thread_attr, &thread_sched_param);`用于设置线程的优先级。通过这些函数,开发者可以精细地控制线程调度,提高程序性能。
easylogging源码学习笔记(6)
`LOG` 是默认日志、CLOG自定义日志、LOG_IF条件日志
特殊日志
LOG_EVERY_N、LOG_AFTER_N、LOG_N_TIMES
for (int i = 1; i <= ; ++i) {
LOG_EVERY_N(2, INFO) << "Logged every second iter";
}// 5 logs written; 2, 4, 6, 7,
for (int i = 1; i <= ; ++i) {
LOG_AFTER_N(2, INFO) << "Log after 2 hits; " << i;
}// 8 logs written; 3, 4, 5, 6, 7, 8, 9,
for (int i = 1; i <= ; ++i) {
LOG_N_TIMES(3, INFO) << "Log only 3 times; " << i;
}// 3 logs writter; 1, 2, 3
条件日志和特殊日志可以搭配使用
* `VLOG_IF(condition, verbose-level)`
* `CVLOG_IF(condition, verbose-level, loggerID)`
* `VLOG_EVERY_N(n, verbose-level)`
* `CVLOG_EVERY_N(n, verbose-level, loggerID)`
* `VLOG_AFTER_N(n, verbose-level)`
* `CVLOG_AFTER_N(n, verbose-level, loggerID)`
* `VLOG_N_TIMES(n, verbose-level)`
* `CVLOG_N_TIMES(n, verbose-level, loggerID)`
日志详细等级判定
if (VLOG_IS_ON(2)) {
// Verbosity level 2 is on for this file
}
性能追踪
* `TIMED_FUNC(obj-name)`
* `TIMED_SCOPE(obj-name, block-name)`
* `TIMED_BLOCK(obj-name, block-name)`
这些宏实际上都是关于el::base::type::PerformanceTrackerPtr,一个指向el::base::PerformanceTracker的指针
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
PerformanceTracker::PerformanceTracker(const std::string& blockName,
base::TimestampUnit timestampUnit,
const std::string& loggerId,
bool scopedLog, Level level) :
m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
// We store it locally so that if user happen to change configuration by the end of scope
// or before calling checkpoint, we still depend on state of configuration at time of construction
el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
if (m_enabled) {
base::utils::DateTime::gettimeofday(&m_startTime);
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
}
在构造函数中获取一个时间,
PerformanceTracker::~PerformanceTracker(void) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
if (m_enabled) {
base::threading::ScopedLock scopedLock(lock());
if (m_scopedLog) {
base::utils::DateTime::gettimeofday(&m_endTime);
base::type::string_t formattedTime = getFormattedTimeTaken();
PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
data.init(this);
data.m_formattedTimeTaken = formattedTime;
PerformanceTrackingCallback* callback = nullptr;
for (const std::pair& h
: ELPP->m_performanceTrackingCallbacks) {
callback = h.second.get();
if (callback != nullptr && callback->enabled()) {
callback->handle(&data);
}
}
}
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
}
在析构函数中获取一个时间,处理时间data,使用PerformanceTrackingCallback类型指针callback,并在callback->handle(&data)中处理输出。
由于定义了ELPP_FEATURE_PERFORMANCE_TRACKING,因此在初始化(INITIALIZE_EASYLOGGINGPP)中实际上是安装了一个base::DefaultPerformanceTrackingCallback。
在PerformanceTracker类的handle函数中,callback是一个PerformanceTrackingCallback类型指针,由于安装的是DefaultPerformanceTrackingCallback对象,因此是一个基类指针指向了派生类对象。处理输出的逻辑在DefaultPerformanceTrackingCallback类的handle函数中。
DefaultPerformanceTrackingCallback类的handle函数首先会将数据成员m_data的指针赋值给函数参数,并创建一个base::type::stringstream_t类型的对象ss用于构建输出内容。根据m_data的dataType,输出不同的信息。在输出时,会使用el::base::Writer类构造并输出内容。