4/1 背刺!春招B站一面,这些问题你都会吗?

简介: 4/1 背刺!春招B站一面,这些问题你都会吗?

下面我将分享一位同学在Bilibili一面的面试经历,对于这次面试,他的评价是,「很有难度」,你试试呢?

【提醒】通过这次面试经验,你将可以复习到以下知识点,注意汇总,不超过10个


  1. RPC调用的网络中断处理
  2. DPDK
  3. C++的多态实现和应用场景
  4. C++类的大小及成员变量初始化
  5. unique_ptr的用法
  6. Golang的内存分配器
  7. 项目的压力测试方法
  8. CPU负载过高的排查方法
  9. HTTP2.0和HTTP3.0的优势
  10. 进程、线程、协程的区别
  11. 内存分配大小的限制
  12. STL Vector的线程安全问题
  13. C++的内存序
  14. 算法题:k个一组反转链表

e7c98c16527500506b450f4b003ecc99.jpg

img

「面试官」: 你好,我们先从你的项目开始谈起。项目中RPC调用在网络中断后,作为client端的状态应该是怎么样的?


「求职者」: 网络中断后,客户端在发起RPC调用时会因为不能建立连接而失败。这时候,一般会有重试机制,例如重试一定次数或者在一定时间内重试。如果重试都失败的话,那么这次RPC调用就会报错。


「面试官」: 明白了。那你对DPDK有了解吗?


「求职者」: DPDK,全称Data Plane Development Kit,是英特尔开源的一套数据平面开发套件。它主要用于网络数据包的高速处理,可以绕过操作系统内核,直接在用户态处理网络数据包,避免了内核态和用户态之间的切换开销,提高了数据包处理的效率。


「面试官」: 很好。接下来,谈谈「C++多态」的实现,以及它的应用场景。


「求职者」: C++的多态主要是「通过虚函数和继承」来实现的。在基类中定义虚函数,然后在派生类中重写这个虚函数,通过基类的指针或引用来调用这个虚函数,就可以实现多态。多态的主要应用场景是当我们需要处理一组对象,这组对象都是同一个基类的派生类,但是具体是哪个派生类在编译时并不确定。这时候,我们就可以利用多态,将这组对象都当作基类对象来处理,而具体的行为会根据对象的实际类型来确定。


「面试官」: 那你知道一个空类的大小是多少吗?


「求职者」: 在C++中,一个空类的大小不是0,而是1字节。「这是为了保证两个不同的对象有不同的地址」。


「面试官」: 好的。那么,如果在class里面定义了int a,但是没有实现构造函数,实例化这个类后,a的值是什么?


「求职者」: 如果没有实现构造函数,那么a的值是未定义的,也就是说,它的值是随机的,取决于这块内存之前的状态。


「面试官」: 明白了。接下来,你能告诉我unique_ptr可以作为函数返回值吗?


「求职者」: 是的,unique_ptr可以作为函数返回值。因为C++11支持移动语义,所以在返回时,unique_ptr会被移动,而不是复制。这样就避免了资源的泄漏。


「面试官」: 那你对golang的内存分配器有了解吗?


「求职者」: golang的内存分配器是基于tcmalloc实现的,它将内存分为小对象和大对象两种。小对象使用fix-size的内存池,大对象使用页堆。golang的内存分配器还有一些特性,比如支持并发分配,支持分代回收等。


「面试官」: 了解了。那么,你的项目的压力测试是怎么做的?


「求职者」: 我们的压力测试是使用JMeter来进行的。「JMeter可以模拟大量用户并发访问」,我们会设置不同的并发数和请求频率,然后观察系统的响应时间、吞吐量、错误率等指标,以此来评估系统的性能。


「面试官」: 那如果项目中的CPU负载过高,你会怎么排查呢?


