2015-08-04 21 views
12

Tôi có một ứng dụng sử dụng số lượng lớn các chuỗi. Vì vậy, tôi có một số vấn đề về cách sử dụng bộ nhớ. Tôi biết rằng một trong những giải pháp tốt nhất trong trường hợp này là sử dụng một DB, nhưng tôi không thể sử dụng điều này cho thời điểm này, vì vậy tôi đang tìm kiếm các giải pháp khác.Chuỗi VS Byte [], sử dụng bộ nhớ

Trong chuỗi C# được lưu trữ trong Utf16, điều đó có nghĩa là tôi đã mất một nửa dung lượng bộ nhớ so với Utf8 (đối với phần chính của chuỗi của tôi). Vì vậy, tôi quyết định sử dụng mảng byte của chuỗi utf8. Nhưng với sự ngạc nhiên của tôi, giải pháp này chiếm không gian bộ nhớ gấp đôi so với các chuỗi đơn giản trong ứng dụng của tôi.

Vì vậy, tôi đã thực hiện một số thử nghiệm đơn giản, nhưng tôi muốn biết ý kiến ​​của các chuyên gia để chắc chắn.

Test 1: phân bổ chuỗi dài cố định

var stringArray = new string[10000]; 
var byteArray = new byte[10000][]; 
var Sb = new StringBuilder(); 
var utf8 = Encoding.UTF8; 
var stringGen = new Random(561651); 
for (int i = 0; i < 10000; i++) { 
    for (int j = 0; j < 10000; j++) { 
     Sb.Append((stringGen.Next(90)+32).ToString()); 
    } 
    stringArray[i] = Sb.ToString(); 
    byteArray[i] = utf8.GetBytes(Sb.ToString()); 
    Sb.Clear(); 
} 
GC.Collect(); 
GC.WaitForFullGCComplete(5000); 

sử dụng bộ nhớ

00007ffac200a510  1  80032 System.Byte[][] 
00007ffac1fd02b8  56  152400 System.Object[] 
000000bf7655fcf0  303  3933750  Free 
00007ffac1fd5738 10004 224695091 System.Byte[] 
00007ffac1fcfc40 10476 449178396 System.String 

Như chúng ta có thể thấy, byte mảng mất không gian bộ nhớ hai lần ít hơn, không ngạc nhiên thực sự ở đây.

thử nghiệm 2: Random phân bổ kích thước chuỗi (với chiều dài thực tế)

var stringArray = new string[10000]; 
var byteArray = new byte[10000][]; 
var Sb = new StringBuilder(); 
var utf8 = Encoding.UTF8; 
var lengthGen = new Random(2138784); 
for (int i = 0; i < 10000; i++) { 
    for (int j = 0; j < lengthGen.Next(100); j++) { 
     Sb.Append(i.ToString()); 
     stringArray[i] = Sb.ToString(); 
     byteArray[i] = utf8.GetBytes(Sb.ToString()); 
    } 
    Sb.Clear(); 
} 
GC.Collect(); 
GC.WaitForFullGCComplete(5000); 

sử dụng bộ nhớ

00007ffac200a510  1  80032 System.Byte[][] 
000000be2aa8fd40  12  82784  Free 
00007ffac1fd02b8  56  152400 System.Object[] 
00007ffac1fd5738  9896  682260 System.Byte[] 
00007ffac1fcfc40 10368  1155110 System.String 

Chuỗi mất một chút ít không gian hơn gấp đôi thời gian không gian bộ nhớ của mảng byte . Với chuỗi ngắn hơn tôi đã mong đợi một chi phí cao hơn cho dây. Nhưng có vẻ như ngược lại, tại sao?

thử nghiệm 3: String mô hình tương ứng với ứng dụng của tôi

