当前位置: 首页 > news >正文

游戏引擎学习第14天

视频参考:https://www.bilibili.com/video/BV1iNUeYEEj4/

1. 为什么关注内存管理?

  • 内存分配是潜在的失败点

    • 每次进行内存分配(mallocnew等)时,都可能失败(例如内存不足)。
    • 这种失败会引入不稳定性或不可预测性,需要额外的错误处理逻辑。
  • 传统编程的理念

    • 在“老派编程”中,作者更倾向于避免动态内存分配,通过静态或固定大小的缓冲区管理内存,从而使程序更加稳定和可靠。

2. 如何避免动态内存分配?

  • 示例中使用的策略
    • 程序需要两个缓冲区(一个音频缓冲区和一个位图缓冲区),它们通过操作系统一次性分配完成,且在整个程序生命周期内从不释放(never freed)。
    • 这种设计消除了动态内存分配的风险,也避免了与内存回收(如垃圾回收)相关的复杂性。

3. 动态内存分配的挑战

  • 现代编程环境的现状

    • 现代程序大量依赖动态内存分配和垃圾回收(如Java、C#中的GC机制)。
    • 动态内存管理可能导致性能抖动(如虚拟内存分页),难以预测程序的行为。
  • 作者的观点

    • 过多依赖动态内存分配,使得程序开发变得复杂且不可控。
    • 作者更喜欢“静态内存分配”或“固定缓冲区”的方法,使程序从一开始就处于稳定状态。

4. 静态内存管理的优点

  • 高可靠性

    • 程序一旦启动,就不会因为内存问题而失败。
  • 简单性

    • 减少内存泄漏和回收相关的复杂性。
  • 性能一致性

    • 消除了垃圾回收或虚拟内存分页带来的性能抖动。

5. 哲学层面的思考

  • 作者更喜欢探索传统编程中的一些“简单且有趣”的做法,比如尽可能避免动态内存分配。
  • 他认为,这种方法可以让编程回归本质——精心设计和实现代码,而不是依赖现代编程环境中的各种工具和机制。

这段视频主要在讨论内存分配的优化以及游戏开发中不同的设计思路。以下是对这段对话的简要总结:

  1. 内存分配策略

    • 演讲者建议采用预分配内存的方式,而不是在运行时频繁分配。
    • 他提到这种方法类似“旧学校”的平分区策略,即一开始抓取一块大内存,并为每个子系统预先分配固定的空间。
  2. 静态变量的使用

    • 演讲者提到使用 static 定义的“局部持久变量”,它本质上是一种作用域限制在函数内的全局变量。
    • 他不喜欢这种设计,因为它可能导致代码混乱和难以维护。
  3. 传统与现代的差异

    • 他对比了旧式街机程序员的开发风格和现代普遍的“内存分配狂欢”方法。
    • 强调大多数现代游戏不会采用他的方式,但他认为提供不同的视角是有意义的。
  4. 未来设计计划

    • 他计划展示如何将游戏状态存储在预先分配的内存中。
    • 演讲者希望避免动态分配,并使用更高效的方式管理内存。
  5. 强调多样化的视角

    • 演讲者明确指出,他并不是在提倡大家完全采用他的方式,而是提供一种不同的思考方式。

这种方法对某些嵌入式系统或资源受限的环境可能很有帮助,但在资源丰富的现代系统中可能并不常见。您对这段内容是否还有特定部分需要进一步解析?

在某些编程或系统设计情况下,处理失败案例的挑战,特别是与内存分配相关的挑战。

  1. 失败案例的策略

    • 必须有一个处理失败案例的计划。例如,每当进行新的资源分配时,需要检查分配是否成功,并妥善处理可能的失败情况。这可能需要复杂的逻辑来处理多种路径的成功或失败。
  2. 现实中的折中策略

    • 一种方法是通过测试运行系统,观察其内存使用情况,并确保启动时分配足够的内存。
    • 使用自定义分配器,预先分配一定的内存以避免运行中内存不足,但这种方法依赖于测试的全面性。
  3. 未知风险的接受

    • 有时,只能假设程序不会超出所需的内存范围,希望玩家或用户不会触发未考虑到的极端场景。这种方法依赖于假设和希望,而不是完全可靠的保障。
  4. 故事的必要性

    • 在设计系统时,需要为失败场景找到一个“故事”或策略,即使有时故事仅仅是“不清楚,但在测试中未发现问题”。
  5. 个人观点

    • 作者对这些策略并不满意,认为这些都不是理想的答案,但在实践中可能不得不接受其中之一。

平台抽象设计中关于游戏与平台之间交互的复杂性,以及如何简化这种交互。以下是总结:

  1. 循环调用的缺点

    • 游戏和平台之间频繁的双向调用(循环调用)会增加复杂性,使得理解两者之间的关联变得更加困难。
    • 这种复杂性在高级功能(如记录和回放整个游戏会话以进行调试)中尤为明显。
  2. 推荐的设计方式

    • 建议设计为简单的数据流模式:
      • 平台作为服务请求游戏提供必要的数据(如视频帧、音频输出等)。
      • 输入(如用户操作)直接传递到游戏,输出直接返回到平台。
    • 避免游戏层再调用平台,这种方式可以减少系统的耦合性,简化回放系统的实现。
  3. 单线程时代的优势

    • 在过去的单线程环境中,这种输入流和输出流定义明确的设计简化了调试工作(例如回放游戏的整个会话)。
    • 单一输入和输出流的模型使得系统在逻辑上更容易追踪和复现问题。
  4. 多核时代的挑战

    • 如今由于多核架构的普及,线程化操作增加了复杂性,使回放系统变得困难。
    • 线程化问题导致这种设计在现代环境中不再像过去那样具有优势。
  5. 结论

    • 过去单线程的设计逻辑有其历史意义和现实效率,但在现代多线程环境中,需要权衡线程管理的难度和系统简单性之间的关系。

游戏开发中的内存管理哲学进行了讨论。以下是要点总结:

  1. 内存管理的责任分配

    • 平台层在游戏启动时分配一块大内存给游戏。
    • 游戏负责对这块内存进行进一步分区和管理。
    • 游戏内的所有系统都必须在一个固定的内存空间中操作,确保游戏启动后能够运行至结束而不会因内存问题失败。
  2. 可靠性目标

    • 游戏开发的目标是做到“一旦启动,就不会失败”。
    • 即使某些资源(如精灵)丢失,游戏仍然能够正常运行。
    • 平台层可能会由于操作系统的问题而失败(例如 Windows 的设计缺陷),但这不会影响游戏的整体可靠性。
  3. 资源管理的哲学

    • 开发中采用“零失败状态”原则,即尽量减少游戏运行中的错误。
    • 提议了“金银铜”标准,以根据不同游戏的复杂程度评估其达到可靠性目标的水平。
  4. 延伸到磁盘 I/O

    • 与内存管理类似,对磁盘 I/O 的管理也遵循相同的哲学,以尽量减少失败。

作者表示,动态分配本质上是一种"自欺欺人"的做法,因为它假设内存是无限的,而实际上硬件的内存是有限的。如果不控制内存使用,程序可能会在运行时遇到不可预见的问题,如内存不足或碎片化。
采用主存池的方案,可以保证程序在固定内存下运行,减少风险。尤其是在面向最终用户的产品中,确保内存不会超出限制并且程序能顺利启动和运行是非常重要的。
1:03:31Why use the void* in game_memory?
在这段字幕中,主要的内容涉及内存管理和如何在游戏中处理不确定类型的内存块。可以从中提取出以下几个关键点:

  1. 不确定的内存块:由于游戏内存的某些部分是不确定的,我们不能为这些部分指定类型。这些部分被视为一个“大内存块”,游戏可以根据需要任意处理这些内存数据。

  2. void指针:提到使用空指针(或void指针)来指示这些不确定的内存块。因为我们不知道它们的具体类型,所以使用void指针来管理这些内存是一个常见的做法。

  3. 类型转换:虽然开始时内存块没有明确的类型,但可以在后续转换成具体的游戏状态或其他类型,并开始进行类型化管理。

  4. 游戏中的使用:游戏开发中常常需要处理各种类型的内存,这些内存数据可以根据需求灵活地操作和转换成具体的类型。

  5. “冷启动”和“冷转换”:提到的“冷启动”和“冷转换”暗示着这些内存块被快速转换成所需的格式,过程没有太多的复杂操作,直接转换为游戏所需要的状态。

  6. 游戏规模的影响:由于游戏的规模可能很大,这种内存处理方法显得尤为重要,能够动态地应对不同类型的内存需求。

存储管理和游戏开发中的一些技术概念。可以理解为以下几点:

  1. 永久存储和临时存储的区分

    • 永久存储:指的是游戏在运行过程中必须保持的数据,这些数据对游戏的持续运行至关重要,例如玩家的进度、已解锁的内容等。
    • 临时存储:是一些临时存储的数据,可以在不影响游戏运行的情况下清除或重新加载,例如缓存的纹理、声音等资源。
  2. 区分的原因

    • 性能优化:临时存储的数据可以被刷新(如在游戏运行时清空缓存),这有助于优化性能,减少内存占用。比如,在iPhone等设备上,清空缓存有助于减少应用的内存占用。
    • 事件驱动:临时存储的数据可能会受到某些事件的影响(例如用户的操作或系统事件),而这些数据不一定需要长期保存。
  3. 为什么分开处理

    • 这样做是为了能够对数据进行更灵活的管理,例如在需要时清除临时存储的数据,而不会影响到永久存储中的重要数据。
    • 这种区分还有助于一些额外的优化技巧,例如缓存和重新加载过程的优化。
  4. 后期调整

    • 在开发过程中,可能会根据实际需求调整永久和临时存储的具体实现,这种调整是灵活的,因此开发者可能会先写好代码框架,再根据实际情况做修改。

避免在平台层和游戏层之间进行频繁往返调用(即“往返旅行”),这种设计模式可能导致系统中的复杂性和性能问题。以下是一个简要的总结:

  1. 避免内存分配的往返:谈话的重点之一是避免频繁在平台层和游戏之间进行内存分配的往返。这种“往返旅行”指的是平台层调用游戏层,游戏层再调用回平台层,从而引入不必要的复杂性和性能开销。

  2. 虚拟化的例子:提到一个虚拟化的例子,试图通过设计避免频繁的内存分配和平台调用之间的交互,这样可以减少系统中的“蜘蛛网”式的复杂关系。

  3. 堆栈和调用关系:系统中创建了一个堆栈,其中平台层和游戏层交替调用,这种结构可能导致管理上的困难,尤其是当每个组件由不同的开发人员编写时。

  4. 简化设计:最后提到的优化建议是,如果设计得当(比如通过传递合适的参数),可以简化整个调用结构,使得系统运行更加高效且易于维护。

总的来说,这段对话强调了通过避免复杂的相互依赖关系和频繁的内存分配,可以让系统变得更高效、更简洁。

下面是平台独立内存代码的修改

在这里插入图片描述

任务管理器查看内存提交的大小

在这里插入图片描述

vscode 的一个小插件

在这里插入图片描述

    "workbench.colorCustomizations": {// 新增文本的背景颜色"diffEditor.insertedTextBackground": "#00501388", // 浅绿色,带透明度// 删除文本的背景颜色"diffEditor.removedTextBackground": "#62000088", // 浅红色,带透明度// 修改区域的边框颜色(可选)},

相关文章:

游戏引擎学习第14天

视频参考:https://www.bilibili.com/video/BV1iNUeYEEj4/ 1. 为什么关注内存管理? 内存分配是潜在的失败点: 每次进行内存分配(malloc、new等)时,都可能失败(例如内存不足)。这种失败会引入不稳…...

关于mysql中的锁

mysql中包含的锁分为: 一、全局锁 二、表锁 三、行锁 一、全局锁 全局锁的力度是最大的,全局锁对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句…...

机器学习-4:机器学习的建模流程

机器学习的建模流程 流程为: 原始数据 --> 数据预处理 --> 特征工程 --> 建模 --> 验证。 原始数据收集 所有AI或机器学习的基础就是数据,没有数据就什么都做不了,在搭建一个系统之前首要考虑的就是有没有足够多的数据可以支撑这…...

Android 6年经验面试总结 2024.11.15

背景:深圳 面过12家中大厂、4家中小厂,通过4家中大厂,2家offer。 针对六年的求职面试总结:项目经验70%30%基础(基础应该必会) 对于上来就问八股文的公司,对于已经工作了5年以上的开发来说&…...

R语言数据分析可视化——summarytools包的使用

R语言中的summarytools包通过提供能够用最少的代码生成数据全面摘要的功能,使数据分析更加简单。summarytools包提供了一种简单的方法来生成数据集的摘要统计信息,包括描述性统计、频率表、交叉表、缺失值、异常值、相关性、线性回归、ANOVA、卡方检验等。本文将介绍如何使用…...

转型一年半,虎牙直播的第二增长曲线喜忧参半

文:互联网江湖 作者:刘致呈 最近,虎牙公司(NYSE:HUYA)公布了2024年第三季度财报。 表现怎么样呢?从财务数据上看,这份成绩单有点不尽人意。 报告期内,虎牙实现营收15.38亿元&…...

makefile笔记

makefile 在 Makefile 中,预定义的变量(也称为内置变量)提供了对构建过程中的默认值和特殊值的访问。这些变量通常由 Make 自动设置,并且可以覆盖它们以改变 Make 的行为。下面是 Make 环境中常见的几个内置变量及其用途&#xf…...

Rewar Model的输出(不包含训练)

这里写自定义目录标题 介绍模型推理的输出过程方案原始Token输出RM输出(回归任务) 介绍 奖励函数模型 (Reward Model) 是人工智能 (AI) 中的一种方法,模型因其对给定提示的响应而获得奖励或分数。现在的文章清一色的讲解RM的训练&#xff0c…...

Python调用API翻译Excel中的英语句子并回填数据

一、问题描述 最近遇到一个把Excel表中两列单元格中的文本读取,然后翻译,再重新回填到单元格中的案例。大约有700多行,1400多个句子,一个个手动复制粘贴要花费不少时间,而且极易出错。这时,我们就可以请出…...

SQL面试题——抖音SQL面试题 最大在线用户数

最大在线用户数 下面的数据记录了一个直播平台上用户进入平台和离开平台的情况 +---+-------------------+-----+ | id| etime| type| +---+-------------------+-----+ | 1|2021-06-10 10:00:00|enter| | 1|2021-06-10 19:00:00|leave| | 2|2021-06-10 11:0…...

前端知识点---Window对象(javascript)了解

Window对象 在JavaScript中,当你在非严格模式下的全局作用域中使用this时,它会引用全局对象。在浏览器环境中,这个全局对象就是Window。 01什么是 Window 对象? Window 是浏览器提供的一个全局对象,它代表了浏览器的…...

llama factory lora 微调 qwen2.5 7B Instruct模型

项目背景 甲方提供一台三卡4080显卡 需要进行qwen2.5 7b Instruct模型进行微调。以下为整体设计。 要使用 LLaMA-Factory 对 Qwen2.5 7B Instruct模型 进行 LoRA(Low-Rank Adapters)微调,流程与之前提到的 Qwen2 7B Instruct 模型类似。LoRA …...

类和对象——拷贝构造函数,赋值运算符重载(C++)

1.拷⻉构造函数 如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。 // 拷贝构造函数//d2(d1) Date(const Date& d) {_year d._yea…...

Android 关于使用videocompressor库压缩没有声音和异常的问题

原库地址 https://gitcode.com/gh_mirrors/vi/VideoCompressor/overview 这个库用起来比较方便,使用Android原生的MediaCodecmp4parser的方式进行压缩,不用接入so库也不用适配cpu 问题 接口库后你会发现过时了,所以你一阵捣鼓后你发现压缩…...

LeetCode-215.数组中的第K个最大元素

. - 力扣(LeetCode)给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问…...

『OpenCV-Python』视频的读取和保存

点赞 + 关注 + 收藏 = 学会了 推荐关注 《OpenCV-Python专栏》 上一讲介绍了 OpenCV 的读取图片的方法,这一讲简单聊聊 OpenCV 读取和保存视频。 视频的来源主要有2种,一种是本地视频文件,另一种是实时视频流,比如手机和电脑的摄像头。 要读取这两种视频的方法都是一样的…...

什么是Spring Boot Actuator

Spring Boot Actuator是一个用于监控和管理Spring Boot应用的框架,它提供了生产级别的功能,如健康检查、审计、指标收集、HTTP跟踪等。以下是对Spring Boot Actuator的详细介绍: 一、主要功能和特点 监控和管理: 提供多种内置端点…...

计算机网络:运输层 —— 运输层端口号

文章目录 运输层端口号的分类端口号与应用程序的关联应用举例发送方的复用和接收方的分用 运输层端口号的分类 端口号只具有本地意义,即端口号只是为了标识本计算机网络协议栈应用层中的各应用进程。在因特网中不同计算机中的相同端口号是没有关系的,即…...

linux下编译安装memcached

一、安装依赖库 Memcached依赖于一些系统库,在大多数Linux发行版中,需要安装libevent库。 Debian/Ubuntu系统 使用以下命令安装依赖库: sudo apt -y update sudo apt -y install libevent - devCentOS/RHEL系统 可以通过以下命令安装&am…...

最短路径生成树的数量-黑暗城堡

信息学奥赛一本通T1486-黑暗城堡 时间限制: 2s 内存限制: 192MB 提交: 18 解决: 9 题目描述 知道黑暗城堡有 N 个房间,M 条可以制造的双向通道,以及每条通道的长度。 城堡是树形的并且满足下面的条件: 设 Di为如果所有的通道都被修建&#xf…...

将已有的MySQL8.0单机架构变成主从复制架构

过程: 把数据库做一个完全备份, 恢复到从节点上, 恢复后从备份的那个点开始往后复制,从而保证后续数据的一致性。 步骤: 修改 master 主节点 的配置( server-id log-bin )master 主节点 完全备份( mysqldump )master 主节点 创建…...

JSON.stringify的应用说明

前言 JSON.stringify() 方法将 JavaScript 对象转换为字符串,在日常开发中较常用,但JSON.stringify其实有三个参数,后两个参数,使用较少,今天来介绍一下后两个参数的使用场景和示例。 语法及参数说明 JSON.stringify()&#xf…...

pyflink datastream数据流ds经过一系列转换后转为table,t_env.from_data_stream(ds)

在 pyflink 处理数据流过程中,有时候需要将data_stream转为table,下面是正确的方式,即每一个算子(map,reduce, window)操作之后需要指定输出数据类型。 from pyflink.common.typeinfo import Types from pyflink.datastream import StreamEx…...

vxe-grid table 校验指定行单元格的字段,只校验某个列的字段

Vxe UI vue vxe-table 中校验表格行是非常简单的,只需要配置好校验规则,然后调用 validate 方法就可以自动完成校验,但是由于项目淡色特殊需求,在某个单元格的值修改后需要对另一个列的值就行校验,这个时候又不需要全部…...

【Java多线程】单例模式(饿汉模式和懒汉模式)

目录 单例模式的定义: 饿汉式--单例模式 定义: 案例: 优缺点: 懒汉式--单例模式: 定义: 1)懒汉式单例模式(非线程安全) 2)线程安全的懒汉式单例模…...