「求职者」: CPU负载过高,「首先我会使用top或者htop命令来查看CPU的使用情况」,看看是哪个进程占用的CPU最高。然后,我会使用perf或者gprof这样的性能分析工具,对这个进程进行性能分析,找出CPU使用高的函数。接下来,我会深入到这个函数的代码中,看看是否有可以优化的地方。如果这个函数是一个循环或者递归,我会检查是否有冗余的计算,是否可以通过缓存结果来减少计算量。如果这个函数是一个阻塞操作,我会看看是否可以通过异步或者并发来提高效率。


「面试官」: 很好。那么,你的项目的瓶颈在哪个Server上呢?


「求职者」: 我们的项目的「瓶颈主要在数据库服务器上」。因为我们的业务逻辑比较复杂,涉及到大量的数据库操作,而且有些查询非常复杂,导致数据库的响应时间较长。所以,我们现在正考虑对数据库进行优化,比如使用索引来提高查询速度,使用读写分离来提高并发性能,或者使用分片来分散数据库的压力。


「面试官」: 明白了。接下来,我想问一下,快手直播流媒体是走长连接网关推送的吗?


「求职者」: 一般的流媒体服务,比如直播,是需要维持一个长连接的,这样才能保证实时性。至于是否通过网关推送,我想应该是的,因为通过网关可以进行负载均衡,提高系统的可用性。


「面试官」: 好的。那你能讲一下HTTP3.0对比HTTP 2.0的优势吗?


「求职者」: HTTP3.0的最大优势是引入了QUIC协议,替代了TCP协议。QUIC协议解决了TCP的头阻塞问题,实现了全双工的多路复用。此外,QUIC协议还支持0-RTT的快速握手,提高了连接的建立速度。而且,QUIC协议还实现了更好的拥塞控制和丢包恢复。


「面试官」: 明白了。那HTTP 2.0 对比 HTTP 1.1的优势又是什么呢?


「求职者」: HTTP 2.0的主要优势是实现了多路复用,解决了HTTP 1.1的头阻塞问题。此外,HTTP 2.0还引入了服务器推送和首部压缩等特性,进一步提高了效率。


「面试官」: 很好。接下来,讲一下进程、线程、协程的区别。


「求职者」: 进程是操作系统进行资源分配的最小单位,线程是操作系统进行调度的最小单位。每个进程有自己独立的地址空间和系统资源,而线程则共享所属进程的资源。协程又称为轻量级线程,它是一种用户态的线程,不需要操作系统参与调度,完全由程序自己控制,因此开销更小,切换更快。


「面试官」: 好的。一个进程调用malloc最大能分配多大的内存?


「求职者」: 一个进程调用malloc能分配的最大内存主要取决于操作系统的位数和进程的地址空间。例如,在32位操作系统中,理论上最大可以分配4GB的内存,但实际上会小于这个值,因为操作系统还需要一部分地址空间来管理硬件和操作系统自身。在64位操作系统中,理论上可以分配的内存会更大,但实际可用内存还是要受到物理内存和操作系统限制。


「面试官」: 明白了。如果有一个8G物理内存的机器,调用malloc(10G)会发生什么?


「求职者」: 如果在一个只有8G物理内存的机器上尝试分配10G内存,操作系统会使用虚拟内存来满足这个请求。虚拟内存通常使用硬盘作为额外的存储空间。但是,如果硬盘的交换空间(swap space)不足,malloc调用可能会失败,并返回NULL指针。


「面试官」: 好的,来谈谈STL Vector线程安全吗,不安全在哪?


「求职者」: STL Vector本身不是线程安全的。如果有多个线程同时对同一个Vector对象进行操作,比如一个线程正在添加元素,而另一个线程正在删除元素或迭代元素,那么就可能会导致数据竞争和不可预期的行为。


「面试官」: 那在多线程下使用Vector一定要加锁吗?


「求职者」: 是的,如果在多线程环境中使用Vector,为了保证操作的原子性和数据的一致性,通常需要对Vector的操作加锁。


