主頁 >  其他 > 有效地移位或大位向量

有效地移位或大位向量

2021-11-26 06:45:22 其他

我有一個很大的記憶體陣列作為一些指標uint64_t * arr(加上大小),它代表普通位。我需要非常有效地(最高性能/最快)將這些位向右移動一些從 0 到 63 的量。

通過移動整個陣列,我的意思是不移動每個元素(如a[i] <<= Shift),而是將其作為單個大位向量進行移動。換句話說,對于每個中間位置i(第一個和最后一個元素除外),我可以在回圈中執行以下操作:

dst[i] = w | (src[i] << Shift);
w = src[i] >> (64 - Shift);

wherew是一些臨時變數,保存前一個陣列元素的右移值。

上面的這個解決方案簡單明了。但我需要更高效的東西,因為我有千兆位元組的資料。

理想情況下是使用一些 SIMD 指令,所以我正在尋找專家的 SIMD 建議。我需要為所有四種流行的指令集實作移位代碼 - SSE-SSE4.2 / AVX / AVX-2 / AVX-512。

但據我所知,例如對于 SSE2,只存在_mm_slli_si128()內在/指令,它僅按 8 的倍數移動(換句話說,位元組移動)。而且我需要按任意位大小進行移位,而不僅僅是位元組移位。

如果沒有 SIMD,我也可以通過使用shld reg, reg, reg指令一次移位 128 位,這允許進行 128 位移位。在 MSVC 中作為內在的__shiftleft128()實作,并生成可以在這里看到的匯編代碼

順便說一句,我需要所有 MSVC/GCC/CLang 的解決方案。

同樣在單回圈迭代中,我可以在順序操作中移動 4 或 8 個字,這將使用 CPU 流水線來加速多條指令的并行亂序執行。

如果需要,我的位向量可以與記憶體中的任意數量的位元組對齊,如果這有助于例如通過對齊讀/寫來提高 SIMD 速度。源和目標位向量存盤器也不同(非重疊)。

換句話說,我正在尋找有關如何在不同的 Intel CPU 上最有效(最高效)解決我的任務的所有建議。

注意,澄清一下,我實際上必須做幾個班次,而不僅僅是單班。我有大位向量X和數百個移位大小s0, s1, ..., sN,其中每個移位大小不同并且也可能很大(例如移位 100K 位),然后我想計算生成的大位向量Y = (X << s0) | (X << s1) | ... | (X << sN)我只是將 StackOverflow 的問題簡化為移動單個向量。但可能這個關于原始任務的細節非常重要。

根據@Jake'Alquimista'LEE 的要求,我決定實作一個現成的玩具最小可重復示例,說明我想要做什么,計算輸入位向量的移位src或生成或最終dst位向量。這個例子根本沒有優化,只是我的任務如何解決的一個簡單的簡單變體。為簡單起見,此示例的輸入向量很小,而不是像我這樣的千兆位元組。這是一個玩具示例,我沒有檢查它是否正確解決了任務,它可能包含小錯誤:

在線試試吧!

#include <cstdint>
#include <vector>
#include <random>

#define bit_sizeof(x) (sizeof(x) * 8)

using u64 = uint64_t;
using T = u64;

