中国工业网_网络工业品牌 资讯创造价值

放棄Python擁抱Mojo?鵝廠工程師真實(shí)使用感受

# 關(guān)注并星標(biāo)騰訊云開發(fā)者


(資料圖)

# 每周1 | 鵝廠工程師帶你審判技術(shù)

#第1期|李志瑞:AI 屆新語(yǔ)言 Mojo 要火??

前段時(shí)間 Modular 發(fā)布了一個(gè)新語(yǔ)言 Mojo,這語(yǔ)言不止官網(wǎng)放了巨大的 emoji ?,而且它的標(biāo)準(zhǔn)文件后綴一個(gè)是「.mojo」另一個(gè)是「.?」,一副立馬要火的樣子呢。

說實(shí)話,這個(gè)用 emoji 做后綴名的操作其實(shí)挺無(wú)聊,也有點(diǎn)敗好感,但如果說這個(gè)語(yǔ)言能在完全兼容 Python 的基礎(chǔ)上大幅提高執(zhí)行效率,并且作者是 LLVM 發(fā)起人 Chris Lattner,是不是突然又有興趣繼續(xù)了解它了呢?

Mojo 被設(shè)計(jì)為 Python 語(yǔ)言的超集,并增加了許多特性,包括:

?? Progressive types:能利用類型信息獲得更好性能和靜態(tài)檢查,但又不強(qiáng)制要求寫類型。

?? Zero cost abstractions:C++ 的核心設(shè)計(jì)準(zhǔn)則,能夠避免用戶為了性能放棄合理設(shè)計(jì)的代碼。

?? Ownership + borrow checker:Rust 語(yǔ)言的安全性來(lái)源,在編譯期避免許多錯(cuò)誤的發(fā)生。

?? The full power of MLIR:原生支持對(duì) MLIR 的直接訪問,能夠從底層擴(kuò)展系統(tǒng)。

在 Mojo 這個(gè)語(yǔ)言的介紹中反復(fù)提到 AI,官網(wǎng)也說它是「a new programming language for all AI developers」。那么為什么 AI 開發(fā)需要一個(gè)新語(yǔ)言呢?首先,我們知道在 AI 屆具有統(tǒng)治地位的語(yǔ)言就是 Python,Python 是一個(gè)語(yǔ)法簡(jiǎn)單清晰,容易上手,且靈活度很高的語(yǔ)言,深受廣大程序員喜愛,XKCD 上有就這么一幅漫畫:

當(dāng)然,受人喜愛的語(yǔ)言有很多,Python 成為 AI 屆的統(tǒng)治語(yǔ)言除了本身易用之外,也有慣性的因素。由于 Python 上機(jī)器學(xué)習(xí)相關(guān)的庫(kù)多,因此機(jī)器學(xué)習(xí)從業(yè)者用的就多,這又反過來(lái)令新的機(jī)器學(xué)習(xí)相關(guān)庫(kù)優(yōu)先為 Python 提供接口,進(jìn)一步加強(qiáng)了其統(tǒng)治地位。因此,為了逐步滲透這個(gè)用戶群,Mojo 兼容 Python 是很正確的一個(gè)選擇。Mojo 不僅承諾語(yǔ)法是 Python 的超集,并且它還能直接調(diào)用 Python 的庫(kù),這意味著 Mojo 不需要從零開始構(gòu)建自己的生態(tài),本身就可以用上繁榮的 Python 生態(tài)了。

雖然 Python 很好,但它有一個(gè)眾所周知的問題,那就是太慢了。而機(jī)器學(xué)習(xí)本身又需要繁重的計(jì)算,因此 Python 生態(tài)中大量庫(kù)的底層其實(shí)都是用高性能的語(yǔ)言(如 C/C++)進(jìn)行實(shí)現(xiàn),然后再提供一個(gè) Python 接口供用戶調(diào)用,典型的如 numpy 這種數(shù)學(xué)庫(kù)。在這種情況下,Python 事實(shí)上是被作為一個(gè)膠水語(yǔ)言來(lái)使用,這造成了開發(fā)的碎片化,如果一個(gè)用戶只是簡(jiǎn)單調(diào)一下庫(kù)那還好說,但一旦到了工業(yè)界,開發(fā)過程中不可避免地就要涉及一些底層庫(kù)的修改,甚至直接換語(yǔ)言來(lái)實(shí)現(xiàn)同樣的功能以提高性能,這種割裂不止增加了開發(fā)成本和精神負(fù)擔(dān),而且考慮到眾多擅長(zhǎng) C/C++ 語(yǔ)言的開發(fā)者也并不是 AI 領(lǐng)域?qū)<遥@種開發(fā)人員能力的不適配也對(duì)整個(gè) AI 生態(tài)的發(fā)展形成了一定阻礙。

