當(dāng)前位置:首頁 > 公眾號精選 > 小林coding
[導(dǎo)讀]我們知道,現(xiàn)代CPU都是多核處理器。由于cpu核心(Kernel)讀取內(nèi)存數(shù)據(jù)較慢,于是就有了緩存的概念。我們希望針對頻繁讀寫的某個內(nèi)存變量,提升本核心的訪問速率。因此我們會給每個核心設(shè)計緩存區(qū)(Cache),緩存該變量。由于緩存硬件的讀寫速度比內(nèi)存快,所以通過這種方式可以提升變量訪問速度。

小故事

小艾吃飯路上碰上小牛,忙問:你昨天面大廠面的咋樣了?聽說他們最喜歡問多線程相關(guān)知識。

小牛說:對啊,第一個問題我就講了20分鐘,直接把面試官講服了。

小艾忙問:什么問題能講這么久?是不是問你情感經(jīng)歷了?

小牛說:…問的volatile關(guān)鍵字。

小艾說:volatile關(guān)鍵詞的作用一般有如下兩個:

  1. 可見性:當(dāng)一個線程修改了由volatile關(guān)鍵字修飾的變量的值時,其它線程能夠立即得知這個修改。

  2. 有序性:禁止編譯器關(guān)于操作volatile關(guān)鍵詞修飾的變量的指令重排序。

你說這兩個說了20分鐘?口吃?

小牛說:你知道volatile的實(shí)現(xiàn)原理嗎?

小艾說:緩存一致性協(xié)議嘛,這有啥?

小牛說:既然硬件保證了緩存一致性協(xié)議,無論該變量是否被volatile關(guān)鍵詞修飾,它都該滿足緩存一致性協(xié)議呀。你這說的有點(diǎn)自相矛盾哦。

小艾說:那volatile的實(shí)現(xiàn)原理是什么?

小牛說:且聽我慢慢道來。


緩存一致性協(xié)議

我們知道,現(xiàn)代CPU都是多核處理器。由于cpu核心(Kernel)讀取內(nèi)存數(shù)據(jù)較慢,于是就有了緩存的概念。我們希望針對頻繁讀寫的某個內(nèi)存變量,提升本核心的訪問速率。因此我們會給每個核心設(shè)計緩存區(qū)(Cache),緩存該變量。由于緩存硬件的讀寫速度比內(nèi)存快,所以通過這種方式可以提升變量訪問速度。
緩存的結(jié)構(gòu)可以如下設(shè)計:

緩存結(jié)構(gòu)圖

其中,一個緩存區(qū)可以分為N個緩存行(Cache line),緩存行是和內(nèi)存進(jìn)行數(shù)據(jù)交換的最小單位。每個緩存行包含三個部分,其中valid用于標(biāo)識該數(shù)據(jù)的有效性。如果有效位為false,CPU核心就從內(nèi)存中讀取,并將對應(yīng)舊的緩存行數(shù)據(jù)覆蓋,否則使用舊緩存數(shù)據(jù);tag用于指示數(shù)據(jù)對應(yīng)的內(nèi)存地址;block則用以存儲數(shù)據(jù),

多核緩存和內(nèi)存

但是,如果涉及到并發(fā)任務(wù),多個核心讀取同一個變量值,由于每個核心讀取的是自己那一部分的緩存,每個核心的緩存數(shù)據(jù)不一致將會導(dǎo)致一系列問題。緩存一致性的問題根源就在于,對于某個變量,好幾個核心對應(yīng)的緩存區(qū)都有,到底哪個是新的數(shù)據(jù)呢?如果只有一個CPU核心對應(yīng)的緩存區(qū)有該變量,那就沒事啦,該緩存肯定是新的。

所以為了保證緩存的一致性,業(yè)界有兩種思路:

  1. 寫失效(Write Invalidate):當(dāng)一個核心修改了一份數(shù)據(jù),其它核心如果有這份數(shù)據(jù),就把valid標(biāo)識為無效;

  2. 寫更新(Write update):當(dāng)一個核心修改了一份數(shù)據(jù),其它核心如果有這份數(shù)據(jù),就都更新為新值,并且還是標(biāo)記valid有效。

業(yè)界有多種實(shí)現(xiàn)緩存一致性的協(xié)議,諸如MSI、MESI、MOSI、Synapse、Firefly Dragon Protocol等,其中最為流行的是MESI協(xié)議。

