c++扩展算子开发③:CUDA算子的开发

c++kquote>本文介绍了使用C++进行CUDA算子开发的流程,以tanh算子为例,包含编写.cu文件实现运算、.cpp文件实现Python调用绑定、.py文件实现安装。展示了前向输出和回传梯度与官方实现一致,还详细拆分了各文件代码及作用。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

c++扩展算子开发③:cuda算子的开发 - 创想鸟

c++扩展算子开发③:CUDA算子的开发

项目说明

  在使用c++进行CUDA算子开发

开发流程

编写.cu文件实现该算子的运算部分,在使用setup.py对算子进行安装时,nvcc程序针对.cu文件进行编译,并最终包含进动态链接库编写.cpp文件使得可以在python中调用CUDA kernel函数,.cpp调用上面.cu文件中启动函数,绑定到python中使用编写.py文件实现该算子安装

项目展示

  在GPU上面运行tanh算子,可以看到官方实现的算子和我们自己实现的CUDA算子的前向输出和回传梯度都一致   安装自己实现的tanh算子,运行后请刷新下环境!!!

In [ ]

!python setup.py install

In [1]

import numpy as npx = np.random.random((4, 10)).astype("float32")print(x)
[[0.8485352  0.82548    0.6914224  0.33665353 0.5060949  0.12096553  0.93415546 0.66898936 0.36616254 0.61785257] [0.9686086  0.8368737  0.87306726 0.5306038  0.35964754 0.09533529  0.6159888  0.5113984  0.3554379  0.92584795] [0.5851171  0.87855285 0.8729009  0.16328739 0.06106287 0.03119349  0.6431769  0.46255094 0.39092144 0.6841152 ] [0.41889587 0.85792965 0.48324853 0.8920178  0.7228439  0.2088154  0.18290831 0.74242246 0.770023   0.89185   ]]

tanh(Offical)

In [2]

import paddlepaddle_x = paddle.to_tensor(x, place=paddle.CUDAPlace(0))paddle_x.stop_gradient = Falsepaddle_y = paddle.tanh(paddle_x)paddle_y.backward()grad = paddle_x.gradient()print("==========================================================")print("前向传播:")print(paddle_y)print("==========================================================")print("检测是否在GPU上:")print(paddle_y.place)print("==========================================================")print("梯度:")print(grad)
W0112 18:06:20.751464  7652 device_context.cc:447] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 10.1, Runtime API Version: 10.1W0112 18:06:20.756742  7652 device_context.cc:465] device: 0, cuDNN Version: 7.6.
==========================================================前向传播:Tensor(shape=[4, 10], dtype=float32, place=CUDAPlace(0), stop_gradient=False,       [[0.69030344, 0.67804146, 0.59889495, 0.32448652, 0.46689692, 0.12037896,         0.73252547, 0.58431470, 0.35063058, 0.54963106],        [0.74809217, 0.68414962, 0.70292914, 0.48584250, 0.34490353, 0.09504751,         0.54832906, 0.47103402, 0.34118930, 0.72865224],        [0.52637470, 0.70569360, 0.70284498, 0.16185147, 0.06098709, 0.03118338,         0.56705880, 0.43216103, 0.37215433, 0.59418815],        [0.39599988, 0.69518942, 0.44884148, 0.71238893, 0.61866784, 0.20583236,         0.18089549, 0.63060653, 0.64694285, 0.71230626]])==========================================================检测是否在GPU上:CUDAPlace(0)==========================================================梯度:[[0.52348113 0.5402598  0.6413248  0.8947085  0.7820073  0.9855089  0.46340644 0.6585763  0.8770582  0.6979057 ] [0.4403581  0.53193927 0.5058906  0.7639571  0.8810415  0.99096596  0.6993352  0.77812696 0.88358986 0.4690659 ] [0.72292966 0.5019965  0.5060089  0.9738041  0.99628055 0.9990276  0.6784443  0.81323683 0.86150116 0.6469404 ] [0.8431841  0.51671165 0.7985413  0.492502   0.6172501  0.957633  0.9672768  0.6023354  0.58146495 0.49261978]]

tanh(Ours)

1、安装tanh算子,运行后请刷新下环境!!!(前面已经安装了)

In [4]

!python setup.py install

2、开始测试

立即学习“C++免费学习笔记(深入)”;

In [3]