var stringArray = new string[10000]; 
var byteArray = new byte[10000][]; 
var Sb = new StringBuilder(); 
var utf8 = Encoding.UTF8; 
var lengthGen = new Random(); 
for (int i=0; i < 10000; i++) { 
    if (i%2 == 0) { 
     for (int j = 0; j < lengthGen.Next(100000); j++) { 
      Sb.Append(i.ToString()); 
      stringArray[i] = Sb.ToString(); 
      byteArray[i] = utf8.GetBytes(Sb.ToString()); 
      Sb.Clear(); 
     } 
    } else { 
     stringArray[i] = Sb.ToString(); 
     byteArray[i] = utf8.GetBytes(Sb.ToString()); 
     Sb.Clear(); 
    } 
} 
GC.Collect(); 
GC.WaitForFullGCComplete(5000); 

sử dụng bộ nhớ

00007ffac200a510  1  80032 System.Byte[][] 
00007ffac1fd02b8  56  152400 System.Object[] 
00007ffac1fcfc40  5476  198364 System.String 
00007ffac1fd5738 10004  270075 System.Byte[] 

Đây chuỗi mất không gian bộ nhớ ít hơn nhiều so với byte. Điều này có thể gây ngạc nhiên, nhưng tôi cho rằng chuỗi rỗng chỉ được tham chiếu một lần. Là nó? Nhưng tôi không biết nếu điều này có thể giải thích tất cả những khác biệt lớn. Có lý do nào khác không? giải pháp tốt nhất là gì?

Trả lời

5

Điều này có thể gây ngạc nhiên, nhưng tôi cho rằng chuỗi rỗng chỉ được tham chiếu một lần.

Có, trống StringBuilder trả về string.Empty là kết quả của nó. Đoạn mã dưới đây in True:

var sb = new StringBuilder(); 
Console.WriteLine(object.ReferenceEquals(sb.ToString(), string.Empty)); 

Nhưng tôi không biết nếu điều này có thể giải thích tất cả những gì khác biệt rất lớn.

Vâng, điều này hoàn toàn giải thích. Bạn đang tiết kiệm trên 5.000 string đối tượng. Sự khác biệt về byte là khoảng 270.000- (198.000/2), khoảng 170 kByte.Chia cho 5 bạn nhận được 34 byte cho mỗi đối tượng, đó là khoảng kích thước của một con trỏ trên một hệ thống 32-bit.

Giải pháp tốt nhất là gì?

Làm điều tương tự: làm cho mình một mảng trống private static readonly, và sử dụng nó mỗi khi bạn nhận được string.Empty từ sb.ToString():

private static readonly EmptyBytes = new byte[0]; 
... 
else 
{ 
    stringArray[i] = Sb.ToString(); 
    if (stringArray[i] == string.Empty) { 
     byteArray[i] = EmptyBytes; 
    } else { 
     byteArray[i] = utf8.GetBytes(Sb.ToString()); 
    } 
    Sb.Clear(); 
} 
+0

Tại sao không sử dụng 'string.IsNullOrEmpty (stringArray [i])' ? –

+0

@MarkJansen Nó chỉ là một minh họa: Tôi biết chắc chắn rằng 'stringArray [i]' rỗng trong nhánh 'else' của' if (i% 2 == 0) 'có điều kiện, vì vậy tôi có thể bỏ qua sự so sánh với 'string.Empty' hoàn toàn. – dasblinkenlight

+0

Thú vị, thực sự sử dụng một tham chiếu byte trống cải thiện việc sử dụng bộ nhớ rất nhiều. Tôi quên nói trong bài viết của mình, hơn là 64 bit và đơn vị là byte. Dù sao điều này không thay đổi ý tưởng của bạn giải thích, ngay cả khi tôi thấy rằng 34 byte cho một con trỏ nó rất nhiều, (thậm chí nhiều hơn với 26 byte trên mỗi chuỗi). Tôi đã mất kích thước của con trỏ 10K (80032 KB tức là 25% kích thước bộ nhớ hữu ích) với System.Byte [] []. Có cách nào để tránh việc sử dụng quá nhiều không? Có thể không phải với mảng byte. – Edeen

Các vấn đề liên quan