因此,Mojo 的目的就是要在 Python 生態(tài)的基礎(chǔ)上,讓用戶能用一個(gè)語(yǔ)言,從使用易用的接口,到開發(fā)復(fù)雜的庫(kù),再到實(shí)現(xiàn)底層黑科技,統(tǒng)一實(shí)驗(yàn)和生產(chǎn)環(huán)境所用的語(yǔ)言。為了實(shí)現(xiàn)這個(gè)目的,Mojo 擴(kuò)展了 Python 語(yǔ)法,支持了緊湊的內(nèi)存布局,并引入了一些現(xiàn)代的語(yǔ)言特性(例如 Rust 的安全性檢查),使得這個(gè)語(yǔ)言能夠漸進(jìn)式地在 AI 界立足。說起來(lái) Chris Lattner 在這方面可以算是經(jīng)驗(yàn)豐富了,不管是在 gcc/msvc 的統(tǒng)治下實(shí)現(xiàn) clang,還是在 objective-c 的統(tǒng)治下為蘋果實(shí)現(xiàn) swift,都是一個(gè)逐步蠶食對(duì)手市場(chǎng)的過程。

說了這么多,該來(lái)看看 Mojo 長(zhǎng)什么樣了。現(xiàn)在 Mojo 還不能直接下載使用,如果想要嘗鮮,需要在官網(wǎng)申請(qǐng),然后在 playground 頁(yè)面中試用,這是一個(gè)基于 Jupyter 的頁(yè)面,可以混合筆記和可執(zhí)行的 Mojo 代碼。

前面提到,Mojo 的語(yǔ)法是 Python 的超集,因此 Mojo 的 Hello World 也跟 Python 一樣簡(jiǎn)單:

print("Hello World") #> Hello World

與 Python 一樣,Mojo 也使用換行符和縮進(jìn)來(lái)定義代碼塊:

fn foo():var x: Int = 1x += 1let y: Int = 1print(x, y)  #> 2 1foo()

上面的代碼中使用 var 來(lái)聲明變量 x,使用 let 來(lái)聲明了不可變量 y。Mojo 像很多較新近的語(yǔ)言一樣,讓不可變量的聲明變得簡(jiǎn)單,以鼓勵(lì)開發(fā)者使用不可變的量。另外注意到這里定義函數(shù)使用了 fn 而非 Python 的 def,這是因?yàn)?Mojo 希望在兼容 Python 的基礎(chǔ)上加入編譯期的檢查和優(yōu)化,而 Python 過于動(dòng)態(tài)的語(yǔ)法很難支持這一目標(biāo),因此,Mojo 同時(shí)支持使用 fn 和 def 兩個(gè)關(guān)鍵字來(lái)聲明函數(shù),對(duì)于調(diào)用者來(lái)說,這兩種方法聲明出來(lái)的函數(shù)沒有什么區(qū)別,但對(duì)于實(shí)現(xiàn)者來(lái)說,可以將 fn 看作「嚴(yán)格模式」下的 def,例如下面的代碼會(huì)編譯錯(cuò)誤(如果改成用 def 則不會(huì)出錯(cuò)):

fn foo(): x = 1 print(x) #error:Expression[12]:6:5:useofunknowndeclaration"x","fn"declarationsrequireexplicitvariabledeclarations#x=1#^

雖然官方承諾 Mojo 的語(yǔ)法是 Python 的超集,但目前 Mojo 還在開發(fā)中,很多 Python 語(yǔ)法都還不支持,例如目前連 Python 的 class 都無(wú)法被編譯通過:

class MyClass:def foo():pass# error: Expression [15]:17:5: classes are not supported yet#     class MyClass:#     ^