import paddlefrom custom_ops import tanh_opcustom_ops_x = paddle.to_tensor(x, place=paddle.CUDAPlace(0))custom_ops_x.stop_gradient = Falsecustom_ops_y = tanh_op(custom_ops_x)custom_ops_y.backward()grad = custom_ops_x.gradient()print("==========================================================")print("前向传播:")print(custom_ops_y)print("==========================================================")print("检测是否在GPU上:")print(custom_ops_y.place)print("==========================================================")print("梯度:")print(grad)
==========================================================前向传播:Tensor(shape=[4, 10], dtype=float32, place=CUDAPlace(0), stop_gradient=False,       [[0.69030344, 0.67804146, 0.59889495, 0.32448652, 0.46689692, 0.12037896,         0.73252547, 0.58431470, 0.35063058, 0.54963106],        [0.74809217, 0.68414962, 0.70292914, 0.48584250, 0.34490353, 0.09504751,         0.54832906, 0.47103402, 0.34118930, 0.72865224],        [0.52637470, 0.70569360, 0.70284498, 0.16185147, 0.06098709, 0.03118338,         0.56705880, 0.43216103, 0.37215433, 0.59418815],        [0.39599988, 0.69518942, 0.44884148, 0.71238893, 0.61866784, 0.20583236,         0.18089549, 0.63060653, 0.64694285, 0.71230626]])==========================================================检测是否在GPU上:CUDAPlace(0)==========================================================梯度:[[0.52348113 0.5402598  0.6413248  0.8947085  0.7820073  0.9855089  0.46340644 0.6585763  0.8770582  0.6979057 ] [0.4403581  0.53193927 0.5058906  0.7639571  0.8810415  0.99096596  0.6993352  0.77812696 0.88358986 0.4690659 ] [0.72292966 0.5019965  0.5060089  0.9738041  0.99628055 0.9990276  0.6784443  0.81323683 0.86150116 0.6469404 ] [0.8431841  0.51671165 0.7985413  0.492502   0.6172501  0.957633  0.9672768  0.6023354  0.58146495 0.49261978]]

项目主体

.cu文件

  .cu文件主要是实现该算子的运算部分,在使用setup.py对算子进行安装时,nvcc程序针对.cu文件进行编译,并最终包含进动态链接库

代码拆分

  1、引入头文件,以及定义一个block含有的thread数目

In [ ]

#include #include #include #include #define BLOCK 512

  2、定义前向传播运算函数
  该函数是一个CUDA特有声明为__global__的模板函数,负责具体执行运算部分
  这里的blockIdx,blockDim,threadIdx分别表示block索引,block维度,thread索引,GPU上有多个并发的线程同时负责以上计算,用gid=blockIdx.x * blockDim.x + threadIdx.x这一语句用来计算绝对索引,负责返回数据中某个位置处值,这样就只需要关注于单个线程计算过程

In [ ]

template__global__ void tanh_forward_cuda_kernel(const data_t* input_data,                                    data_t* output_data,                                    int input_numel){    int gid = blockIdx.x * blockDim.x + threadIdx.x;    for(int i=gid; i<input_numel; i+=blockDim.x*gridDim.x){        output_data[i] = std::tanh(input_data[i]);    }}

  3、定义前向传播启动函数
  该函数是一个返回paddle::Tensor类型的函数,负责对输入进行一些转换,数据初始化以及返回前向传播运算成果
  这里的PD_DISPATCH_FLOATING_TYPES这个宏,实现了动态分发机制(dynamic dispatch),即它会在运行时,根据输入具体的数值类型,去决定之前CUDA kernel模块函数需要实例化为哪种函数吗,这也是之前用模板类data_t的原因。
  PD_DISPATCH_FLOATING_TYPES这个宏函数,传入的参数有三个:数据类型,用来报错的函数名、一个Lambda函数
  ①数据类型可以通过.type()获取
  ②用来报错的函数名可以自己命名,一般与该算子作用相关
  ③Lambda函数部分([&]表示该Lambda表达式中用到的外部变量是传引用的)包括前面2中实现的运算函数tanh_forward_cuda_kernel;运算函数后面用到了<<>>这一写法启动kernel,其中需要根据输出大小分配grid数(用grid = (input_numel + BLOCK – 1) / BLOCK算出来),并设置每一block中的thread数(宏定义中的BLOCK),还有传入tensor目前所在的stream;接着就是( )里面传递参数进运算函数tanh_forward_cuda_kernel

In [ ]