「面试官」: 如果两个线程同时对Vector下相同索引的元素进行修改会发生什么?


「求职者」: 如果两个线程同时对Vector下相同索引的元素进行修改,那么最终的结果取决于哪个线程最后完成写入操作。这是典型的竞态条件,可能会导致程序的行为难以预测和调试。


「面试官」: 那么,能介绍一下C++的内存序吗?


「求职者」: C中的内存序是指在多线程环境下,对内存的读写操作的可见性和顺序。在多处理器系统中,不同处理器对内存的读写操作可能会有不同的顺序,这就导致了内存顺序问题。为了解决这个问题,C11引入了原子操作和内存序的概念,提供了几种不同的内存序选项,例如

memory_order_relaxedmemory_order_consumememory_order_acquirememory_order_releasememory_order_acq_relmemory_order_seq_cst

「面试官」: 最后一个问题,手撕:k个一组反转链表。

「求职者」: 好的,这个问题可以通过迭代或递归来解决。我可以给出一个迭代的解法。

ListNode* reverseKGroup(ListNode* head, int k) {
    if(head == nullptr || k == 1) return head;

    ListNode dummy(0);
    dummy.next = head;
    ListNode *curr = &dummy, *nex, *pre = &dummy;

    int count = 0;
    while(curr->next != nullptr) {
        curr = curr->next;
        count++;
    }
    
    while(count >= k) {
        curr = pre->next;
        nex = curr->next;
        for(int i = 1; i < k; i++) {
            curr->next = nex->next;
            nex->next = pre->next;
            pre->next = nex;
            nex = curr->next;
        }
        pre = curr;
        count -= k;
    }
    
    return dummy.next;
}

「面试官」: 很好,你的回答很全面。谢谢你,这就结束了我们今天的面试,等消息吧。

相关文章
|
自然语言处理 算法 数据挖掘
自蒸馏:一种简单高效的优化方式
背景知识蒸馏(knowledge distillation)指的是将预训练好的教师模型的知识通过蒸馏的方式迁移至学生模型,一般来说,教师模型会比学生模型网络容量更大,模型结构更复杂。对于学生而言,主要增益信息来自于更强的模型产出的带有更多可信信息的soft_label。例如下右图中,两个“2”对应的hard_label都是一样的,即0-9分类中,仅“2”类别对应概率为1.0,而soft_label
自蒸馏:一种简单高效的优化方式
|
6月前
|
算法 Go
🚀 力扣热题 78:子集(详细解析)
✅ 回溯法:经典通用模板,逻辑清晰易扩展。 ✅ 二进制法:简洁高效,适合面试快速写出解法。
216 30
|
1天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1063 0
|
10天前
|
人工智能 运维 安全
|
1天前
|
弹性计算 Kubernetes jenkins
如何在 ECS/EKS 集群中有效使用 Jenkins
本文探讨了如何将 Jenkins 与 AWS ECS 和 EKS 集群集成,以构建高效、灵活且具备自动扩缩容能力的 CI/CD 流水线,提升软件交付效率并优化资源成本。
245 0
|
8天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
9天前
|
人工智能 测试技术 API
智能体(AI Agent)搭建全攻略:从概念到实践的终极指南
在人工智能浪潮中,智能体(AI Agent)正成为变革性技术。它们具备自主决策、环境感知、任务执行等能力,广泛应用于日常任务与商业流程。本文详解智能体概念、架构及七步搭建指南,助你打造专属智能体,迎接智能自动化新时代。
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
B站开源IndexTTS2,用极致表现力颠覆听觉体验
在语音合成技术不断演进的背景下,早期版本的IndexTTS虽然在多场景应用中展现出良好的表现,但在情感表达的细腻度与时长控制的精准性方面仍存在提升空间。为了解决这些问题,并进一步推动零样本语音合成在实际场景中的落地能力,B站语音团队对模型架构与训练策略进行了深度优化,推出了全新一代语音合成模型——IndexTTS2 。
738 23