不過,Mojo 現(xiàn)在先提供了另一個(gè)用來(lái)組織數(shù)據(jù)的關(guān)鍵字 struct,相比于 class,struct 更加靜態(tài)可控,便于優(yōu)化。一方面,struct 支持類似 Python class 風(fēng)格的函數(shù)聲明和運(yùn)算符重載。而另一方面,struct 又類似于 C++ 的 struct 和 class,內(nèi)部的成員在內(nèi)存中緊湊排布,而且不支持在運(yùn)行時(shí)動(dòng)態(tài)添加成員和方法,便于編譯期進(jìn)行優(yōu)化,例如:

struct MyIntPair:var first: Intvar second: Intfn __init__(inout self, first: Int, second: Int):self.first = firstself.second = secondfn __lt__(self, rhs: MyIntPair) -> Bool:return self.first < rhs.first or(self.first == rhs.first andself.second < rhs.second)let p1 = MyIntPair(1, 2)let p2 = MyIntPair(2, 1)if p1 < p2: print("p1 < p2")  #> p1 < p2

雖然有點(diǎn)不同,但整體上看起來(lái)還是非常熟悉的對(duì)吧。說到這里,有一點(diǎn)需要提醒各位注意,盡管 Mojo 之后會(huì)令語(yǔ)法成為 Python 語(yǔ)法的超集,但其語(yǔ)義則有時(shí)會(huì)和 Python 不同,這意味著 Python 的代碼直接拷到 Mojo 里可能會(huì)出現(xiàn)編譯通過但執(zhí)行結(jié)果不同的情況,這里簡(jiǎn)單提一個(gè)比較常見的例子:函數(shù)傳參。在 Python 中,函數(shù)傳參的語(yǔ)義類似于 C++ 的傳指針,在函數(shù)內(nèi)部雖然不能更改調(diào)用者指向的對(duì)象,但可以改變?cè)搶?duì)象內(nèi)部的狀態(tài),例如下面的代碼:

def foo(lst):lst[0] = 5print(lst)x = [1, 2, 3]foo(x)print(x)

在 Python 中,這段代碼打印出來(lái)的結(jié)果是兩次 [5, 2, 3]。但在 Mojo 中,使用 def 定義的函數(shù)默認(rèn)的傳遞邏輯是復(fù)制值,也就是說,盡管在函數(shù)中能夠修改參數(shù)內(nèi)部的狀態(tài),但修改對(duì)于調(diào)用方來(lái)說是不可見的,因此上面這段代碼在 Mojo 中打印的結(jié)果是 [5, 2, 3](foo 內(nèi)部)和 [1, 2, 3](foo 外部)。

除了語(yǔ)法像 Python,Mojo 非常務(wù)實(shí)的一點(diǎn)在于它構(gòu)建于 Python 的生態(tài)之上。因此即便 Mojo 還沒能完整支持 Python 的語(yǔ)法,它還是優(yōu)先支持了對(duì) Python 庫(kù)的調(diào)用,以便讓開發(fā)者能受益于龐大完善的 Python 的生態(tài)。例如下面的代碼就使用了 Python 的 numpy 庫(kù):

fromPythonInterfaceimportPythonlet np = Python.import_module("numpy")ar = np.arange(15).reshape(3, 5)print(ar.shape)   #> (3, 5)

Mojo 作為一個(gè)新語(yǔ)言,廣泛吸收許多現(xiàn)代的程序語(yǔ)言設(shè)計(jì)思想,例如 Rust 的所有權(quán)和借用檢查,以此提升代碼的安全性。在 Mojo 中,使用 fn 定義的函數(shù)的參數(shù)默認(rèn)傳的是不可變的引用,即「借用」,調(diào)用方仍然擁有其所有權(quán),因此在函數(shù)內(nèi)部不可以對(duì)參數(shù)進(jìn)行修改。Mojo 提供了一個(gè) borrow 關(guān)鍵字來(lái)標(biāo)注這樣的參數(shù)傳遞情況,對(duì)于 fn 來(lái)說是可以省略的,也就是說下面 foo 函數(shù)中兩個(gè)參數(shù)的傳遞方式相同:

fn foo(borrowed a: SomethingBig, b: SomethingBig):a.use()b.use()

在 Rust 中,傳參的默認(rèn)行為是移動(dòng),如果需要借用則需要在傳入時(shí)加上 &,這兩種方式倒是沒有太大的優(yōu)劣之分,Mojo 的行為可能更接近于 Python 這類高級(jí)語(yǔ)言的習(xí)慣。如果想要修改傳入的參數(shù),則需要手動(dòng)注明 inout,例如:

fn swap(inout lhs: Int, inout rhs: Int):let tmp = lhslhs = rhsrhs = tmpfn test_swap():var x = 42var y = 12print(x, y)  #> 42, 12swap(x, y)print(x, y)  #> 12, 42test_swap()

按道理說,Mojo 應(yīng)該像 Rust 一樣規(guī)避一個(gè)變量同時(shí)被可變和不可變借用,也應(yīng)該規(guī)避同時(shí)被可變借用,但目前 Mojo 編譯器似乎還沒實(shí)現(xiàn)這一特性,例如下面的代碼還是能編譯通過的:

var x = 42swap(x,x)

從這也可以看出 Mojo 確實(shí)還處在比較早期的發(fā)展階段。

另一個(gè)重要的內(nèi)存安全概念是對(duì)象的所有權(quán),當(dāng)一個(gè)函數(shù)獲取了對(duì)象的所有權(quán)后,調(diào)用方就不應(yīng)該再去使用這個(gè)對(duì)象了,例如我們實(shí)現(xiàn)了一個(gè)只支持移動(dòng)的類型 UniquePtr:

struct UniquePtr:var ptr: Intfn __init__(inout self, ptr: Int):self.ptr = ptrfn __moveinit__(inout self, owned existing: Self):self.ptr = existing.ptrfn __del__(owned self):self.ptr = 0

同時(shí),我們有兩個(gè)函數(shù),其中,use_ptr 使用了前面提到的 borrow 關(guān)鍵字,借用了 UniquePtr 對(duì)象,而 take_ptr 則使用 owned 關(guān)鍵字,指明它需要獲取傳入對(duì)象的所有權(quán)。那么,在調(diào)用 take_ptr 的時(shí)候,我們就需要在參數(shù)后面加上 ^ 后綴,用來(lái)表明我們將所有權(quán)轉(zhuǎn)移給 take_ptr:

fn use_ptr(borrowed p: UniquePtr):print(p.ptr)fn take_ptr(owned p: UniquePtr):print(p.ptr)fn test_ownership():let p = UniquePtr(100)use_ptr(p)    #> 100take_ptr(p^)  #> 100test_ownership()

因此,如果我們將 use_ptr 和 take_ptr 的調(diào)用順序調(diào)換一下,就會(huì)出現(xiàn)編譯錯(cuò)誤:

fn test_ownership():let p = UniquePtr(100)take_ptr(p^)use_ptr(p)    # ERROR!test_ownership()# error: Expression [13]:23:12: use of uninitialized value "p"#    use_ptr(p) # ERROR: p is no longer valid here!#            ^

Mojo 的另一個(gè)強(qiáng)大之處在于它讓對(duì) MLIR>) 的操作變得更簡(jiǎn)單。MLIR 全稱是 Multi-Level Intermediate Representation,是一個(gè)編譯器開發(fā)框架,它存在的目的是通過定義多種方言來(lái)逐級(jí)將代碼轉(zhuǎn)換為機(jī)器碼,以降低編譯器的開發(fā)成本。在 MLIR 之前,一個(gè)廣為人熟知的 IR 是 LLVM IR,一個(gè)語(yǔ)言的編譯器作者可以通過將自己的語(yǔ)言編譯為 LLVM IR 來(lái)接入 LLVM 的工具鏈,使得編譯器作者不需要關(guān)心底層具體硬件的差別,實(shí)現(xiàn)了對(duì)底層編譯工具鏈的復(fù)用:

但 LLVM IR 層級(jí)過低,難以進(jìn)行特定于語(yǔ)言本身的優(yōu)化,從上面的圖中也能看出,各個(gè)語(yǔ)言為了實(shí)現(xiàn)語(yǔ)言本身的優(yōu)化,都在編譯為 LLVM IR 之前加入了自己的 IR。另外 LLVM IR 擴(kuò)展起來(lái)也非常困難,難以適應(yīng)復(fù)雜異構(gòu)計(jì)算的要求,而異構(gòu)計(jì)算在 AI 開發(fā)中又非常普遍。MLIR 相比于之前的 IR,更加模塊化,僅保留了一個(gè)非常小的內(nèi)核,方便開發(fā)者進(jìn)行擴(kuò)展。很多編譯器將代碼編譯為 MLIR,而 Mojo 提供了直接訪問 MLIR 的能力,這使得 Mojo 能夠受益于這些工具。更多關(guān)于 MLIR 的內(nèi)容可以參考這一系列文章:編譯器與中間表示: LLVM IR, SPIR-V, 以及 MLIR,這里就不做過多贅述,我們主要關(guān)注在 Mojo 中可以如何操作 MLIR。舉例而言,如果我們希望實(shí)現(xiàn)一個(gè)新的 boolean 類型 OurBool,我們可以這樣實(shí)現(xiàn):

