.NET性能優化-使用ValueStringBuilder拼接字符串

前言

這一次要和大家分享的一個Tips是在字符串拼接場景使用的,我們經常會遇到有很多短小的字符串需要拼接的場景,在這種場景下及其的不推薦使用String.Concat也就是使用+=運算符。 目前來說官方最推薦的方案就是使用StringBuilder來構建這些字符串,那么有什么更快內存占用更低的方式嗎?那就是今天要和大家介紹的ValueStringBuilder

ValueStringBuilder

ValueStringBuilder不是一個公開的API,但是它被大量用于.NET的基礎類庫中,由于它是值類型的,所以它本身不會在堆上分配,不會有GC的壓力。 微軟提供的ValueStringBuilder有兩種使用方式,一種是自己已經有了一塊內存空間可供字符串構建使用。這意味著你可以使用棧空間,也可以使用堆空間甚至非托管堆的空間,這對于GC來說是非常友好的,在高并發情況下能大大降低GC壓力。

//?構造函數:傳入一個Span的Buffer數組
public?ValueStringBuilder(Span<char>?initialBuffer);//?使用方式:
//?棧空間
var?vsb?=?new?ValueStringBuilder(stackalloc?char[512]);
//?普通數租
var?vsb?=?new?ValueStringBuilder(new?char[512]);
//?使用非托管堆
var?length?=?512;
var?ptr?=?NativeMemory.Alloc((nuint)(512?*?Unsafe.SizeOf<char>()));
var?span =?new?Span<char>(ptr,?length);
var?vsb?=?new?ValueStringBuilder(span);
.....
NativeMemory.Free(ptr);?//?非托管堆用完一定要Free

另外一種方式是指定一個容量,它會從默認的ArrayPoolchar對象池中獲取緩沖空間,因為使用的是對象池,所以對于GC來說也是比較友好的,千萬需要注意,池中的對象一定要記得歸還

//?傳入預計的容量
public?ValueStringBuilder(int?initialCapacity)??
{??//?從對象池中獲取緩沖區_arrayToReturnToPool?=?ArrayPool<char>.Shared.Rent(initialCapacity);??......
}

那么我們就來比較一下使用+=StringBuilderValueStringBuilder這幾種方式的性能吧。