MESI協(xié)議就是根據(jù)寫失效的思路,設(shè)計的一種緩存一致性協(xié)議。為了實(shí)現(xiàn)這個協(xié)議,原先的緩存行修改如下:

緩存結(jié)構(gòu)圖

原先的valid是一個比特位,代表有效/無效兩種狀態(tài)。在MESI協(xié)議中,該位改成兩位,不再只是有效和無效兩種狀態(tài),而是有四個狀態(tài),分別為:

  1. M(Modified):表示核心的數(shù)據(jù)被修改了,緩存數(shù)據(jù)屬于有效狀態(tài),但是數(shù)據(jù)只處于本核心對應(yīng)的緩存,還沒有將這個新數(shù)據(jù)寫到內(nèi)存中。由于此時數(shù)據(jù)在各個核心緩存區(qū)只有唯一一份,不涉及緩存一致性問題;

  2. E(Exclusive):表示數(shù)據(jù)只存在本核心對應(yīng)的緩存中,別的核心緩存沒這個數(shù)據(jù),緩存數(shù)據(jù)屬于有效狀態(tài),并且該緩存中的最新數(shù)據(jù)已經(jīng)寫到內(nèi)存中了。同樣由于此時數(shù)據(jù)在各個核心緩存區(qū)只有一份,也不涉及緩存一致性問題;

  3. S(Shared):表示數(shù)據(jù)存于多個核心對應(yīng)的緩存中,緩存數(shù)據(jù)屬于有效狀態(tài),和內(nèi)存一致。這種狀態(tài)的值涉及緩存一致性問題;

  4. I(Invalid):表示該核心對應(yīng)的緩存數(shù)據(jù)無效。

看到這里,大家想必知道為什么這個協(xié)議稱為MESI協(xié)議了吧,它的命名就是取了這四個狀態(tài)的首字母而已。

為了保證緩存一致性,每個核心要寫新數(shù)據(jù)前,需要確保其他核心已經(jīng)置同一變量數(shù)據(jù)的緩存行狀態(tài)位為Invalid后,再把新數(shù)據(jù)寫到自己的緩存行,并之后寫到內(nèi)存中。

MESI協(xié)議包含以下幾個行為:

  • 讀(Read):當(dāng)某個核心需要某個變量的值,并且該核心對應(yīng)的緩存沒這個變量時,就會發(fā)出讀命令,希望別的核心緩存或者內(nèi)存能給該核心最新的數(shù)據(jù);

  • 讀命令反饋(Read Response):讀命令反饋是對讀命令的回應(yīng),包含了之前讀命令請求的數(shù)據(jù)。舉例來說,Kernel0發(fā)送讀命令,請求變量a的值,Kernel1對應(yīng)的緩存區(qū)包含變量a,并且該緩存的狀態(tài)是M狀態(tài),所以Kernel1會給Kernel0的讀命令發(fā)送讀命令反饋,給出該值;

  • 無效化(Invalidate):無效化指令是一條廣播指令,它告訴其他所有核心,緩存中某個變量已經(jīng)無效了。如果變量是獨(dú)占的,只存在某一個核心對應(yīng)的緩存區(qū)中,那就不存在緩存一致性問題了,直接在自己緩存中改了就行,也不用發(fā)送無效化指令;

  • 無效化確認(rèn)(Invalidate Acknowledge):該指令是對無效化指令的回復(fù),收到無效化指令的核心,需要將自己緩存區(qū)對應(yīng)的變量狀態(tài)改為Invalid,并回復(fù)無效化確認(rèn),以此保證發(fā)送無效化確認(rèn)的緩存已經(jīng)無效了;

  • 讀無效(Read Invalidate):這個命令是讀命令和無效化命令的綜合體。它需要接受讀命令反饋和無效化確認(rèn);

  • 寫回(Writeback)這個命令的意思是將核心中某個緩存行對應(yīng)的變量值寫回到內(nèi)存中去。

下圖給了個一個應(yīng)用MESI讀寫數(shù)據(jù)的例子。在該圖中,假設(shè)CPU有兩個核心,Kernel0表示第一個核心,Kernel1表示第二個核心。這里給出了Kernel0想寫新數(shù)據(jù)到自己緩存的例子。

MESI工作原理

  1. 首先Kernel0先完成新數(shù)據(jù)的創(chuàng)建;

  2. Kernel0向全體其他核心發(fā)送無效化指令,告訴其他核心其所對應(yīng)的緩存區(qū)中的這條數(shù)據(jù)已經(jīng)過期無效。本圖例中只有一個其他核心,為Kernel1;

  3. 其他核心收到廣播消息后,將自己對應(yīng)緩存的數(shù)據(jù)的標(biāo)志位記為無效,然后給Kernel0回確認(rèn)消息;

  4. 收到所有其他Kernel的確認(rèn)消息后,Kernel0才能將新數(shù)據(jù)寫回到它所對應(yīng)的緩存結(jié)構(gòu)中去。