alias OurTrue: OurBool = __mlir_attr.`true`alias OurFalse: OurBool = __mlir_attr.`false`@register_passable("trivial")struct OurBool:var value: __mlir_type.i1fn __init__() -> Self:return OurFalsefn __init__(value: __mlir_type.i1) -> Self:return Self {value: value}fn __bool__(self) -> Bool:return Bool(self.value)

這里定義了一個(gè)類型為 OurBool 的類型,里面有一個(gè)直接使用 MLIR 內(nèi)置類型 i1 的成員 value 。在 Mojo 中,我們可以通過 __mlir_type.typename 的形式來(lái)訪問 MLIR 類型。接著,我們?yōu)檫@個(gè)類型提供了兩個(gè)構(gòu)造函數(shù),默認(rèn)情況下構(gòu)造為 OurFalse 也可基于傳入的參數(shù)進(jìn)行構(gòu)建。最下面的 __bool__ 也和 Python 的 __bool__ 一樣,用于使該類型具有和內(nèi)置 boolean 類型的性質(zhì),此時(shí)我們可以這樣使用它:

let t: OurBool = OurTrueif t: print("true")  #> true

除了使用 MLIR 之外,Mojo 甚至可以允許開發(fā)者使用 MLIR 實(shí)現(xiàn)邏輯,例如下面的代碼中通過應(yīng)用 MLIR 的 index.casts 操作來(lái)實(shí)現(xiàn)類型轉(zhuǎn)換,然后再通過 index.cmp 對(duì)值進(jìn)行比較:

# ...struct OurBool:# ...fn __eq__(self, rhs: OurBool) -> Self:let lhsIndex = __mlir_op.`index.casts`[_type : __mlir_type.index](self.value)let rhsIndex = __mlir_op.`index.casts`[_type : __mlir_type.index](rhs.value)return Self(__mlir_op.`index.cmp`[pred : __mlir_attr.`#index`](lhsIndex, rhsIndex))

基于封裝好的 __eq__ 方法,我們可以很容易實(shí)現(xiàn) __invert__ 方法:

# ...struct OurBool:# ...fn __invert__(self) -> Self:return OurFalse if self == OurTrue else OurTrue

此時(shí),我們就可以對(duì) OurBool 類型的對(duì)象使用 ~ 操作符了:

let f = OurFalseif ~f: print("false")  #> false

通過這個(gè)簡(jiǎn)單的例子我們可以看出,在 Mojo 中,開發(fā)者可以通過訪問 MLIR 來(lái)實(shí)現(xiàn)和內(nèi)置類型同等高效的類型。這使得開發(fā)者可以在 Mojo 上為新硬件的數(shù)據(jù)類型封裝高效簡(jiǎn)單的 Mojo 接口而不需要切換語(yǔ)言。雖然大部分開發(fā)者并不需要接觸 MLIR,但 Mojo 為更深入和更底層的優(yōu)化提供了充分的可能性。

雖然 Mojo 反復(fù)強(qiáng)調(diào)它是為 AI 設(shè)計(jì)的新語(yǔ)言,但以目前 Mojo 的設(shè)計(jì)方向來(lái)看,它的發(fā)展前景并不止于 AI。本質(zhì)上 Mojo 提供了一個(gè)能夠兼容 Python 生態(tài)的高性能語(yǔ)言,且這個(gè)語(yǔ)言可以讓 Python 開發(fā)者幾乎無(wú)痛地切換過去,那 Python 開發(fā)者何樂而不為呢?對(duì)于使用 Mojo 的開發(fā)者來(lái)說,上層業(yè)務(wù)可以將 Mojo 當(dāng) Python 一樣使用,享受到簡(jiǎn)明的語(yǔ)法帶來(lái)的高開發(fā)效率,當(dāng)出現(xiàn)性能瓶頸的時(shí)候,也不用切換語(yǔ)言去進(jìn)行優(yōu)化,直接使用 Mojo 重構(gòu)模塊即可。雖然現(xiàn)在還沒法在生產(chǎn)環(huán)境中驗(yàn)證這個(gè)想法,但這個(gè)未來(lái)聽起來(lái)確實(shí)非常美好。關(guān)于 Mojo 和 Python 開發(fā)性能的對(duì)比,您可瀏覽 Mojo 發(fā)布會(huì)上的 Jeremy Howard demo for Mojo launch 視頻。