//?一個簡單的類
public?class?SomeClass??
{??public?int?Value1;?public?int?Value2;?public?float?Value3;??public?double?Value4;?public?string??Value5;?public?decimal?Value6;??public?DateTime?Value7;?public?TimeOnly?Value8;?public?DateOnly?Value9;??public?int[]??Value10;??
}
//?Benchmark類
[MemoryDiagnoser]??
[HtmlExporter]??
[Orderer(SummaryOrderPolicy.FastestToSlowest)]??
public?class?StringBuilderBenchmark??
{??private?static?readonly?SomeClass?Data;??static?StringBuilderBenchmark()??{??var?baseTime?=?DateTime.Now;??Data?=?new?SomeClass??{??Value1?=?100,?Value2?=?200,?Value3?=?333,??Value4?=?400,?Value5?=?string.Join('-',?Enumerable.Range(0,?10000).Select(i?=>?i.ToString())),??Value6?=?655,?Value7?=?baseTime.AddHours(12),??Value8?=?TimeOnly.MinValue,?Value9?=?DateOnly.MaxValue,??Value10?=?Enumerable.Range(0,?5).ToArray()??};??}//?使用我們熟悉的StringBuilder[Benchmark(Baseline?=?true)]??public?string?StringBuilder()??{??var?data?=?Data;??var?sb?=?new?StringBuilder();??sb.Append("Value1:");?sb.Append(data.Value1);??if?(data.Value2?>?10)??{??sb.Append("?,Value2:");?sb.Append(data.Value2);??}??sb.Append("?,Value3:");?sb.Append(data.Value3);??sb.Append("?,Value4:");?sb.Append(data.Value4);??sb.Append("?,Value5:");?sb.Append(data.Value5);??if?(data.Value6?>?20)??{??sb.Append("?,Value6:");?sb.AppendFormat("{0:F2}",?data.Value6);??}??sb.Append("?,Value7:");?sb.AppendFormat("{0:yyyy-MM-dd?HH:mm:ss}",?data.Value7);??sb.Append("?,Value8:");?sb.AppendFormat("{0:HH:mm:ss}",?data.Value8);??sb.Append("?,Value9:");?sb.AppendFormat("{0:yyyy-MM-dd}",?data.Value9);??sb.Append("?,Value10:");??if?(data.Value10?is?null?or?{Length:?0})?return?sb.ToString();??for?(int?i?=?0;?i?<?data.Value10.Length;?i++)??{??sb.Append(data.Value10[i]);??}??return?sb.ToString();??}//?StringBuilder使用Capacity[Benchmark]??public?string?StringBuilderCapacity()??{??var?data?=?Data;??var?sb?=?new?StringBuilder(20480);??sb.Append("Value1:");?sb.Append(data.Value1);??if?(data.Value2?>?10)??{??sb.Append("?,Value2:");?sb.Append(data.Value2);??}??sb.Append("?,Value3:");?sb.Append(data.Value3);??sb.Append("?,Value4:");?sb.Append(data.Value4);??sb.Append("?,Value5:");?sb.Append(data.Value5);??if?(data.Value6?>?20)??{??sb.Append("?,Value6:");?sb.AppendFormat("{0:F2}",?data.Value6);??}??sb.Append("?,Value7:");?sb.AppendFormat("{0:yyyy-MM-dd?HH:mm:ss}",?data.Value7);??sb.Append("?,Value8:");?sb.AppendFormat("{0:HH:mm:ss}",?data.Value8);??sb.Append("?,Value9:");?sb.AppendFormat("{0:yyyy-MM-dd}",?data.Value9);??sb.Append("?,Value10:");??if?(data.Value10?is?null?or?{Length:?0})?return?sb.ToString();??for?(int?i?=?0;?i?<?data.Value10.Length;?i++)??{??sb.Append(data.Value10[i]);??}??return?sb.ToString();??}??//?直接使用+=拼接字符串[Benchmark]??public?string?StringConcat()??{??var?str?=?"";??var?data?=?Data;??str?+=?("Value1:");?str?+=?(data.Value1);??if?(data.Value2?>?10)??{??str?+=?"?,Value2:";?str?+=?data.Value2;??}??str?+=?"?,Value3:";?str?+=?(data.Value3);??str?+=?"?,Value4:";?str?+=?(data.Value4);??str?+=?"?,Value5:";?str?+=?(data.Value5);??if?(data.Value6?>?20)??{??str?+=?"?,Value6:";?str?+=?data.Value6.ToString("F2");??}??str?+=?"?,Value7:";?str?+=?data.Value7.ToString("yyyy-MM-dd?HH:mm:ss");??str?+=?"?,Value8:";?str?+=?data.Value8.ToString("HH:mm:ss");??str?+=?"?,Value9:";?str?+=?data.Value9.ToString("yyyy-MM-dd");??str?+=?"?,Value10:";??if?(data.Value10?is?not?null?&&?data.Value10.Length?>?0)??{??for?(int?i?=?0;?i?<?data.Value10.Length;?i++)??{??str?+=?(data.Value10[i]);??}?????}??return?str;??}??//?使用棧上分配的ValueStringBuilder[Benchmark]??public?string?ValueStringBuilderOnStack()??{??var?data?=?Data;??Span<char>?buffer?=?stackalloc?char[20480];??var?sb?=?new?ValueStringBuilder(buffer);??sb.Append("Value1:");?sb.AppendSpanFormattable(data.Value1);??if?(data.Value2?>?10)??{??sb.Append("?,Value2:");?sb.AppendSpanFormattable(data.Value2);??}??sb.Append("?,Value3:");?sb.AppendSpanFormattable(data.Value3);??sb.Append("?,Value4:");?sb.AppendSpanFormattable(data.Value4);??sb.Append("?,Value5:");?sb.Append(data.Value5);??if?(data.Value6?>?20)??{??sb.Append("?,Value6:");?sb.AppendSpanFormattable(data.Value6,?"F2");??}??sb.Append("?,Value7:");?sb.AppendSpanFormattable(data.Value7,?"yyyy-MM-dd?HH:mm:ss");??sb.Append("?,Value8:");?sb.AppendSpanFormattable(data.Value8,?"HH:mm:ss");??sb.Append("?,Value9:");?sb.AppendSpanFormattable(data.Value9,?"yyyy-MM-dd");??sb.Append("?,Value10:");??if?(data.Value10?is?not?null?&&?data.Value10.Length?>?0)??{??for?(int?i?=?0;?i?<?data.Value10.Length;?i++)??{??sb.AppendSpanFormattable(data.Value10[i]);??}?????}??return?sb.ToString();??}//?使用ArrayPool?堆上分配的StringBuilder[Benchmark]??public?string?ValueStringBuilderOnHeap()??{??var?data?=?Data;??var?sb?=?new?ValueStringBuilder(20480);??sb.Append("Value1:");?sb.AppendSpanFormattable(data.Value1);??if?(data.Value2?>?10)??{??sb.Append("?,Value2:");?sb.AppendSpanFormattable(data.Value2);??}??sb.Append("?,Value3:");?sb.AppendSpanFormattable(data.Value3);??sb.Append("?,Value4:");?sb.AppendSpanFormattable(data.Value4);??sb.Append("?,Value5:");?sb.Append(data.Value5);??if?(data.Value6?>?20)??{??sb.Append("?,Value6:");?sb.AppendSpanFormattable(data.Value6,?"F2");??}??sb.Append("?,Value7:");?sb.AppendSpanFormattable(data.Value7,?"yyyy-MM-dd?HH:mm:ss");??sb.Append("?,Value8:");?sb.AppendSpanFormattable(data.Value8,?"HH:mm:ss");??sb.Append("?,Value9:");?sb.AppendSpanFormattable(data.Value9,?"yyyy-MM-dd");??sb.Append("?,Value10:");??if?(data.Value10?is?not?null?&&?data.Value10.Length?>?0)??{??for?(int?i?=?0;?i?<?data.Value10.Length;?i++)??{??sb.AppendSpanFormattable(data.Value10[i]);??}?????}return?sb.ToString();??}}

結果如下所示。343eecb2ca08f1f44710b91b5a74f644.png從上圖的結果中,我們可以得出如下的結論。

  • 使用StringConcat是最慢的,這種方式是無論如何都不推薦的。

  • 使用StringBuilder要比使用StringConcat快6.5倍,這是推薦的方法。

  • 設置了初始容量的StringBuilder要比直接使用StringBuilder快25%,正如我在你應該為集合類型設置初始大小[1]一樣,設置初始大小絕對是相當推薦的做法。

  • 棧上分配的ValueStringBuilderStringBuilder要快50%,比設置了初始容量的StringBuilder還快25%,另外它的GC次數是最低的。

  • 堆上分配的ValueStringBuilderStringBuilder要快55%,他的GC次數稍高與棧上分配。 從上面的結論中,我們可以發現ValueStringBuilder的性能非常好,就算是在棧上分配緩沖區,性能也比StringBuilder快25%。

源碼解析

ValueStringBuilder的源碼不長,我們挑幾個重要的方法給大家分享一下,部分源碼如下。

//?使用?ref?struct?該對象只能在棧上分配
public?ref?struct?ValueStringBuilder
{//?如果從ArrayPool里分配buffer?那么需要存儲一下//?以便在Dispose時歸還private?char[]??_arrayToReturnToPool;//?暫存外部傳入的bufferprivate?Span<char>?_chars;//?當前字符串長度private?int?_pos;//?外部傳入bufferpublic?ValueStringBuilder(Span<char>?initialBuffer){//?使用外部傳入的buffer就不使用從pool里面讀取的了_arrayToReturnToPool?=?null;_chars?=?initialBuffer;_pos?=?0;}public?ValueStringBuilder(int?initialCapacity){//?如果外部傳入了capacity?那么從ArrayPool里面獲取_arrayToReturnToPool?=?ArrayPool<char>.Shared.Rent(initialCapacity);_chars?=?_arrayToReturnToPool;_pos?=?0;}//?返回字符串的Length?由于Length可讀可寫//?所以重復使用ValueStringBuilder只需將Length設置為0public?int?Length{get?=>?_pos;set{Debug.Assert(value?>=?0);Debug.Assert(value?<=?_chars.Length);_pos?=?value;}}......[MethodImpl(MethodImplOptions.AggressiveInlining)]public?void?Append(char?c){//?添加字符非常高效?直接設置到對應Span位置即可int?pos?=?_pos;if?((uint)?pos?<?(uint)?_chars.Length){_chars[pos]?=?c;_pos?=?pos?+?1;}else{//?如果buffer空間不足,那么會走GrowAndAppend(c);}}[MethodImpl(MethodImplOptions.AggressiveInlining)]public?void?Append(string??s){if?(s?==?null){return;}//?追加字符串也是一樣的高效int?pos?=?_pos;//?如果字符串長度為1?那么可以直接像追加字符一樣if?(s.Length?==?1?&&?(uint)?pos?<?(uint)?_chars?.Length){_chars[pos]?=?s[0];_pos?=?pos?+?1;}else{//?如果是多個字符?那么使用較慢的方法AppendSlow(s);}}private?void?AppendSlow(string?s){//?追加字符串?空間不夠先擴容//?然后使用Span復制?相當高效int?pos?=?_pos;if?(pos?>?_chars.Length?-?s.Length){Grow(s.Length);}s
#if?!NETCOREAPP.AsSpan()
#endif.CopyTo(_chars.Slice(pos));_pos?+=?s.Length;}//?對于需要格式化的對象特殊處理[MethodImpl(MethodImplOptions.AggressiveInlining)]public?void?AppendSpanFormattable<T>(T?value,?string??format?=?null,?IFormatProvider??provider?=?null)where?T?:?ISpanFormattable{//?ISpanFormattable非常高效if?(value.TryFormat(_chars.Slice(_pos),?out?int?charsWritten,?format,?provider)){_pos?+=?charsWritten;}else{Append(value.ToString(format,?provider));}}[MethodImpl(MethodImplOptions.NoInlining)]private?void?GrowAndAppend(char?c){//?單個字符擴容在添加Grow(1);Append(c);}//?擴容方法[MethodImpl(MethodImplOptions.NoInlining)]private?void?Grow(int?additionalCapacityBeyondPos){Debug.Assert(additionalCapacityBeyondPos?>?0);Debug.Assert(_pos?>?_chars.Length?-?additionalCapacityBeyondPos,"Grow?called?incorrectly,?no?resize?is?needed.");//?同樣也是2倍擴容,默認從對象池中獲取bufferchar[]?poolArray?=?ArrayPool<char>.Shared.Rent((int)?Math.Max((uint)?(_pos?+?additionalCapacityBeyondPos),(uint)?_chars.Length?*?2));_chars.Slice(0,?_pos).CopyTo(poolArray);char[]??toReturn?=?_arrayToReturnToPool;_chars?=?_arrayToReturnToPool?=?poolArray;if?(toReturn?!=?null){//?如果原本就是使用的對象池?那么必須歸還ArrayPool<char>.Shared.Return(toReturn);}}//?[MethodImpl(MethodImplOptions.AggressiveInlining)]public?void?Dispose(){char[]??toReturn?=?_arrayToReturnToPool;this?=?default;?//?為了安全,在釋放時置空當前對象if?(toReturn?!=?null){//?一定要記得歸還對象池ArrayPool<char>.Shared.Return(toReturn);}}
}

從上面的源碼我們可以總結出ValueStringBuilder的幾個特征:

  • 比起StringBuilder來說,實現方式非常簡單。

  • 一切都是為了高性能,比如各種Span的用法,各種內聯參數,以及使用對象池等等。

  • 內存占用非常低,它本身就是結構體類型,另外它是ref struct,意味著不會被裝箱,不會在堆上分配。

適用場景

ValueStringBuilder是一種高性能的字符串創建方式,針對于不同的場景,可以有不同的使用方式。1.非常高頻次的字符串拼接的場景,并且字符串長度較小此時可以使用棧上分配ValueStringBuilder。 大家都知道現在ASP.NET Core性能非常好,在其依賴的內部庫UrlBuilder[2]中,就使用棧上分配,因為棧上分配在當前方法結束后內存就會回收,所以不會造成任何GC壓力。8406d8374815a3f4b598ecb9d156d31a.png2.非常高頻次的字符串拼接場景,但是字符串長度不可控此時使用ArrayPool指定容量ValueStringBuilder。比如在.NET BCL庫中有很多場景使用,比如動態方法的ToString[3]實現。從池中分配雖然沒有棧上分配那么高效,但是一樣的能降低內存占用和GC壓力。9c16b9fc17670536f49e8678ed76311c.png3. 非常高頻次的字符串拼接場景,但是字符串長度可控,此時可以棧上分配和ArrayPool分配聯合使用,比如正則表達式[4]解析類中,如果字符串長度較小那么使用棧空間,較大那么使用ArrayPool。d79671fe682a5abe8f963c1b0fe60b6d.png

需要注意的場景

1.在async\await中無法使用ValueStringBuilder。原因大家也都知道,因為ValueStringBuilderref struct它只能在棧上分配async\await會編譯成狀態機拆分await前后的方法,所以ValueStringBuilder不好在方法內傳遞,不過編譯器也會警告。558667c34a3163bab84a844e66bbccea.png2.無法將ValueStringBuilder作為返回值返回,因為在當前棧上分配,方法結束后它會被釋放,返回它將指向未知的地址。這個編譯器也會警告。3a858dacc12a8ef6964711367cabbb26.png3.如果要將ValueStringBuilder傳遞給其它方法,那么必須使用ref傳遞,否則發生值拷貝會存在多個實例。這個編譯器不會警告,但是你必須非常注意。97a24949bc7a6e890521dbf1a2953ed4.png4. 如果使用棧上分配,那么Buffer大小控制在5KB內比較穩妥,至于為什么需要這樣,后面有機會在講一講。

總結

今天和大家分享了一下高性能幾乎無內存占用的字符串拼接結構體ValueStringBuilder,在大多數的場景還是推薦大家使用。但是要非常注意上面提到的[5]的幾個場景,如果不符合條件,那么大家還是可以使用高效的StringBuilder來進行字符串拼接。

本文源碼鏈接:?

https://github.com/InCerryGit/BlogCode-Use-ValueStringBuilder

參考資料

[1]

你應該為集合類型設置初始大小: https://www.cnblogs.com/InCerry/p/Dotnet-Opt-Perf-You-Should-Set-Capacity-For-Collection.html

[2]

UrlBuilder: https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/src/libraries/System.Private.Uri/src/System/UriBuilder.cs#L284-L362

[3]

ToString: https://github.com/dotnet/runtime/blob/43dd0a74ab524278620d8c6a9d33a9b73b2d2228/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs#L137

[4]

正則表達式: https://github.com/dotnet/runtime/blob/43dd0a74ab524278620d8c6a9d33a9b73b2d2228/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs#L150

[5]

上面提到的: #需要注意的場景

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281521.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281521.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281521.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Buildroot 龍芯1C支持指南

本文轉載自&#xff1a;https://github.com/pengphei/smartloong-sphinx/blob/master/source/cn/loongson1c_buildroot_guide.rst Buildroot 龍芯1C支持指南 引子&#xff1a;從龍芯1C預訂拿到板子已經很長一段時間了&#xff0c;因為各種事情&#xff0c;一直讓它呆在角落的冷…

STOLUCK:經濟下行的當下 ,STO或將幫助中小企業度過寒冬

2018年被稱為創業陣亡率特別高的一年&#xff0c;相關報道稱有近20%的創業團隊面臨“后續融資跟不上&#xff0c;可能死在春天來臨之前”的窘境。經濟不景氣的當下&#xff0c;上下游資金不足&#xff0c;信貸機構沒錢&#xff0c;風投業捉襟見肘。實際今年3月份開始&#xff0…

[ 轉載 ] Java面試精選【Java基礎第一部分】

http://www.cnblogs.com/hnlshzx/p/3491587.html 轉載于:https://www.cnblogs.com/ILoke-Yang/p/8137326.html

html如何自動調整邊框大小,html – Chrome與大小調整:顯示中的邊框:表格

我正在使用display&#xff1a;table做一個小的2窗格布局.對于間距(也來自背景圖像),我使用填充.因為我需要孩子們有一個確切的寬度&#xff1a;50&#xff05;來自可用空間(考慮到父div的填充),我使用Box-sizing&#xff1a;border-Box.這在Opera中運行良好,但在Chrome中,框大…

淺析C# Dictionary實現原理

一、前言二、理論知識1、Hash 算法2、Hash 桶算法3、解決沖突算法三、Dictionary 實現1. Entry 結構體2. 其它關鍵私有變量3. Dictionary - Add 操作4. Dictionary - Find 操作5. Dictionary - Remove 操作6. Dictionary - Resize 操作(擴容)7. Dictionary - 再談 Add 操作8. C…

對特朗普獲勝感到意外? 那你是被社交媒體迷惑了

北京時間11月10日消息&#xff0c;據外媒報道&#xff0c;昨天曠日持久的美國總統選戰終于告一段落&#xff0c;特朗普的獲勝讓民調徹底成了一張廢紙&#xff0c;而早就在Facebook上提前歡慶希拉里勝利的人則徹底蒙圈了&#xff0c;就連萬里之外的中國吃瓜群眾們也開始追著許多…

貓晚流量再創記錄,阿里云直播方案護航優酷2500萬用戶體驗

2019獨角獸企業重金招聘Python工程師標準>>> 對“剁手黨而言&#xff0c;天貓雙11早已經超越了簡單的“買買買”&#xff0c;更是一場邊看邊玩的狂歡盛宴。今年的天貓雙11狂歡夜晚會&#xff08;簡稱“貓晚”&#xff09;在上海舉辦&#xff0c;這臺兼具年輕潮流與國…

python實現二叉樹和它的七種遍歷

介紹&#xff1a; 樹是數據結構中非常重要的一種&#xff0c;主要的用途是用來提高查找效率&#xff0c;對于要重復查找的情況效果更佳&#xff0c;如二叉排序樹、FP-樹。另外可以用來提高編碼效率&#xff0c;如哈弗曼樹。 代碼&#xff1a; 用python實現樹的構造和幾種遍歷算…

.NET性能系列文章二:Newtonsoft.Json vs System.Text.Json

微軟終于追上了&#xff1f;圖片來自 Glenn Carstens-Peters[1]Unsplash[2]歡迎來到.NET 性能系列的另一章。這個系列的特點是對.NET 世界中許多不同的主題進行研究、基準和比較。正如標題所說的那樣&#xff0c;重點在于使用最新的.NET7 的性能。你將看到哪種方法是實現特定主…

android gpu平板 推薦,性能強的不像話,最強安卓平板華為平板M6上手

原標題&#xff1a;性能強的不像話&#xff0c;最強安卓平板華為平板M6上手你為什么買平板電腦&#xff1f;當這一問題問出以后&#xff0c;許多朋友的表情都很微妙&#xff0c;隨后大概率的回答則相當統一&#xff1a;"我買平板干嘛&#xff1f;"。其實得到這樣一個…

【Python】HackBack(獲取暴力破解服務器密碼的IP來源)

1、前言 又在0x00sec上翻到好東東。 https://0x00sec.org/t/python-hackback-updated/882 帖子里的腳本會得到那些暴力服務器密碼失敗的IP和用戶名&#xff0c;并且使用shodan api做一個溯源定位。 #!/usr/bin/python3.4 import re import urllib.request import json log_path…

企業應用“數據優先”革命的下一個主戰場:安全與運營

根據IDC發布的2015年全球CIO日程預測&#xff0c;80%的CIO將提供一個實現創新和改善業務決策的新體系架構。 大數據時代&#xff0c;企業軟件市場正在經歷一次大遷移&#xff0c;數以十億計的企業IT支出預算將投向“數據優先”應用&#xff0c;而不是長久以來以業務流程和工作流…

給Web開發人員的以太坊入坑指南

以太坊現在各種學習資料數不勝數&#xff0c;但由于以太坊正處于飛速發展階段&#xff0c;有些學習資料很快就過時了。所以想找到有價值的資料無異于大海撈針。我費了很大功夫&#xff0c;才建立起對以太坊的整體認識&#xff0c;搞清楚它的工作機制。我相信很多躍躍欲試的開發…

和碩看重物聯網大勢 程建中:從擅長領域出發

物聯網(IoT)前景可期已是全球科技產業的共識&#xff0c;但是如何真正找出到位的商機&#xff0c;卻考驗產業鏈業者的智能。蘋果iPhone代工廠和碩聯合科技執行長程建中表示&#xff0c;物聯網與大數據相關應用商機看俏&#xff0c;物聯網筑的夢比網際網路還大&#xff0c;當年網…

html選擇文本框后提示消失,兩種方法實現文本框輸入內容提示消失

第一種方法&#xff1a;基于HTML5 input標簽的新特性 - placeholder 。另外&#xff0c;x-webkit-speech 屬性可以實現語音輸入功能。第二種方法&#xff1a;用span模擬&#xff0c;定位span&#xff0c;借助JS鍵盤事件判斷輸入&#xff0c;確定span里的內容顯示隱藏。無標題文…

TensorFlow基本計算單元——變量

# -*- coding: utf-8 -*- import tensorflow as tf a 3 # 創建變量 w tf.Variable([[0.5, 1.0]]) #行向量 x tf.Variable([[2.0], [1.0]]) y tf.matmul(w, x) #矩陣相乘 print(y) # Tensor("MatMul:0", shape(1, 1), dtypefloat32)init_op tf.global_variables…

程序人生:織夢dedecms后臺/會員驗證碼關閉

dedecms默認是所有的功能幾乎只要用到驗證碼的地方我們都需要驗證的&#xff0c;如果要關閉一些驗證功能我們可以參考下面的教程&#xff0c;這里介紹了關閉后臺&#xff0c;留言板&#xff0c;會員系統等驗證碼功能關閉了。提示&#xff1a;支持DedeCMS V5.6 以上的所有版本取…

html中圖片的屬性優化,Html標簽元素在SEO中的優化方式(二)

接上html標簽元素在SEO中的優化方式(一)中對HTML界面的介紹&#xff0c;我們今天繼續補充HTML標簽的SEO優化方式在內容中有幾個值得去研究一下的優化元素--導航和內部鏈接&#xff1a;很明顯的一點&#xff0c;建立導航會使搜索引擎可以容易的確定網站結構&#xff0c;但是很多…

Gartner認為安全性將取代成本和敏捷性成為政府部門采用云服務的首要原因

全球領先的信息技術研究和顧問公司Gartner表示&#xff0c;公有云如今具備可擴展性、計算威力、海量存儲和安全性&#xff0c;可打造更好的政府數字化平臺并滿足對業績和價值不斷增長的期望值。 Gartner預計到2018年&#xff0c;提升的安全性將取代成本節約和敏捷性成為政府部門…

一款簡單的縮放拖拽圖片控件

本文介紹一個針對 .NET 桌面應用程序的獨立圖片縮放拖拽顯示控件 SQPhoto[1]。SQPhoto 是一個 Windows 桌面應用的組件&#xff0c;支持 .NET6 和 .NET Framework 4.6 。基于 PictureBox 的圖片展示工具&#xff0c;增加了拖動和縮放功能&#xff0c;便于在某些場景下的圖片展…