2024年10月

1. 前言

本篇我们讲解
2个月搞定计算机二级C语言
——真题 6

真题6-程序评分

2. 程序填空题

2.1 题目要求

真题6-程序填空

2.2 提供的代码

#include <stdio.h>
unsigned long fun(unsigned long n) {
    unsigned long x = 0;
    int           t;
    while (n) {
        t = n % 10;
        /**********found**********/
        if (t % 2 == __1__)
        /**********found**********/
            x = __2__ + t;
        /**********found**********/
        n = __3__;
    }
    return x;
}
main() {
    unsigned long n = -1;
    while (n > 99999999 || n < 0) {
        printf("Please input(0<n<100000000): ");
        scanf("%ld", &n);
    }
    printf("\nThe result is: %ld\n", fun(n));
    getchar();
}

2.3 解题思路

题目要求将各位上的偶数取出,需要将各位的数取出后再判断该数是否为偶数,偶数可以通过该数除 2 取余的结果来判断,如果结果为 0 则是偶数,为 1 则是奇数。

第(1)处填空:

此处是用来判断取出的一位数字是否为偶数,上面说过如何判断是否为偶数,所以这里我们需要让它判断是否等于 0。

if (t % 2 == 0)

第(2)处填空:

这里我们要实现的功能是重组一个新的数,题目要求是相反的顺序,所以我们需要每次让
x * 10
,下面程序的代码中演示:

// t = 6 时符合条件,x = 0 * 10 + 6,则 x 为 6
// t = 9 时不符合条件,则执行 n = n / 10;
// t = 4 时符合条件,x = 6 * 10 + 4,则 x 为 64
// t = 8 时符合条件,x = 64 * 10 + 8,则 x 为 648
// 下同
x = x * 10 + t;

第(3)处填空:

这里我们要实现的是将前面取出的个位数除去,原本的十位变为个位,以便下次循环进入时
t = n % 10;
取出的是一个新数值。

n = n / 10;

2.4 代码实现

填写完整的代码:

#include <stdio.h>
unsigned long fun(unsigned long n) {
    unsigned long x = 0;
    int           t;
    while (n) {
        t = n % 10;
        /**********found**********/
        if (t % 2 == 0)      // 除 2 取余等于 0,则为整数
        /**********found**********/
            x = x * 10 + t;  // 将 t 的值加到 x 的个位
        /**********found**********/
        n = n / 10;  // 逐次除去 n 的个位
    }
    return x;
}
main() {
    unsigned long n = -1;
    while (n > 99999999 || n < 0) {
        printf("Please input(0<n<100000000): ");
        scanf("%ld", &n);
    }
    printf("\nThe result is: %ld\n", fun(n));
    getchar();
    getchar();
}

提示:为确保代码正常运行,请在题库编程环境的对应题目中进行测试和运行。

3. 程序修改题

3.1 题目要求

真题6-程序修改

3.2 提供的代码

#include <stdio.h>
void fun(char* p) {
    char max, *q;
    int  i = 0;
    max    = p[i];
    q      = p;
    while (p[i] != 0) {
        if (max < p[i]) {
            max = p[i];
    /**********found**********/
            q = p + i
        }
        i++;
    }
    /**********found**********/
    wihle(q > p) {
        *q = *(q - 1);
        q--;
    }
    p[0] = max;
}
main() {
    char str[80];
    printf("Enter a string:  ");
    gets(str);
    printf("\nThe original string:      ");
    puts(str);
    fun(str);
    printf("\nThe string after moving:  ");
    puts(str);
    printf("\n\n");
    getchar();
}

3.3 解题思路

本题只有两处错误,一个语法错误,一个关键字写错了,都是初学者经常犯的错误,在编程时需要注意一下。

第(1)处修改:

这里是因为语句后面没有加
;
,加上就好。

q = p + i;

第(2)处修改:

这句是因为
while
写错了。对于这种错误,更有可能出错的是
main
,会有人写成
mian
导致程序出错,也要注意。

while(q > p) {

3.4 代码实现

修改后的代码:

#include <stdio.h>
void fun(char* p) {
    char max, *q;
    int  i = 0;
    max    = p[i];
    q      = p;
    while (p[i] != 0) {
        if (max < p[i]) {
            max = p[i];
    /**********found**********/
            q = p + i;
        }
        i++;
    }
    /**********found**********/
    while(q > p) {
        *q = *(q - 1);
        q--;
    }
    p[0] = max;
}
main() {
    char str[80];
    printf("Enter a string:  ");
    gets(str);
    printf("\nThe original string:      ");
    puts(str);
    fun(str);
    printf("\nThe string after moving:  ");
    puts(str);
    printf("\n\n");
    getchar();
}

提示:为确保代码正常运行,请在题库编程环境的对应题目中进行测试和运行。

4. 程序设计题

4.1 题目要求

真题6-程序设计

4.2 提供的代码

#include <stdio.h>
#pragma warning(disable : 4996)
#define N 10
int fun(int x[], int e, int* sum) {
}
main() {
    void NONO();
    int  x[N] = {1, 7, 8, 6, 10, 15, 11, 13, 29, 31}, e = 3, n, sum;
    n = fun(x, e, &sum);
    printf("n=%d,sum=%d\n", n, sum);
    NONO();
}

void NONO() {
    /* 请在此函数内打开文件,输入测试数据,调用 fun 函数,
       输出数据,关闭文件。 */
    int   i, j, x[10], n, e, sum;
    FILE *rf, *wf;

    rf = fopen("in.dat", "r");
    wf = fopen("out.dat", "w");
    for (i = 0; i < 5; i++) {
        for (j = 0; j < 10; j++)
            fscanf(rf, "%d ", &x[j]);
        fscanf(rf, "%d", &e);
        n = fun(x, e, &sum);
        fprintf(wf, "%d, %d\n", n, sum);
    }
    fclose(rf);
    fclose(wf);
}

4.3 解题思路

根据题目提供的数组
x
、整数
e
和输出结果可以看出是要求数组中能整除
e
的,也就是整除
e
的倍数,所以本题的题目描述错了。

那么我们只需要遍历数组
x
,判断元素是否能整除
e
,是则计数加 1,否则求和加该元素存储的数。

4.4 代码实现

填写完整的代码:

#include <stdio.h>
#pragma warning(disable : 4996)
#define N 10
int fun(int x[], int e, int* sum) {
    int count = 0, i = 0;

    *sum = 0;   // 将 sum 存储的数据清空,防止出现垃圾值

    for (i = 0; i < N; i++) {   // 遍历数组 X,范围取 0 ~ (N-1)
        if (x[i] % e == 0) {    // 判断是否能整除 e
            count++;            // 计数
        } else {
            *sum += x[i];       // 求和
        }
    }

    return count;
}
main() {
    void NONO();
    int  x[N] = {1, 7, 8, 6, 10, 15, 11, 13, 29, 31}, e = 3, n, sum;
    n = fun(x, e, &sum);
    printf("n=%d,sum=%d\n", n, sum);
    NONO();
    getchar();
    getchar();
}

void NONO() {
    /* 请在此函数内打开文件,输入测试数据,调用 fun 函数,
       输出数据,关闭文件。 */
    int   i, j, x[10], n, e, sum;
    FILE *rf, *wf;

    rf = fopen("in.dat", "r");
    wf = fopen("out.dat", "w");
    for (i = 0; i < 5; i++) {
        for (j = 0; j < 10; j++)
            fscanf(rf, "%d ", &x[j]);
        fscanf(rf, "%d", &e);
        n = fun(x, e, &sum);
        fprintf(wf, "%d, %d\n", n, sum);
    }
    fclose(rf);
    fclose(wf);
}

提示:为确保代码正常运行,请在题库编程环境的对应题目中进行测试和运行。

5. 后记

本篇博客到这就结束了,如果您有疑问或建议欢迎您在留言区留言。

1.前言

我是一位运维工程师,用通义灵码个人版的@workspace 和 @terminal 的能力做快速了解一个工程、查找工程内的实现逻辑,以及执行指令不知道如何写,或者不清楚某个指令的意思,对比之前没有灵码,现在提效了20%,再也不需要“百度一下”或者“谷歌”了,使用的具体流程如下:

想象一下,开发同事完成项目上线后,提交给你运维,之前你根本不知道从何处入手,如果你正在某个代码仓库里工作,灵码可以预先感知和分析你的代码工程。这样,当你需要实现新的需求或重构功能时,通过询问灵码,它就像一位对这个项目已了如指掌的老朋友,可以快速帮你生成一些重复度很高的代码,或者是提供需求实现思路的头脑风暴和示例代码,成为真正“懂你”的编码搭子。

近期,通义灵码上线新能力:@workspace,基于本地代码库的 RAG 技术,让灵码深度感知你的代码库,更懂你的需求,一起结对编程。我们首先了解一下它可以做什么呢?

2.@workspace能帮我做什么?

2.1快速上手熟悉新的代码工程

可以询问当前项目的任何细节,如“项目的结构和功能是什么?”,“订单管理的代码实现在哪”,“如何构建和运行当前项目”。与灵码对话,消除陌生感,快速上手新项目。

2.2头脑风暴,协助实现新需求

可以结合当前代码工程,分析用户的需求问题,提供实现代码或头脑风暴。如“如何增加用户鉴权逻辑”,“实现一个功能,批量添加我的收藏里的商品到购物车”,“增加删除文章的接口,包含后端controller,service,mapper,前端vue的变更”等等,让灵码快速提供解决方案及代码。

场景一:新项目通过@workspace快速上手熟悉

问题1:介绍下项目的结构和功能,如下图所示:

问题 2: 如何构建和运行本工程,如下图所示:

场景二:@workspace协助快速实现新需求

问题 1:增加删除文章的接口,包含后端controller,service,mapper,前端vue的变更,如下图所示:

问题 2:请帮我实现一个功能,使用手机号+验证码登录,如果手机号没有注册过账号,则自动用手机号注册,如下图所示:

3.@terminal能做什么?

3.1@terminal 问答

当你遇到执行指令不知道如何写,或者不清楚某个指令的意思时,可以在智能问答窗口中通过 @ 可唤起 @terminal,选择后使用自然语言描述你的需要指令诉求,通义灵码将可以生成你需要的命令。生成指令后,你可以一键插入到 teminal 中进行执行或让通义灵码继续解释。当然,你也可以在选择 @terminal 后,输入指令让通义灵码生成指令解释。如下图所示:

我们拿一个VUE 项目来试试,通义灵码可以轻松分析整个项目的完整结构,然后详细解释各个文件, 这样看源码再也不是问题了,即使是小白也能在短时间内看懂项目的复杂逻辑。如下图所示:

4.小结

通义灵码不仅能自动续写代码、生成单元测试,还能优化代码、生成详细的注释。你只需要专注于核心业务逻辑,通义灵码会为你处理那些耗时的重复性工作。无论是Java、Python 还是其他主流编程语言,它都能胜任,简直就是开发者的贴心助手。

通义灵码支持 200 种编程语言,兼容 Visual Studio Code、Visual Studio、JetBrains IDEs 等主流编程工具,只要你写代码,它就可以想你所想,为你带来全新开发体验。

前言

随着制造业的不断发展,企业对于生产效率和管理水平的要求越来越高。

EasyMES 是一款基于 .NET 6 开发的零部件离散型 MES(Manufacturing Execution System)+ WMS(Warehouse Management System)系统,可以帮助企业提升生产效率和管理水平。

本文将详细介绍 EasyMES 的功能、部署方式以及使用说明。

系统介绍

EasyMES 是一款专为零部件离散型生产设计的综合管理系统,涵盖了从生产到仓储的各个环节。系统集成了钉钉消息推送功能,支持 MySQL 和 SQL Server 数据库,并采用 MIT 协议开源。

系统功能

1、MES 功能

离散型零部件制造管理:实现生产过程的精细化管理,提高生产效率。

2、WMS 功能

仓储管理:支持入库、出库、盘点、移库等功能,并具备打印能力,便于库存管理。

3、APS 功能

自动排程系统:根据订单自动安排采购计划、交付计划、生产计划,实现资源的合理调配。

4、调度系统

协同操作调度:每一个生产节点完成后,自动通知下一个节点的操作人员,保证流程顺畅。

5、追溯系统

制造过程追溯:通过批号、容器等信息,追踪制造过程,确保产品质量可控。

6、看板系统

内置看板自适应模板:支持轮播、定点投放等功能,包含工位操作看板、物流指示看板,方便现场管理和操作。

系统框架

1、MES 框架

2、MES 后台管理系统

3、MES 小程序

4、MES API接口

整体流程

MES业务解决方案的总体流程,涵盖了项目管理、工艺准备、生产计划管理、现场管理等多个方面。

生产流程

APS

制造企业中用于优化生产计划和调度的重要工具。

系统源码

系统源码包含了以下内容,可下载查看和学习:

  • 系统代码:完整的源代码文件。
  • 数据库文件:系统所需的数据库脚本和配置文件。
  • 文档说明:详细的系统说明文档。
  • 部署教程:详细的部署步骤和指南。
  • 使用指南:系统的基本操作和使用方法。
  • 操作手册:全面的操作手册,涵盖所有功能和流程。

项目效果

1、常规管理

2、MES系统

3、移动端

4、数据大屏

5、设备扫码看板

系统部署

1、Web和WebAPI需要同时部署

2、Web和WebAPI设置跨域、主程序地址

3、设置钉钉,目前系统使用的4个设置,逐个修改,在钉钉开发平台创建设置服务器IP为白名单,使用旧版,可以找到设置三方WEB登录


4、修改钉钉小程序,指向服务器API和WEB地址(图片服务器),可以设置多个工厂,后台服务器不同部署

5、基础数据维护,物料维护、工序维护、设备维护、物料bom维护(工序消耗)、物料设备维护(模具绑定物料)、库区维护、库位维护、流转箱维护(追溯使用)、设备库区维护(产出存放)、设备物料绑定(生产设定)、工序设备绑定,这些组成最基本的数据。

6、班别,目前存放与字典中,请自行修改,目前白夜班模式。

项目地址

Gitee:
https://gitee.com/qian_wei_hong/EasyMES

总结

EasyMES 是一款功能全面的零部件离散型 MES+WMS 系统,它不仅能够帮助企业在生产过程中实现高效的管理和调度,还能通过仓储管理和自动排程等功能提升企业的运营效率。

系统采用开源的方式发布,可以促进制造业信息化建设的发展。

希望本文能帮助大家更好地理解和使用 EasyMES,如果您有任何问题或建议,欢迎加入群进行交流。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号
[DotNet技术匠]
社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!

大家好,我是 V 哥,今天的内容来聊一聊 ZGC,Java Z Garbage Collector(ZGC)是一个低延迟垃圾收集器,旨在优化内存管理,主要用于大内存应用场景。它通过以下几个关键创新,彻底改变了传统Java的内存管理方式:

V 哥总结的以下5点,欢迎一起讨论。

1.
极低的暂停时间

  • ZGC的暂停时间一般保持在10毫秒以下,且不随堆大小增加而延长,这对需要实时响应的大型应用特别重要。
  • 这是通过并发标记和并发重分配技术实现的,不用停顿整个应用来完成垃圾收集任务。

2.
大内存支持

  • ZGC设计之初就是为TB级堆的内存管理而生。相较于G1或CMS等收集器,它能处理更大的内存,且不影响性能。

3.
着色指针(Colored Pointers)

  • ZGC使用了64位指针的高位来标记对象的不同状态,实现了对象的可移动性与生命周期管理。这种方法允许对象在不更新引用的情况下在堆内移动。
  • 着色指针与加载屏障结合,使得ZGC能高效追踪对象的状态,并确保内存管理和对象移动的一致性。

4.
并发压缩和去碎片化

  • ZGC可以在不停止应用的情况下进行堆内存的压缩与碎片整理。这提高了内存的利用率,使长时间运行的Java应用能够持续稳定地运行。

5.
分代优化

  • 虽然ZGC并不区分年轻代和老年代,但其高效的内存管理方式使得其在各种生命周期的对象处理上都具备优势,尤其适合那些生命周期难以预测的对象。

使用场景

  • ZGC适合内存需求庞大的服务,如实时数据分析、高性能服务器、在线交易系统等,对低延迟、实时响应有较高要求的Java应用。

ZGC极大提升了Java的内存管理能力,使开发者在处理大规模内存应用时有了更多选择,进一步优化了应用的性能和响应速度。

在实时数据分析、高性能服务器和在线交易系统这些场景中,低延迟、高并发和稳定性是至关重要的,而ZGC的特性能够显著优化这些应用的性能。下面我们具体看看ZGC在每个场景中的应用方式。

1.
实时数据分析

  • 特点
    :实时数据分析通常需要处理大量数据,内存需求大,对响应时间的要求较高。特别是在数据流分析、事件处理和大数据统计等应用中,GC停顿会直接影响到数据处理的实时性。
  • ZGC优化

    • 低延迟
      :ZGC的暂停时间保持在10毫秒以下,且不随堆大小增加。这意味着即使数据量大幅增长,ZGC的GC停顿时间仍然可以忽略不计,从而保证数据分析过程的流畅。
    • 并发内存压缩
      :ZGC能够在GC时并发进行内存压缩和去碎片化,避免长时间运行的应用在堆内存碎片化后造成的性能下降。这对实时数据分析平台尤其重要,因为这些应用通常需要长时间持续运行并处理大量动态生成的对象。
    • 大内存支持
      :实时数据分析系统往往需要处理TB级别的数据,ZGC可以高效管理大容量内存,从而保障了大数据分析应用的内存需求。

2.
高性能服务器

  • 特点
    :高性能服务器需要在高并发的环境下快速响应请求,特别是在处理海量短周期请求的服务中,GC停顿可能会造成用户请求阻塞和系统响应缓慢。
  • ZGC优化

    • 并发GC处理
      :ZGC的标记、压缩和对象移动等操作几乎完全并发,不会因GC停顿影响服务响应,从而保证了服务器的高并发性能。
    • 着色指针
      :ZGC使用64位的着色指针,允许对象在不更新所有引用的情况下实现迁移。这不仅简化了内存管理,还减少了内存碎片,使得服务在高并发场景中能更稳定高效地分配内存。
    • 弹性扩展
      :对于高性能服务器来说,当请求数量暴增时,ZGC能高效管理和分配大内存池,以满足高峰期的资源需求。

3.
在线交易系统

  • 特点
    :在线交易系统(如金融和电商)对延迟要求极高,因为任何GC停顿都会直接影响用户体验甚至引发交易错误。交易系统还需保障长期稳定运行,避免内存泄漏或性能下降。
  • ZGC优化

    • 极低GC停顿时间
      :ZGC的停顿时间通常在几毫秒级别,这种低延迟特点在交易系统中能大幅减少因GC造成的交易延迟,保证交易过程的顺畅。
    • 安全的对象移动
      :ZGC的着色指针和加载屏障实现了对象的并发移动,不需要在GC期间暂停应用。这种对象移动特性避免了长时间运行的在线系统因GC停顿导致的交易卡顿或延迟。
    • 高吞吐量和可靠性
      :ZGC的并发压缩和内存碎片管理避免了老年代碎片化带来的性能下降,使交易系统在高并发、长时间运行环境下能保持稳定的内存分配与管理能力。

小结

  • 低延迟、高并发、稳定性保障
    :ZGC以低延迟的GC停顿、高效的大内存管理和并发垃圾收集等特性,能够有效应对实时数据分析、高性能服务器和在线交易系统的挑战。
  • 降低内存碎片化风险
    :ZGC的并发内存压缩和去碎片化机制确保在长时间运行下应用仍保持稳定和高效。
  • 适应复杂多样的场景
    :无论是需快速响应的实时系统还是追求稳定性和低延迟的在线服务,ZGC的先进内存管理方式都提供了显著的性能提升,使得应用无需受限于传统GC的限制。

如何使用 ZGC

要在Java中启用Z Garbage Collector(ZGC),咱们可以在启动Java应用时通过以下步骤配置JVM参数来开启ZGC,看 V哥一一道来。

1.
检查JVM版本

  • ZGC在JDK 11及以上版本中可用(并在JDK 15及更高版本中完全稳定)。
  • 确认您的JVM版本支持ZGC。

可以通过命令行检查版本:

   java -version

2.
启动参数

  • 使用
    -XX:+UseZGC
    参数启用ZGC。
  • 可以在命令行中添加该参数来启动应用,例如V 哥有一个应用:
     java -XX:+UseZGC -Xms<size> -Xmx<size> -jar  vg-app.jar
  • -Xms<size>

    -Xmx<size>
    设置堆内存的最小和最大值。ZGC通常用于大堆内存环境,可以根据需求设置堆大小,例如
    -Xmx16g
    表示最大堆内存为16GB。

3.
可选参数

ZGC还支持一些优化参数,可以根据需求调整:

  • 限制GC线程数量

    -XX:ConcGCThreads=<n>
    ,默认线程数量与CPU核心数相关,但可以根据应用负载适当调整。
  • 日志输出
    :开启详细的GC日志帮助监控ZGC性能。
     java -XX:+UseZGC -Xlog:gc -Xlog:gc+phases -jar vg-app.jar
  • 最大暂停时间目标
    :虽然ZGC停顿时间极低,但仍可设置目标暂停时间,如
    -XX:MaxGCPauseMillis=<time>
    。ZGC会尽量保持在目标之下,但并不严格保证。

4.
启动测试

使用
-Xlog:gc
查看GC日志,以确认ZGC已启用并监控GC性能。可以运行应用后在日志中查看是否显示类似于以下信息:

   Using ZGC
   [gc,start      ] GC(0) Start

示例完整命令

假设应用需要16GB的堆空间并希望监控GC日志,完整命令如下:

java -XX:+UseZGC -Xms16g -Xmx16g -Xlog:gc -jar vg-app.jar

注意事项

  • 操作系统要求
    :ZGC仅在64位的Linux、macOS、Windows上支持,需确保您的操作系统兼容。
  • 硬件要求
    :ZGC对大内存的硬件支持要求较高,通常适合使用大于8GB的堆。

over,开启ZGC并配置适当的参数后,Java应用将在低暂停时间的大内存环境中运行,适合实时数据分析、高并发服务器等应用。

现在用的电脑是 21 年配的,当时并没有 AI 相关的需求,各种各样的原因吧,抉择后选择了 AMD 的显卡,但在 2024 年的今天,使用 AI 进行一些工作已不再是什么罕见的需求,所以我也想尝试一下,但发现 AMD 显卡却处处碰壁,研究后发现,经过各方面的努力,AMD 显卡在 AI 方面的支持已经有了很大的进步,但是由于历史原因,NVIDIA 显卡在这方面的支持更加完善,所以我在这里记录一下我在 Windows 环境下使用 AMD 显卡运行 AI 程序的过程。

这些步骤现在已经可以通过整合包完成,搜索“绘世整合包”就能找到,这里只是记录一下学习过程,方便学习查阅。

CUDA

首先要说的就是 CUDA,不同于 CPU,GPU 的并行计算能力更强,而 CUDA 就是 NVIDIA 推出的并行计算平台和编程模型,它可以让开发者对 GPU 进行编程,然后在 NVIDIA 的 GPU 上运行,这样就可以充分利用 GPU 的并行计算能力,而不是只用来显示图形。CUDA 是 NVIDIA 的专利技术,所以 AMD 显卡是无法使用 CUDA 的。并且,得益于比较早的推出时间,CUDA 的生态系统也更加完善,有很多的库和框架都是基于 CUDA 的,比如 TensorFlow、PyTorch 等。

而当下热门的 AI 绘画工具 Stable Diffusion 就需要用到 PyTorch,换句话说,如果 AMD 显卡能够支持 PyTorch,那么就可以运行 Stable Diffusion。

Linux

首先我了解到在 Linux 环境下,AMD 显卡是可以运行 PyTorch 的,而且 AMD 也推出了 ROCm,这是一个开源的并行计算平台,可以让 AMD 显卡运行 PyTorch,但是在 Windows 环境下,ROCm 并不支持 PyTorch,所以我们需要另辟蹊径。

研究过程中也考虑了装双系统,但以双系统的方式来使用 AI 绘画较为麻烦,可能过一段时间就不想再切换系统了,所以我还是更希望在 Windows 环境下使用 AMD 显卡运行 Stable Diffusion。

Linux 环境下的运行效率据说已经没有什么损耗了,相关资料很多,这里不放了。

DirectML

2023 年的方案里,能看到名为 pytorch-directml 的项目,这是一个 PyTorch 的后端,可以让 PyTorch 在 Windows 环境下使用 DirectML 运行,而 DirectML 是微软推出的一个机器学习加速库,可以让 PyTorch 在 Windows 环境下使用 AMD 显卡运行。

在经过社区的努力后,克服重重困难,pytorch-directml 项目已经可以在 Windows 环境下使用 AMD 显卡运行 PyTorch 了,这就为我们提供了一个在 Windows 环境下使用 AMD 显卡运行 Stable Diffusion 的方案。

stable-diffusion 最有名的整合包
AUTOMATIC1111/stable-diffusion-webui
的 wiki 中也有 AMD 显卡的使用说明,但是这个项目并没有直接支持 AMD 显卡,所以我们需要使用一个 fork 项目。wiki 截图如下:

通过链接跳转到对应项目,发现名称已经变了——
lshqqytiger/stable-diffusion-webui-amdgpu
,这个项目早期应该是叫做
stable-diffusion-webui-directml
,可以看出现在并不仅限于使用 pytorch-directml,后面我们会提到。

下图是 GPU-Z 的截图,可以看到显卡对 DirectML 以及 CUDA 的支持情况,虽然本文一直说的是 AMD 显卡,但实际上 Intel 的显卡也有一部分支持 DirectML,理论上也是可以使用这个方案的,不过案例较少,没有了解,这里不详细说明了。

参考资料:

在 windows 上通过 pytorch-directml 利用 AMD 显卡加速 stable-diffusion

ROCm

AMD ROCm 是一个开放式软件栈,包含多种驱动程序、开发工具和 API,可为从底层内核到最终用户应用的 GPU 编程提供助力。ROCm 已针对生成式 AI 和 HPC 应用进行了优化,并且能够帮助轻松迁移现有代码。

定位应该和 CUDA 类似,这个只要到官网下载安装就行了,目前的 Stable Diffusion 应该是只能使用 5.7 版本的,下载包命名的时间是 23 年 Q4。

PyTorch

PyTorch 是一种用于构建深度学习模型的功能完备框架,是一种通常用于图像识别和语言处理等应用程序的机器学习。使用 Python 编写,因此对于大多数机器学习开发者而言,学习和使用起来相对简单。PyTorch 的独特之处在于,它完全支持 GPU,并且使用反向模式自动微分技术,因此可以动态修改计算图形。这使其成为快速实验和原型设计的常用选择。

如果使用 DirectML 方案,那么就需要安装 pytorch_directml:

pip install torch-directml

Python

既然反复提到 PyTorch,那这里在说一下 Python,相关学习资料多如牛毛,这里不过多介绍了,主要说一下版本问题,与前端的 Node.js 环境类似,不同版本适配情况也不同,能做的工作也不一样,所以需要的一个类似版本切换的工具,Node.js 使用的是 nvm,Python 可以用 Conda,原理不太一样,Conda 是创建一个对应版本的虚拟环境,然后在虚拟环境中安装对应版本的 Python,这样就可以在不同的项目中使用不同的 Python 版本了。如果不是经常用到 Python 开发的话,使用 MiniConda 就可以了,这个是一个轻量级的 Conda,只包含了最基本的功能,不会占用太多空间。

  1. 下载安装

latest-miniconda-installer-links

  1. 创建虚拟环境

上面的 DirectML 方案中,Wiki 有提到推荐 Python 3.10.6

# 创建虚拟环境
conda create -n pytdml python=3.10.6

# 第一次使用需要初始化
conda init

# 激活虚拟环境
conda activate pytdml

# 安装 pytorch_directml
pip install torch-directml

参考资料:

在 Windows 上通过 DirectML 启用 PyTorch

ZLUDA

其实说了那么多 DirectML,但是实际上这个方案现在已经不是最好的了,而且 DirectML 仍然不能将 AMD 显卡的性能全部发挥出来,今年 2 月份,
ZLUDA
发布了 AMD 显卡版本,可以让 AMD 显卡运行 CUDA 程序,这样就可以在 Windows 环境下使用 AMD 显卡运行 Stable Diffusion 了。

ZLUDA 是非 NVIDIA GPU 上 CUDA 的替代方案。ZLUDA 允许使用非 NVIDIA GPU 运行未修改的 CUDA 应用程序,具有接近原生性能。

理论上,只需安装好 ROCm,然后直接使用 CUDA 版的 pytorch,再用 lshqqytiger 编译的版本替换对应的 CUDA dll 文件,即可直接在 Windows 上运行。

lshqqytiger 版本:
https://github.com/lshqqytiger/ZLUDA

另外,webui 推荐 SD.NEXT
automatic
,这个版本对 AMD 显卡支持更好,我最后是用这个版本跑起来的。运行 webui.bat,默认启动在 7860 端口,第一次启动要编译十几二十分钟,耐心等待,界面如下图:

我之前已经挂载过模型了,如果第一次进入可能会让你选择一个模型。加个提示词随便输出一张,尺寸最好不要太大,512×512 就行,原因会在另一篇专门讲生成的文章里说:

可以看到 GPU 的占用已经上去了,说明此时已经是在用显卡进行计算了:

总结

  1. 安装 ROCm

  2. 安装 Python

  3. 安装 PyTorch

  4. 安装 ZLUDA(主要是配置环境变量)

  5. 替换 CUDA dll 文件

  6. 运行 webui(后来发现整合包好像可以自动处理 PyTorch 等,我这边已经装好了就没法验证)

这一篇主要还是记录一下自己的学习过程,后续在生成方面就打算直接用整合好的包了,功能更加全面,会比自己搭建的方便一些,这样能更集中精力在具体的生成上,而不是在环境上浪费太多时间。