目前 Mojo 還在比較早期的階段,不僅許多語(yǔ)言特性都還沒實(shí)現(xiàn),而且連本地開發(fā)的套件都沒有提供。不過其發(fā)展路線和設(shè)計(jì)思路都非常務(wù)實(shí) ,又有一個(gè)足夠?qū)I(yè)的領(lǐng)導(dǎo)者和公司作為背景支撐,可以說是未來(lái)可期,也非常希望這個(gè)語(yǔ)言能在其他領(lǐng)域得到更廣泛的應(yīng)用。

關(guān)鍵詞:

來(lái)源:程序員客棧
編輯:GY653

免責(zé)聲明:本網(wǎng)站內(nèi)容主要來(lái)自原創(chuàng)、合作媒體供稿和第三方自媒體作者投稿,凡在本網(wǎng)站出現(xiàn)的信息,均僅供參考。本網(wǎng)站將盡力確保所提供信息的準(zhǔn)確性及可靠性,但不保證有關(guān)資料的準(zhǔn)確性及可靠性,讀者在使用前請(qǐng)進(jìn)一步核實(shí),并對(duì)任何自主決定的行為負(fù)責(zé)。本網(wǎng)站對(duì)有關(guān)資料所引致的錯(cuò)誤、不確或遺漏,概不負(fù)任何法律責(zé)任。任何單位或個(gè)人認(rèn)為本網(wǎng)站中的網(wǎng)頁(yè)或鏈接內(nèi)容可能涉嫌侵犯其知識(shí)產(chǎn)權(quán)或存在不實(shí)內(nèi)容時(shí),應(yīng)及時(shí)向本網(wǎng)站提出書面權(quán)利通知或不實(shí)情況說明,并提供身份證明、權(quán)屬證明及詳細(xì)侵權(quán)或不實(shí)情況證明。本網(wǎng)站在收到上述法律文件后,將會(huì)依法盡快聯(lián)系相關(guān)文章源頭核實(shí),溝通刪除相關(guān)內(nèi)容或斷開相關(guān)鏈接。

  • 相關(guān)推薦

相關(guān)詞