std::vector tanh_forward_cuda(const paddle::Tensor &input){    auto output = paddle::Tensor(paddle::PlaceType::kGPU, input.shape());    int input_numel = input.size();    int grid = (input_numel + BLOCK - 1) / BLOCK;    PD_DISPATCH_FLOATING_TYPES(        input.type(), "tanh_forward_cuda_kernel", ([&] {            tanh_forward_cuda_kernel<<>>(                input.data(),                 output.mutable_data(input.place()),                 input_numel            );        })    );    return {output};}

  4、同理,定义反向回传的运算函数和启动函数

In [ ]

template__global__ void tanh_backward_cuda_kernel(const data_t* input_data,                                    const data_t* output_grad_data,                                    data_t* input_grad_data,                                    int output_numel){    int gid = blockIdx.x * blockDim.x + threadIdx.x;    for(int i=gid; i<output_numel; i+=blockDim.x*gridDim.x){        input_grad_data[i] = output_grad_data[i] * (1 - std::pow(std::tanh(input_data[i]), 2));    }}std::vector tanh_backward_cuda(const paddle::Tensor &input,                                               const paddle::Tensor &output,                                               const paddle::Tensor &output_grad){    auto input_grad = paddle::Tensor(paddle::PlaceType::kGPU, input.shape());    int output_numel = output.size();    int grid = (output_numel + BLOCK - 1) / BLOCK;    PD_DISPATCH_FLOATING_TYPES(        input.type(), "tanh_backward_cuda_kernel", ([&] {            tanh_backward_cuda_kernel<<>>(                input.data(),                 output_grad.data(),                 input_grad.mutable_data(input.place()),                 output_numel            );        })    );    return {input_grad};}

完整代码

In [ ]

#include #include #include #include #define BLOCK 512template__global__ void tanh_forward_cuda_kernel(const data_t* input_data,                                    data_t* output_data,                                    int input_numel){    int gid = blockIdx.x * blockDim.x + threadIdx.x;    for(int i=gid; i<input_numel; i+=blockDim.x*gridDim.x){        output_data[i] = std::tanh(input_data[i]);    }}template__global__ void tanh_backward_cuda_kernel(const data_t* input_data,                                    const data_t* output_grad_data,                                    data_t* input_grad_data,                                    int output_numel){    int gid = blockIdx.x * blockDim.x + threadIdx.x;    for(int i=gid; i<output_numel; i+=blockDim.x*gridDim.x){        input_grad_data[i] = output_grad_data[i] * (1 - std::pow(std::tanh(input_data[i]), 2));    }}std::vector tanh_forward_cuda(const paddle::Tensor &input){    auto output = paddle::Tensor(paddle::PlaceType::kGPU, input.shape());    int input_numel = input.size();    int grid = (input_numel + BLOCK - 1) / BLOCK;    PD_DISPATCH_FLOATING_TYPES(        input.type(), "tanh_forward_cuda_kernel", ([&] {            tanh_forward_cuda_kernel<<>>(                input.data(),                 output.mutable_data(input.place()),                 input_numel            );        })    );    return {output};}std::vector tanh_backward_cuda(const paddle::Tensor &input,                                               const paddle::Tensor &output,                                               const paddle::Tensor &output_grad){    auto input_grad = paddle::Tensor(paddle::PlaceType::kGPU, input.shape());    int output_numel = output.size();    int grid = (output_numel + BLOCK - 1) / BLOCK;    PD_DISPATCH_FLOATING_TYPES(        input.type(), "tanh_backward_cuda_kernel", ([&] {            tanh_backward_cuda_kernel<<>>(                input.data(),                 output_grad.data(),                 input_grad.mutable_data(input.place()),                 output_numel            );        })    );    return {input_grad};}

.cpp文件

  .cpp文件是为了使得可以在python中调用CUDA kernel函数,它调用上面.cu文件中启动函数,绑定到python中使用

代码拆分

  1、引入头文件,以及定义PADDLE_WITH_CUDA和CHECK_INPUT(x)
  ①PADDLE_WITH_CUDA是用来能够获取Tensor.steam(),详细可看官方定义下的代码

#if defined(PADDLE_WITH_CUDA)  /// bref Get current stream of Tensor  cudaStream_t stream() const;#elif defined(PADDLE_WITH_HIP)  hipStream_t stream() const;#endif

  ②CHECK_INPUT(x)用来查验Tensor是否在GPU上面或者数据类型是否出错In [ ]