python 异步编程之协程

最近在学习python的异步编程,这里就简单记录一下,免得日后忘记。 首先,python异步实现大概有三种方式,多进程,多线程和协程;多线程和多进程就不用多说了,基本上每种语言都会有多进行和多线程的…...

现代密码学|古典密码学例题讲解|AES数学基础(GF(2^8)有限域上的运算问题)| AES加密算法

文章目录 古典密码凯撒密码和移位变换仿射变换例题多表代换例题 AES数学基础(GF(2^8)有限域上的运算问题)多项式表示法 | 加法 | 乘法X乘法模x的四次方1的乘法 AES加密算法初始变换字节代换行移位列混合轮密钥加子密钥&#xff08…...

算法沉淀一:双指针

目录 前言: 双指针介绍 对撞指针 快慢指针 题目练习 1.移动零 2.复写零 3.快乐数 4.盛水最多的容器 5.有效三角形的个数 6.和为s的两个数 7.三数之和 8.四数之和 前言: 此章节介绍一些算法,主要从leetcode上的题来讲解&#xff…...

Word_小问题解决_1

1.第二页是空白的,但是删不掉 将鼠标弄到第二页最开始的地方打开段落设置行距为固定值0.7磅 2.表格中有文字进入了表格中怎么办 打开段落,将缩进改为0即可...

基于opencv制作GUI界面

可以基于cvui头文件实现一些控件操作&#xff0c;头文件及demo实例 这是一个demo main.cpp #include <opencv2/opencv.hpp> #define CVUI_IMPLEMENTATION #include "cvui.h"#define WINDOW_NAME "CVUI Hello World!"int main(void) {cv::Mat frame…...