主站蜘蛛池模板: 阻垢剂|缓蚀剂|杀菌剂|分散剂|水处理剂|印染助剂|水处理药剂|造纸助剂|膜阻垢剂|缓蚀剂|HEDP|ATMP|螯合剂-山东凯瑞化学有限公司 水处理药剂生产厂家 | 专业护工_医院护工_护工陪护_住家护工- 心陪护| 螺旋输送机_无轴螺旋输送机_绞龙螺旋输送机- 河北品丞环保机械有限公司 | 土工膜_土工布_复合土工膜_山东土工膜生产厂家_山东路易达新材料有限公司 | 在线腐蚀率仪,在线污垢热阻仪,靶式光源仪-北京同德创业科技有限公司 | 湖南众一离心机股份有限公司_活塞推料离心机_沉降离心机_卧式刮刀离心机 | 景德镇星瑞陶瓷有限公司--官网-景德镇星瑞陶瓷有限公司 | 中华石油化工网 www.cnpec.net——歌颂石化 服务石化 奉献石化 发展石化 | 潜水搅拌机|潜水推流机|曝气机|刮吸泥机|格栅除污机-南京远蓝环境设备 | 仪器校准-计量检测-计量校准-中健计量检测(广东)有限公司 | 江苏吉宏特专用汽车制造有限公司_联合吸污车-下水道管道清洗疏通车-综合养护吸排车 | 纠偏系统厂家-迈欣机械| 无线计量仪表-电力物联网仪表-CE认证电表 | 延吉新闻网 - 未来之选·就是延吉 [YanJinews.com] | 激光切管机_等离子切管机_相贯线切管机厂家|服务为先-山东美峰智能设备有限公司 | 美缝剂_美缝剂加盟_瓷砖美缝剂_美缝剂厂家_填缝剂_领翔美缝剂-【官网】 | 南宁清洁公司|外墙清洗|开荒清洁|洒水车|管道疏通|园林绿化_广西优而美环境工程有限公司 | 郑州阳光房|封阳台|钢结构【河南郑州如意阳光房门窗有限公司】 | 湖南长沙手术室、实验室、无尘室、洁净室、无尘车间的净化工程装修公司-福临建设 | 聚丙烯酰胺,聚合氯化铝,重金属捕捉剂,污泥调理剂,活性氧化铝,生石灰,反渗透阻垢剂,工业葡萄糖,硫酸铝,果壳活性炭,柱状活性炭,蜂窝活性炭,石英砂,锰砂-北京雁归来环保科技有限公司-以真诚为立足之本,以质量为生存之本,愿与海内外同仁共创双赢。雁归来人一路走来,气贯长虹,勇锐盖过怯弱,进取压倒苟安!我们紧扣时代脉搏,专注水处理、继往开来! | 气体泄漏检测仪,COD水质分析仪,RD8200管线探测仪-成都恒通兴业科技有限责任公司 | 鸿茗商务-杭州鸿茗商务咨询有限公司| 压力容器锻件_升高法兰_管板_阀体_接管锻件 - 山西中重重工集团 压力机-压装机-黄油机-黄油泵-[广东品嘉灵]专业定制各种精密压装设备 | 心心床垫_睡眠床垫-安徽瑶海心心工贸有限公司 | 声测管厂家_注浆管现货_桩基声测管_河北沧州新迈实业有限公司 | 化妆粉扑厂家【秀兰】一线品牌资格供应商_海绵粉扑批发_气垫粉扑价格_广州秀兰生物科技有限公司 化工招聘网 化工人才网|化工英才网-化工企业招聘首选网站 | 铁行火车票_铁行火车票网上订票_铁行火车票客户端【铁行官网】 | 友联智能|RFID应用服务供应商|专注RFID行业解决方案|RFID数据采集-助力行业数字化转型 | 山东啤酒箱塑料提手_注塑产品加工_手提绳厂家-淄博浩晨包装制品有限公司 | 山东德曼医疗设备集团有限公司| 铝合金百叶窗_西安百叶窗厂家-西安市未央区通达建材物资部 | 生物发酵罐(细菌/植物/液体玻璃实验室发酵罐设备)CIP清洗罐,灭活罐「厂家」-安徽赛德齐瑞发酵罐品牌 | 智能试剂柜-疾控|高校实验室|医院药品智能试剂管理柜-北京晶品赛思 | 徐州车牌识别_徐州门禁一卡通_徐州人脸识别门禁-江苏琪瑞特智能科技有限公司 | 呼吸家官网|肺功能检测仪生产厂家|国产肺功能仪知名品牌|肺功能检测仪|肺功能测试仪|婴幼儿肺功能仪|弥散残气肺功能仪|肺功能测试系统|广州红象医疗科技有限公司|便携式肺功能仪|大肺功能仪|呼吸康复一体机|儿童肺功能仪|肺活量计|医用简易肺功能仪|呼吸康复系统|肺功能仪|弥散肺功能仪(大肺)|便携式肺功能检测仪|肺康复|呼吸肌力测定肺功能仪|肺功能测定仪|呼吸神经肌肉刺激仪|便携式肺功能 | 热水工程|空气能热水工程|超低温采暖工程|太阳能热水工程|空气源热泵厂家|炬邦热能设备有限公司 热熔钻孔机【优质厂家】_多年热熔钻设备研发制造经验 | 同步分流马达_液压泵维修_派克多路阀-济南富诚液压设备有限公司 通用变频器|国产变频器|深圳变频器厂家-深圳市英捷思技术有限公司 | 郑州空调维修_郑州中央空调维修_空调清洗维保-郑州大晟机电设备安装工程有限公司 | 江西铭鑫冶金设备有限公司-破碎机,铜米机,选矿摇床,电池回收设备 | 太原万通汽车学校[官网]-太原好的汽修培训学校,学新能源汽车技术,学汽修,学汽车检测与维修技术 | 软文营销推广-新闻稿发布-软文撰写-百科词条编辑-品牌全案策划推广网络营销传播-喜尚传媒 |