根據(jù)上圖,我們可以發(fā)現(xiàn),影響MESI協(xié)議的時間瓶頸主要有兩塊:

  1. 無效化指令:Kernel0需要通知所有的核心,該變量對應(yīng)的緩存在其他核心中是無效的。在通知完之前,該核心不能做任何關(guān)于這個變量的操作。

  2. 確認(rèn)響應(yīng):Kernel0需要收到其他核心的確認(rèn)響應(yīng)。在收到確認(rèn)消息之前,該核心不能做任何關(guān)于這個變量的操作,需要持續(xù)等待其他核心的響應(yīng),直到所有核心響應(yīng)完成,將其對應(yīng)的緩存行標(biāo)志位設(shè)為Invalid,才能繼續(xù)其它操作。

針對這兩部分,我們可以進(jìn)一步優(yōu)化:

  1. 針對無效化指令的加速:在緩存的基礎(chǔ)上,引入Store Buffer這個結(jié)構(gòu)。Store Buffer是一個特殊的硬件存儲結(jié)構(gòu)。通俗的來講,核心可以先將變量寫入Store Buffer,然后再處理其他事情。如果后面的操作需要用到這個變量,就可以從Store Buffer中讀取變量的值,核心讀數(shù)據(jù)的順序變成Store Buffer → 緩存 → 內(nèi)存。這樣在任何時候核心都不用卡住,做不了關(guān)于這個變量的操作了。引入Store Buffer后的結(jié)構(gòu)如下所示:

Store Buffer結(jié)構(gòu)

  1. 針對確認(rèn)響應(yīng)的加速:在緩存的基礎(chǔ)上,引入Invalidate Queue這個結(jié)構(gòu)。其他核心收到Kernel0的Invalidate的命令后,立即給Kernel0回Acknowledge,并把Invalidate這個操作,先記錄到Invalidate Queue里,當(dāng)其他操作結(jié)束時,再從Invalidate Queue中取命令,進(jìn)行Invalidate操作。所以當(dāng)Kernel0收到確認(rèn)響應(yīng)時,其他核心對應(yīng)的緩存行可能還沒完全置為Invalid狀態(tài)。引入Invalidate Queue后的結(jié)構(gòu)如下所示:

Invalidate Queue結(jié)構(gòu)


緩存一致性協(xié)議優(yōu)化存在的問題

上一節(jié)講了兩種緩存一致性協(xié)議的加速方式。但是這兩個方式卻會對緩存一致性導(dǎo)致一定的偏差,下面我們來看一下兩個出錯的例子:

例子1:關(guān)于Store Buffer帶來的錯誤,假設(shè)CPU有兩個核心,Kernel0表示第一個核心,Kernel1表示第二個核心。

...
public void foo(){
    a=1;
    b=1;
}
public void bar(){
    while(b==0continue;
    assert(a==1):"a has a wrong value!";
}
...

如果Kernel0執(zhí)行foo()函數(shù),Kernel1執(zhí)行bar()函數(shù),按照之前我們的理解,如果b變量為1了,那a肯定為1了,assert(a==1)肯定不會報錯。但是事實(shí)卻不是這樣的。

假設(shè)初始情況是這樣的:在執(zhí)行兩個函數(shù)前Kernel1的緩存包含變量a=0,不包含緩存變量b,Kernel0的緩存包含變量b=0,不包含緩存變量a。

Kernel0執(zhí)行foo()函數(shù),Kernel1執(zhí)行bar()函數(shù)時,。這樣的話計算機(jī)的指令程序可能會如下展開:


  1. Kernel0執(zhí)行a=1。由于Kernel0的緩存行不包含變量a,因此Kernel0會將變量a的值存在Store Buffer中,并且向其他Kernel進(jìn)行read Invalidate操作,通知a變量緩存無效;

  2. Kernel1執(zhí)行while(b==0),由于Kernel1的緩存沒有變量b,因此它需要發(fā)送一個讀命令,去找b的值;

    2
  3. Kernel0執(zhí)行b=1,由于Kernel0的緩存中已經(jīng)有了變量b,而且別的核心沒有這個變量的緩存,所以它可以直接更改緩存b的值;

    3
  4. Kernel0收到讀命令后,將最新的b的值發(fā)送給Kernel1,并且將變量b的狀態(tài)由E(獨(dú)占)改變?yōu)镾(共享);

    4
  5. Kernel1收到b的值后,將其存到自己Kernel對應(yīng)的緩存區(qū)中;

    5
  6. Kernel1接著執(zhí)行while(b==0),因?yàn)榇藭rb的新值為1,因此跳出循環(huán);

    6
  7. Kernel1執(zhí)行assert(a==1),由于Kernel1緩存中a的值為0,并且是有效的,所以斷言出錯;

    7
  8. Kernel1終于收到了第一步Kernel0發(fā)送的Invalidate了,趕緊將緩存區(qū)的a==1置為invalid,但是為時已晚。

    8