int main() {
    std::mt19937_64 rng{123};

    // Random generate source bit vector
    std::vector<T> src(100'000);
    for (size_t i = 0; i < src.size();   i)
        src[i] = rng();

    size_t const src_bitsize = src.size() * bit_sizeof(T);

    // Destination bit vector, for example twice bigger in size
    std::vector<T> dst(src.size() * 2);

    // Random generate shifts
    std::vector<u64> shifts(200);
    for (size_t i = 0; i < shifts.size();   i)
        shifts[i] = rng() % src_bitsize;

    // Right-shift that handles overflow
    auto Shr = [](auto x, size_t s) {
        return s >= bit_sizeof(x) ? 0 : (x >> s);
    };

    // Do actual Shift-Ors
    for (auto orig_shift: shifts) {
        size_t const
            word_off = orig_shift / bit_sizeof(T),
            bit_off = orig_shift % bit_sizeof(T);

        if (word_off >= dst.size())
            continue;
        
        size_t const
            lim = std::min(src.size(), dst.size() - word_off);

        T w = 0;
        
        for (size_t i = 0; i < lim;   i) {
            dst[word_off   i] |= w | (src[i] << bit_off);
            w = Shr(src[i], bit_sizeof(T) - bit_off);
        }

        // Special case of handling for last word
        if (word_off   lim < dst.size())
            dst[word_off   lim] |= w;
    }
}

My real project's current code is different from toy example above. This project already solves correctly a real-world task. I just need to do extra optimizations. Some optimizations I already did, like using OpenMP to parallelize shift-or operations on all cores. Also as said in comments, I created specialized templated functions for each shift size, 64 functions in total, and choosing one of 64 functions to do actual shift-or. Each C function has compile time value of shift size, hence compiler does extra optimizations taking into account compile time values.

uj5u.com熱心網友回復:

您可以,甚至可能不需要顯式使用 SIMD 指令。目標編譯器 GCC、CLANG 和 MSVC 以及其他編譯器如 ICC 都支持自動矢量化。雖然手動優化的程式集可以勝過編譯器生成的向量化指令,但它通常更難實作,您可能需要針對不同架構的多個版本。導致高效自動向量化指令的通用代碼是一種可以跨許多平臺移植的解決方案。

例如一個簡單的 shiftvec 版本

void shiftvec(uint64_t* dst, uint64_t* src, int size, int shift)
{
    for (int i = 0; i < size;   i,  src,  dst)
    {
        *dst = ((*src)<<shift) | (*(src 1)>>(64-shift));
    }
}

使用最新的 GCC(或 CLANG 也可以)編譯,并-O3 -std=c 11 -mavx2在程式集的核心回圈中生成 SIMD 指令

.L5:
  vmovdqu ymm4, YMMWORD PTR [rsi rax]
  vmovdqu ymm5, YMMWORD PTR [rsi 8 rax]
  vpsllq ymm0, ymm4, xmm2
  vpsrlq ymm1, ymm5, xmm3
  vpor ymm0, ymm0, ymm1
  vmovdqu YMMWORD PTR [rdi rax], ymm0
  add rax, 32
  cmp rax, rdx
  jne .L5

在 Godbolt.org 上查看:https ://godbolt.org/z/5TxhqMhnK

如果您想在 dst 中組合多個班次,這也可以概括:

void shiftvec2(uint64_t* dst, uint64_t* src1, uint64_t* src2, int size1, int size2, int shift1, int shift2)
{
    int size = size1<size2 ? size1 : size2;
    for (int i = 0; i < size;   i,  src1,  src2,  dst)
    {
        *dst = ((*src1)<<shift1) | (*(src1 1)>>(64-shift1));
        *dst |= ((*src2)<<shift2) | (*(src2 1)>>(64-shift2)); 
    }
    for (int i = size; i < size1;   i,  src1,  dst)
    {
        *dst = ((*src1)<<shift1) | (*(src1 1)>>(64-shift1));        
    }
    for (int i = size; i < size2;   i,  src2,  dst)
    {
        *dst = ((*src2)<<shift2) | (*(src2 1)>>(64-shift2));
    }
}

編譯為核心回圈:

.L38:
  vmovdqu ymm7, YMMWORD PTR [rsi rcx]
  vpsllq ymm1, ymm7, xmm4
  vmovdqu ymm7, YMMWORD PTR [rsi 8 rcx]
  vpsrlq ymm0, ymm7, xmm6
  vpor ymm1, ymm1, ymm0
  vmovdqu YMMWORD PTR [rax rcx], ymm1
  vmovdqu ymm7, YMMWORD PTR [rdx rcx]
  vpsllq ymm0, ymm7, xmm3
  vmovdqu ymm7, YMMWORD PTR [rdx 8 rcx]
  vpsrlq ymm2, ymm7, xmm5
  vpor ymm0, ymm0, ymm2
  vpor ymm0, ymm0, ymm1
  vmovdqu YMMWORD PTR [rax rcx], ymm0
  add rcx, 32
  cmp r10, rcx
  jne .L38

在一個回圈中組合多個源將減少用于加載/寫入目標的記憶體帶寬總量。您可以組合的數量限制當然受可用暫存器的限制。注意,xmm2xmm3用于shiftvec包含所述移位值,因此具有不同版本的編譯時已知的移值可以釋放那些暫存器。

此外__restrict,為每個指標使用(由 GCC、CLANG、MSVC 支持)將告訴編譯器范圍不重疊。

我最初在 MSVC 提供適當的自動矢量化代碼時遇到了問題,但似乎添加更多類似 SIMD 的結構將使其適用于所有三個所需的編譯器 GCC、CLANG 和 MSVC:

void shiftvec(uint64_t* __restrict dst, const uint64_t* __restrict src, int size, int shift)
{
    int i = 0;
    // MSVC: use steps of 2 for SSE, 4 for AVX2, 8 for AVX512
    for (; i 4 < size; i =4,dst =4,src =4)
    {
        for (int j = 0; j < 4;   j)
            *(dst j) = (*(src j))<<shift;
        for (int j = 0; j < 4;   j)
            *(dst j) |= (*(src 1)>>(64-shift));
    }
    for (; i < size;   i,  src,  dst)
    {
        *dst = ((*src)<<shift) | (*(src 1)>>(64-shift));
    }    
}

uj5u.com熱心網友回復:

我會嘗試依靠 x64 能力來讀取未對齊的地址,并且當星星正確(未)對齊時幾乎沒有明顯的損失。一個人只需要處理 (shift % 8) 或 (shift % 16) 的幾種情況——所有這些都可以用 SSE2 指令集,用零固定余數,對資料向量有一個未對齊的偏移量,并通過memcpy.

也就是說,內部回圈看起來像:

uint16_t const *ptr;
auto a = _mm_loadu_si128((__m128i*)ptr);
auto b = _mm_loadu_si128((__m128i*)(ptr   1);
a = _mm_srl_epi16(a, c);
b = _mm_sll_epi16(a, 16 - c);
_mm_storeu_si128((__m128i*)ptr, mm_or_si128(a,b));
ptr  = 8;

將這個回圈展開幾次,也許可以_mm_alignr_epi8在 SSE3 上使用來放寬記憶體帶寬(以及那些需要結合未對齊記憶體訪問結果的流水線階段):

auto a0 = w; 
auto a1 = _mm_load_si128(m128ptr   1);
auto a2 = _mm_load_si128(m128ptr   2);
auto a3 = _mm_load_si128(m128ptr   3);
auto a4 = _mm_load_si128(m128ptr   4);
auto b0 = _mm_alignr_epi8(a1, a0, 2);
auto b1 = _mm_alignr_epi8(a2, a1, 2);
auto b2 = _mm_alignr_epi8(a3, a2, 2);
auto b3 = _mm_alignr_epi8(a4, a3, 2);
// ... do the computation as above ...
w = a4;   // rotate the context

uj5u.com熱心網友回復:

換句話說,我正在尋找有關如何在不同的 Intel CPU 上最有效(最高效)解決我的任務的所有建議。

效率的關鍵是懶惰。懶惰的關鍵是撒謊——假裝你改變了,實際上沒有做任何改變。

對于初始示例(僅用于說明概念),請考慮:

struct Thingy {
    int ignored_bits;
    uint64_t data[];
}

void shift_right(struct Thingy * thing, int count) {
    thing->ignored_bits  = count;
}

void shift_left(struct Thingy * thing, int count) {
    thing->ignored_bits -= count;
}

int get_bit(struct Thingy * thing, int bit_number) {
    bit_number  = thing->ignored_bits;
    return !!(thing->data[bit_number / 64] & (1 << bit_number % 64));
}

對于實際代碼,您需要關心各種細節 - 您可能希望從陣列開頭的備用位(和非零位ignored_bits)開始,以便您可以假裝右移;對于每個小的移位,您可能想要清除“移入”位(否則它將表現得像浮點數 - 例如(5.0 << 8) >> 8) == 5.0);如果/何時ignored_bits超出某個范圍,您可能需要一個大的memcpy()等等。

為了更多的樂趣;濫用低級記憶體管理 - 使用VirtualAlloc()(Windows)或mmap()(Linux)保留一個巨大的空間,然后將您的陣列放在空間的中間,然后根據需要在陣列的開始/結束處分配/釋放頁面;這樣您只需要memcpy()在原始位向左/向右“移動”數十億位之后即可。

當然,結果是它會使代碼的其他部分復雜化——例如,將 2 個位域 OR 在一起,您必須進行棘手的“獲取 A;移位 A 以匹配 B;結果 = A OR B”調整。這不是性能的交易破壞者。

uj5u.com熱心網友回復:

#include <cstdint>
#include <immintrin.h>

template<unsigned Shift>
void foo(uint64_t* __restrict pDst, const uint64_t* __restrict pSrc, intptr_t size)
{
    uint64_t* pSrc0, * pSrc1, * pSrc2, * pSrc3, * pDst0, * pDst1, * pDst2, * pDst3;
    __m256i prev, current;
    intptr_t i, stride;

    stride = size >> 2;
    i = stride;

    pSrc0 = pSrc;
    pSrc1 = pSrc   stride;
    pSrc2 = pSrc   2 * stride;
    pSrc2 = pSrc   3 * stride;

    pDst0 = pDst;
    pDst1 = pDst   stride;
    pDst2 = pDst   2 * stride;
    pDst3 = pDst   3 * stride;

    prev = _mm256_set_epi64x(0, pSrc1[-1], pSrc2[-1], pSrc3[-1]);

    while (i--)
    {
        current = _mm256_set_epi64x(*pSrc0  , *pSrc1  , *pSrc2  , *pSrc3  );
        prev = _mm256_srli_epi64(prev, 64 - Shift);
        prev = _mm256_or_si256(prev, _mm256_slli_epi64(current, Shift));
        *pDst0   = _mm256_extract_epi64(prev, 3);
        *pDst1   = _mm256_extract_epi64(prev, 2);
        *pDst2   = _mm256_extract_epi64(prev, 1);
        *pDst3   = _mm256_extract_epi64(prev, 0);

        prev = current;
    }
}

您可以在 AVX2 上一次對最多四個 64 位元素執行操作(在 AVX512 上最多為八個)

如果 size 不是 4 的倍數,則最多可以處理 3 個剩余的。

PS:自動矢量化從來都不是一個合適的解決方案。

uj5u.com熱心網友回復:

不,你不能

NEON 和 AVX(512) 都支持高達 64 位元素的桶形移位操作。

但是,您可以ext使用 NEON 和alignrAVX的指令將整個 128 位向量“移位”n 位元組(8 位)

并且您應該避免使用 vector 類來提高性能,因為它只不過是對性能不利的鏈表。

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/366020.html

標籤:c performance simd sse avx

上一篇:Openpyxl、Pandas或兩者

下一篇:如何在Python中查找日期時間范圍

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more