#include #include #define PADDLE_WITH_CUDA#define CHECK_INPUT(x) PD_CHECK(x.place() == paddle::PlaceType::kGPU, #x " must be a GPU Tensor.")

  2、声明.cu里的启动函数,以便后面编程时进行联想以及让编译器知道这么一个函数

In [ ]

std::vector tanh_forward_cuda(const paddle::Tensor &input);std::vector tanh_backward_cuda(const paddle::Tensor &input,                                               const paddle::Tensor &output,                                               const paddle::Tensor &output_grad);

  3、编写前向传播函数,主要实现调用.cu里的前向传播启动函数

In [ ]

std::vector tanh_forward(const paddle::Tensor& input) {  CHECK_INPUT(input);  return tanh_forward_cuda(input);}

  4、编写反向传播函数,主要实现调用.cu里的反向回传启动函数

In [ ]

std::vector tanh_backward(const paddle::Tensor& input,                                          const paddle::Tensor& output,                                          const paddle::Tensor& output_grad) {  CHECK_INPUT(input);  CHECK_INPUT(output);  CHECK_INPUT(output_grad);  return tanh_backward_cuda(input, output, output_grad);}

  5、使用PD_BUILD_OP系列宏,构建算子的描述信息,实现python与c++算子的绑定,作用有点类似PYBIND11_MODULE
  PD_BUILD_OP:用于构建前向算子
  PD_BUILD_GRAD_OP:用于构建前向算子对应的反向算子
  注意:构建同一个算子的前向、反向实现,宏后面使用的算子名需要保持一致(此例中的tanh_op)
  注意:PD_BUILD_OP与PD_BUILD_GRAD_OP中的Inputs与Outputs的name有强关联,对于前向算子的某个输入,如果反向算子仍然要复用,那么其name一定要保持一致(此例中的Inputs({“input”}和Outputs({“output”}),因为内部执行时,会以name作为key去查找对应的变量,比如这里前向算子的input与反向算子的input指代同一个Tensor

In [ ]

PD_BUILD_OP(tanh_op)    .Inputs({"input"})    .Outputs({"output"})    .SetKernelFn(PD_KERNEL(tanh_forward));PD_BUILD_GRAD_OP(tanh_op)    .Inputs({"input", "output", paddle::Grad("output")})    .Outputs({paddle::Grad("input")})    .SetKernelFn(PD_KERNEL(tanh_backward));

完整代码

In [ ]

#include #include #define PADDLE_WITH_CUDA#define CHECK_INPUT(x) PD_CHECK(x.place() == paddle::PlaceType::kGPU, #x " must be a GPU Tensor.")std::vector tanh_forward_cuda(const paddle::Tensor &input);std::vector tanh_backward_cuda(const paddle::Tensor &input,                                               const paddle::Tensor &output,                                               const paddle::Tensor &output_grad);std::vector tanh_forward(const paddle::Tensor& input) {  CHECK_INPUT(input);  return tanh_forward_cuda(input);}std::vector tanh_backward(const paddle::Tensor& input,                                          const paddle::Tensor& output,                                          const paddle::Tensor& output_grad) {  CHECK_INPUT(input);  CHECK_INPUT(output);  CHECK_INPUT(output_grad);  return tanh_backward_cuda(input, output, output_grad);}PD_BUILD_OP(tanh_op)    .Inputs({"input"})    .Outputs({"output"})    .SetKernelFn(PD_KERNEL(tanh_forward));PD_BUILD_GRAD_OP(tanh_op)    .Inputs({"input", "output", paddle::Grad("output")})    .Outputs({paddle::Grad("input")})    .SetKernelFn(PD_KERNEL(tanh_backward));

.py文件

  .py文件主要是实现该算子安装

  在安装后引用该算子,以此为例,是通过from custom_ops import tanh_op来引用的

  其中custom_ops来自setup.py部分的name里

  c++扩展算子开发③:CUDA算子的开发 - 创想鸟

  其中tan_op来自.cpp部分的PD_BUILD_OP里

  c++扩展算子开发③:CUDA算子的开发 - 创想鸟

In [ ]

from paddle.utils.cpp_extension import CUDAExtension, setupsetup(    name='custom_ops',    ext_modules=CUDAExtension(        sources=['tanh.cpp', 'tanh.cu']    ))

以上就是c++++扩展算子开发③:CUDA算子的开发的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/68785.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月12日 19:47:54
下一篇 2025年11月12日 20:49:00

相关推荐

  • Linux命令行中wc命令的实用技巧

    wc命令可统计文件的行数、单词数、字符数和字节数,常用-l统计行数,如wc -l /etc/passwd查看用户数量;结合grep可分析日志,如grep “error” logfile.txt | wc -l统计错误行数;-w统计单词数,-m统计字符数(含空格换行),-c统计…

    2025年12月6日 运维
    000
  • VSCode入门:基础配置与插件推荐

    刚用VSCode,别急着装一堆东西。先把基础设好,再按需求加插件,效率高还不卡。核心就三步:界面顺手、主题舒服、功能够用。 设置中文和常用界面 打开软件,左边活动栏有五个图标,点最下面那个“扩展”。搜索“Chinese”,装上官方出的“Chinese (Simplified) Language Pa…

    2025年12月6日 开发工具
    000
  • VSCode性能分析与瓶颈诊断技术

    首先通过资源监控定位异常进程,再利用开发者工具分析性能瓶颈,结合禁用扩展、优化语言服务器配置及项目设置,可有效解决VSCode卡顿问题。 VSCode作为主流的代码编辑器,虽然轻量高效,但在处理大型项目或配置复杂扩展时可能出现卡顿、响应延迟等问题。要解决这些性能问题,需要系统性地进行性能分析与瓶颈诊…

    2025年12月6日 开发工具
    000
  • VSCode的悬浮提示信息可以自定义吗?

    可以通过JSDoc、docstring和扩展插件自定义VSCode悬浮提示内容,如1. 添加JSDoc或Python docstring增强信息;2. 调整hover延迟与粘性等显示行为;3. 使用支持自定义提示的扩展或开发hover provider实现深度定制,但无法直接修改HTML结构或手动编…

    2025年12月6日 开发工具
    000
  • 外媒:V社亲手摧毁CS2饰品市场 或许永难复原!

    《反恐精英2》的皮肤经济正遭遇前所未有的崩塌。在10月23日valve发布更新后的48小时内,这项允许玩家使用“交易升级合约”将五件隐秘级别皮肤兑换为刀具或手套的功能上线后,整个市场估值从约60亿美元骤降至30亿美元。短短两天内,数十亿虚拟资产化为乌有,令无数玩家对这个曾是全球最大数字游戏经济体之一…

    2025年12月6日 游戏教程
    000
  • 复古生存恐怖游戏《血肉生惧》月底登陆Steam

    德国独立游戏发行商assemble entertainment携手美国独立开发团队tainted pact正式宣布,其精心打造的复古风生存恐怖游戏《血肉生惧》(flesh made fear)将于2025年10月31日通过steam平台登陆pc。本作是知名制作人迈克尔·科西奥的最新力作,他曾主导创作…

    2025年12月6日 游戏教程
    000
  • Microsoft Teams如何设置访客权限 Microsoft Teams外部协作的安全管理

    首先登录Microsoft 365管理中心启用Teams访客访问功能,接着在Azure AD中配置目录范围与信息可见性限制,最后通过敏感度标签、审核日志、DLP策略及文件共享设置实施沟通与内容安全管控,实现外部协作的安全管理。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 D…

    2025年12月6日 科技
    000
  • 如何传承与延续 《光环:战役进化》核心问题详解

    官方已确认:《光环》系列将以《光环:战役进化》开启新篇章。这款作品采用虚幻引擎5完全重制《光环:战斗进化》的原版战役,还包含诸多全新惊喜。 原版《光环:战斗进化》是游戏史上的经典之作,更是助力定义第一人称射击体验的文化符号。对光环工作室(Halo Studios)而言,无论是面向新玩家还是老粉丝,重…

    2025年12月6日
    000
  • PDF文档中隐藏下载链接真实路径的教程

    本教程旨在解决pdf文档中下载链接显示完整url路径的问题,尤其是在鼠标悬停时暴露动态参数。文章将解释为何传统的.htaccess重写或javascript方法不适用于pdf环境,并提出一种利用html “标签的`title`属性来控制链接提示文本的有效策略,从而在不影响功能的前提下,优…

    2025年12月6日 后端开发
    000
  • win11提示“无法加载文件或程序集”错误怎么办_Win11文件或程序集无法加载修复方法

    首先运行SFC扫描修复系统文件,若无效则使用DISM修复系统映像,接着重装Visual C++运行库,再通过设置修复或重置出错应用,最后可手动替换并注册缺失的程序集文件。 如果您尝试在Windows 11系统中运行某个程序或打开文件时,收到“无法加载文件或程序集”的错误提示,则可能是由于系统文件损坏…

    2025年12月6日
    000
  • ECDSA签名生成:Java到C#的JcaPEMKeyConverter替代方案

    本文针对将Java ECDSA签名生成代码迁移到C#时,`JcaPEMKeyConverter`类的替代方案问题,提供了一种基于BouncyCastle库的解决方案。通过`Org.BouncyCastle.OpenSsl.PemReader`读取私钥,并使用`SignerUtilities`类进行签…

    2025年12月6日 java
    000
  • Linux文件系统readlink命令使用方法

    readlink命令用于解析符号链接指向的实际路径,基本用法为readlink 文件名,-f选项可递归解析为绝对路径,常用于脚本中获取真实文件位置,如readlink -f “$0″确定脚本自身路径,结合which命令可追踪命令真实执行文件,-n、-q、-s等选项支持静默处理…

    2025年12月6日 运维
    000
  • VSCode后端:Flask应用调试指南

    答案:配置VSCode调试Flask需安装Flask、编写入口文件、在launch.json中设置调试参数,然后设断点并启动调试会话。具体步骤包括创建launch.json文件并配置program、env和args等选项,确保使用正确Python解释器,避免端口占用,最后通过运行和调试面板启动应用,…

    2025年12月6日 开发工具
    000
  • 如何管理和同步VSCode的扩展配置,以便在新设备上快速恢复开发环境?

    使用 Settings Sync 是最快方式,通过 GitHub 账号同步扩展、设置、快捷键和代码片段;也可手动导出扩展列表(code –list-extensions > extensions.txt)并在新设备安装,结合备份 settings.json 等配置文件实现环境快速恢…

    2025年12月6日 开发工具
    000
  • 无XHR请求时提取JavaScript动态生成内容的教程

    本教程探讨了在爬取网页时,当目标内容由javascript动态生成且无明显xhr请求时的数据提取策略。我们将揭示数据可能已内嵌于初始html或js代码中,并演示如何通过检查页面源代码、识别关键标识符来定位并提取这些隐藏的json格式数据,从而实现高效的网页内容抓取。 挑战:JavaScript动态内…

    2025年12月6日 web前端
    000
  • VSCode扩展包管理依赖解析

    VSCode扩展依赖通过package.json中的extensionDependencies声明,安装时自动解析并提示用户安装所需扩展,确保按顺序激活且禁止循环依赖,依赖间通过contributes.api共享功能,使用vsce打包时需手动处理生产依赖和性能优化,最终实现扩展间的协同运行与API调…

    2025年12月6日 开发工具
    000
  • 《绝地潜兵2》要出中文配音 Steam遭差评轰炸

    《绝地潜兵2》官方宣布正在制作中文配音,PlayStation中国官博也同步转发了这一消息。这是继小岛秀夫的《死亡搁浅2》之后,今年第二款支持中文配音的PlayStation第一方作品。 在发布的宣传视频中,Arrowhead工作室的两位制作人向中国玩家致谢,表示:“你好中国,我们很高兴看到各位加入…

    2025年12月6日 游戏教程
    000
  • 光环工作室确认在新作中使用生成式AI:只是用来优化工作流程的工具!

    近日,《光环》系列的开山之作《光环:战斗进化》迎来重制版——《光环:战役进化》,官方已正式宣布该游戏将于2026年登陆xbox series x/s、pc及playstation 5平台,并将首次支持简体中文配音!steam商店页面>>> 此前有爆料人RebsGaming透露,Ha…

    2025年12月6日 游戏教程
    000
  • VSCode代码转换:编码格式处理

    遇到乱码时先查看文件编码,点击右下角编码名称选择“通过编码重新打开”,尝试 UTF-8、GBK 等常用编码以正确显示内容;2. 确认后可选择“通过编码保存”将文件转换为 UTF-8 等标准编码,便于跨平台协作;3. 为避免重复操作,可在设置中将 “files.encoding&#8221…

    2025年12月6日 开发工具
    000
  • 从动态网页中提取JavaScript生成的内容

    本文旨在提供一种从动态网页中提取由JavaScript生成的内容的方法。通过分析网页的初始加载代码,寻找嵌入其中的JSON数据,我们可以有效地抓取目标信息,即使网页不使用额外的XHR请求。本文将详细介绍如何定位和提取这些数据,并提供相应的示例。 很多现代网站使用JavaScript动态生成内容,这给…

    2025年12月6日 web前端
    000

发表回复

登录后才能评论
关注微信