所以我們看到,這個例子出錯的原因完全是由Store Buffer這個結(jié)構(gòu)引發(fā)的。如果規(guī)定將Store Buffer中數(shù)據(jù)完全刷入到緩存,才能執(zhí)行對應(yīng)變量寫操作的話,該錯誤也能避免了。

例子2:關(guān)于Invalidate Queue帶來的錯誤,同樣假設(shè)CPU有兩個核心,Kernel0表示第一個核心,Kernel1表示第二個核心。

...
public void foo(){
    a=1;
    b=1;
}
public void bar(){
    while(b==0continue;
    assert(a==1):"a has a wrong value!";
}
...

Kernel0執(zhí)行foo()函數(shù),Kernel1執(zhí)行bar()函數(shù),猜猜看這次斷言會出錯嗎?

假設(shè)在初始情況是這樣的:變量a的值在Kernel0和Kernel1對應(yīng)的緩存區(qū)都有,狀態(tài)為S(共享),初值為0,變量b的值是0,狀態(tài)為E(獨(dú)占),只存在于Kernel1對應(yīng)的緩存區(qū),不存在Kernel0對應(yīng)的緩存區(qū)。假設(shè)Kernel0執(zhí)行foo()函數(shù),Kernel1執(zhí)行bar()函數(shù)時,程序執(zhí)行過程如下:

0

  1. Kernel0執(zhí)行a=1,此時由于a變量被更改了,需要給Kernel1發(fā)送無效化命令,并且將a的值存儲在Kernel0的Store Buffer中;

    1
  2. Kernel1執(zhí)行while(b==0),由于Kernel1對應(yīng)的緩存不包含變量b,它需要發(fā)出一個讀命令;

    2
  3. Kernel0執(zhí)行b=1,由于是獨(dú)占的,因此它直接更改自己緩存的值;

    3
  4. Kernel0收到讀命令,將最新的b的值發(fā)送給Kernel1,并且將變量b的狀態(tài)改變?yōu)镾(共享);

    4
  5. Kernel1收到Kernel0在第一步發(fā)的無效化命令,將這個命令存到Invalidate Queue中,打算之后再處理,并且給Kernel0回確認(rèn)響應(yīng);

    5
  6. Kernel1收到包含b值的讀命令反饋,把該值存到自己緩存下;

    6
  7. Kernel1收到b的值之后,打破while循環(huán);

    7
  8. Kernel1執(zhí)行assert(a==1),由于此時Invalidate Queue中的無效化a=0這個緩存值還沒執(zhí)行,因此Kernel1會接著用自己緩存中的a=1這個緩存值,這就出現(xiàn)了問題;

    8
  9. Kernel1開始執(zhí)行Invalidate Queue中的命令,將a=0這個緩存值無效化。但這時已經(jīng)太晚了。

    9

所以我們看到,這個例子出錯的原因完全是由Invalidate Queue這個結(jié)構(gòu)引發(fā)的。如果規(guī)定將Invalidate Queue中命令完全處理完,才能執(zhí)行對應(yīng)變量讀操作的話,該錯誤也能避免了。


內(nèi)存屏障

既然剛剛我們遇到了問題,那如何改正呢?這里就終于到了今天的重頭戲,內(nèi)存屏障了。內(nèi)存屏障簡單來講就是一行命令,規(guī)定了某個針對緩存的操作。這里我們來看一下最常見的寫屏障和讀屏障。

  1. 針對Store Buffer:核心在后續(xù)變量的新值寫入之前,把Store Buffer的所有值刷新到緩存;核心要么就等待刷新完成后寫入,要么就把后續(xù)的后續(xù)變量的新值放到Store Buffer中,直到Store Buffer的數(shù)據(jù)按順序刷入緩存。這種也稱為內(nèi)存屏障中的寫屏障(Store Barrier)。

  2. 針對Invalidate Queue:執(zhí)行后需等待Invalidate Queue完全應(yīng)用到緩存后,后續(xù)的讀操作才能繼續(xù)執(zhí)行,保證執(zhí)行前后的讀操作對其他CPU而言是順序執(zhí)行的。這種也稱為內(nèi)存屏障中的讀屏障(Load Barrier)。


volatile中的內(nèi)存屏障

對于JVM的內(nèi)存屏障實(shí)現(xiàn)中,也采取了內(nèi)存屏障。JVM的內(nèi)存屏障有四種,這四種實(shí)際上也是上述的讀屏障和寫屏障的組合。我們來看一下這四種屏障和他們的作用:

  1. LoadLoad屏障:對于這樣的語句

    第一大段讀數(shù)據(jù)指令; 
    LoadLoad; 
    第二大段讀數(shù)據(jù)指令;

LoadLoad指令作用:在第二大段讀數(shù)據(jù)指令被訪問前,保證第一大段讀數(shù)據(jù)指令執(zhí)行完畢

  1. StoreStore屏障:對于這樣的語句

    第一大段寫數(shù)據(jù)指令; 
    StoreStore; 
    第二大段寫數(shù)據(jù)指令;

StoreStore指令作用:在第二大段寫數(shù)據(jù)指令被訪問前,保證第一大段寫數(shù)據(jù)指令執(zhí)行完畢

  1. LoadStore屏障:對于這樣的語句

    第一大段讀數(shù)據(jù)指令; 
    LoadStore; 
    第二大段寫數(shù)據(jù)指令;

LoadStore指令作用:在第二大段寫數(shù)據(jù)指令被訪問前,保證第一大段讀數(shù)據(jù)指令執(zhí)行完畢。

  1. StoreLoad屏障:對于這樣的語句

    第一大段寫數(shù)據(jù)指令; 
    StoreLoad; 
    第二大段讀數(shù)據(jù)指令;

StoreLoad指令作用:在第二大段讀數(shù)據(jù)指令被訪問前,保證第一大段寫數(shù)據(jù)指令執(zhí)行完畢。

針對volatile變量,JVM采用的內(nèi)存屏障是:

  1. 針對volatile修飾變量的寫操作:在寫操作前插入StoreStore屏障,在寫操作后插入StoreLoad屏障;

  2. 針對volatile修飾變量的讀操作:在每個volatile讀操作前插入LoadLoad屏障,在讀操作后插入LoadStore屏障;

通過這種方式,就可以保證被volatile修飾的變量具有線程間的可見性和禁止指令重排序的功能了。

總結(jié)

講了這么多,我們來總結(jié)一下。

volatile關(guān)鍵字保證了兩個性質(zhì):

  • 可見性:可見性是指當(dāng)多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。

  • 有序性:對一個volatile變量的寫操作,執(zhí)行在任意后續(xù)對這個volatile變量的讀操作之前。

單單緩存一致性協(xié)議無法實(shí)現(xiàn)volatile。

緩存一致性可以通過Store Buffer和Invalidate Queue兩種結(jié)構(gòu)進(jìn)行加速,但這兩種方式會造成一系列不一致性的問題。

因此后續(xù)提出了內(nèi)存屏障的概念,分為讀屏障和寫屏障,以此修正Store Buffer和Invalidate Queu產(chǎn)生的問題。

通過讀屏障和寫屏障,又發(fā)展出了LoadLoad屏障,StoreStore屏障,LoadStore屏障,StoreLoad屏障JVM也是利用了這幾種屏障,實(shí)現(xiàn)volatile關(guān)鍵字。


巨人的肩膀

  1. Java多線程編程核心指南

  2. https://www.jianshu.com/p/ef8de88b1343

  3. Paul E. McKenney Memory Barriers: a Hardware View for Software Hackers

  4. https://www.cnblogs.com/xiaolincoding/p/13886559.html


好文推薦

你不好奇 CPU 是如何執(zhí)行任務(wù)的?

10 張圖打開 CPU 緩存一致性的大門


哈嘍,我是小林,一股清流,就愛圖解計算機(jī)基礎(chǔ)。

如果覺得文章對你有幫助,歡迎分享給你的朋友,也給小林點(diǎn)個「在看」,這對小林非常重要。謝謝你們,給各位小姐姐小哥哥們抱拳了,我們下次見!

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