SqlDataReader 性能列表或列表<对象 []>对象>
我一直在尝试尽可能快地从 SQL 服务器读取数据的方法,并且发现了一个有趣的发现。如果我将数据读入 List
而不是 List
,性能会提高一倍以上。
我怀疑这是因为不必在字段上调用 ToString()
方法,但我一直认为使用对象会对性能产生负面影响。
是否有任何理由不使用对象数组列表而不使用字符串数组?
编辑:我刚刚想到的一个想法是该数据的存储大小。将数据存储在对象数组中会比存储在字符串中占用更多空间吗?
这是我的测试代码:
private void executeSqlObject()
{
List<object[]> list = new List<object[]>();
using (SqlConnection cnn = new SqlConnection(_cnnString))
{
cnn.Open();
SqlCommand cmd = new SqlCommand("select * from test_table", cnn);
SqlDataReader reader = cmd.ExecuteReader();
int fieldCount = reader.FieldCount;
while (reader.Read())
{
object[] row = new object[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
row[i] = reader[i];
}
list.Add(row);
}
}
}
private void executeSqlString()
{
List<string[]> list = new List<string[]>();
using (SqlConnection cnn = new SqlConnection(_cnnString))
{
cnn.Open();
SqlCommand cmd = new SqlCommand("select * from test_table", cnn);
SqlDataReader reader = cmd.ExecuteReader();
int fieldCount = reader.FieldCount;
while (reader.Read())
{
string[] row = new string[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
row[i] = reader[i].ToString();
}
list.Add(row);
}
}
}
private void runTests()
{
Stopwatch watch = new Stopwatch();
for (int i = 0; i < 10; i++)
{
watch.Start();
executeSqlObject();
Debug.WriteLine("Object Time: " + watch.ElapsedMilliseconds.ToString());
watch.Reset();
}
for (int i = 0; i < 10; i++)
{
watch.Start();
executeSqlString();
Debug.WriteLine("String Time: " + watch.ElapsedMilliseconds.ToString());
watch.Reset();
}
}
以及结果:
Object Time: 879
Object Time: 812
Object Time: 825
Object Time: 882
Object Time: 880
Object Time: 905
Object Time: 815
Object Time: 799
Object Time: 823
Object Time: 817
Average: 844
String Time: 1819
String Time: 1790
String Time: 1787
String Time: 1856
String Time: 1795
String Time: 1731
String Time: 1792
String Time: 1799
String Time: 1762
String Time: 1869
Average: 1800
I have been experimenting with ways to read data from a SQL server as quickly as possible and I came across an interesting discovery. If I read the data into a List<object[]>
instead of a List<string[]>
, performance increases by more than double.
I suspect this is due to not having to call the ToString()
method on the fields, but I always thought that using objects had a negative impact on performance.
Is there any reason to not use a list of object arrays instead of string arrays?
EDIT: One thought I just had was the storage size of this data. Will storing the data in object arrays take more room than as strings?
Here is my test code:
private void executeSqlObject()
{
List<object[]> list = new List<object[]>();
using (SqlConnection cnn = new SqlConnection(_cnnString))
{
cnn.Open();
SqlCommand cmd = new SqlCommand("select * from test_table", cnn);
SqlDataReader reader = cmd.ExecuteReader();
int fieldCount = reader.FieldCount;
while (reader.Read())
{
object[] row = new object[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
row[i] = reader[i];
}
list.Add(row);
}
}
}
private void executeSqlString()
{
List<string[]> list = new List<string[]>();
using (SqlConnection cnn = new SqlConnection(_cnnString))
{
cnn.Open();
SqlCommand cmd = new SqlCommand("select * from test_table", cnn);
SqlDataReader reader = cmd.ExecuteReader();
int fieldCount = reader.FieldCount;
while (reader.Read())
{
string[] row = new string[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
row[i] = reader[i].ToString();
}
list.Add(row);
}
}
}
private void runTests()
{
Stopwatch watch = new Stopwatch();
for (int i = 0; i < 10; i++)
{
watch.Start();
executeSqlObject();
Debug.WriteLine("Object Time: " + watch.ElapsedMilliseconds.ToString());
watch.Reset();
}
for (int i = 0; i < 10; i++)
{
watch.Start();
executeSqlString();
Debug.WriteLine("String Time: " + watch.ElapsedMilliseconds.ToString());
watch.Reset();
}
}
And the results:
Object Time: 879
Object Time: 812
Object Time: 825
Object Time: 882
Object Time: 880
Object Time: 905
Object Time: 815
Object Time: 799
Object Time: 823
Object Time: 817
Average: 844
String Time: 1819
String Time: 1790
String Time: 1787
String Time: 1856
String Time: 1795
String Time: 1731
String Time: 1792
String Time: 1799
String Time: 1762
String Time: 1869
Average: 1800
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
object
仅在导致额外装箱时增加开销。即便如此,这种影响也相当小。在您的情况下,reader[i]
始终返回object
。您已经将其作为对象
,无论它是对字符串还是int等的引用。当然调用.ToString() 增加开销;在大多数情况下(int、DateTime 等),这涉及格式化代码和分配一个(或多个)额外字符串。通过更改为字符串,您将更改数据(更糟糕的是,IMO - 例如,您无法再对日期进行正确的排序)并增加开销。这里的边缘情况是,如果所有列实际上已经是字符串 - 在这种情况下,您只需添加一些虚拟方法调用(但没有额外的实际工作)。
作为信息,如果您追求原始性能,我强烈建议您查看微型 ORM,例如 dapper。它们经过深度优化,但避免了“完整”ORM 的负担。例如,在 dapper:
will 中,我期望在为您提供强类型对象数据时表现非常相似。
object
only adds overhead if you are causing additional boxing. And even then, this impact is fairly minimal. In your case,reader[i]
always returnsobject
. You already have it asobject
, no matter whether that is a reference to a string, or an int, etc. Of course calling.ToString()
adds overhead; in most cases (int, DateTime, etc) this involves both formatting code and the allocation of one (or more) extra string. By changing tostring
you are changing the data (for the worse, IMO - for example, you can no longer do correct sorts on dates, for example) and adding overhead. The edge case here is if all the columns are already actually strings - in which case you just add a few virtual method calls (but no extra real work).For info, if you are after raw performance, I thoroughly recommend looking at the micro-ORMs such as dapper. They are heavily optimised, but avoid the weight of "full" ORMs. For example, in dapper:
will, I expect, perform very comparably while giving you strongly typed object data.
这取决于将检索到的值放入数组后您想要对它们执行什么操作,如果您愿意将每个值视为一个对象,那么拥有一个对象列表就可以了,但是如果您想将它们视为字符串,那么在某个时候你将不得不将对象转换/转换回字符串,这样你就会在某个地方产生成本。
正如 Cory 提到的,如果您从 SqlDataReader 中以字符串形式读取值,则应该使用 GetString(int) 方法进行测试,而不是对该值调用 ToString() ,并将其用作基准。
或者,您可以将值读入数据集,而不是使用数组,这样以后使用起来可能会更容易。
归根结底,什么是最好的很大程度上取决于您从数据库检索结果后想要如何使用它们。
It would depend on what you wanted to do with the retrieved values after you got them into the arrays, if you're happy to treat each value as an object then having a list of objects is fine, but if you want to treat them as strings then at some point you're going to have to convert/cast the object back to a string so you're going to incur the cost somewhere.
As Cory mentioned if you're reading the value as a string from the SqlDataReader you should test using the GetString(int) method rather than calling ToString() on the value, and use this as the benchmark.
Alternatively, rather than use arrays you can read the values into a DataSet which may prove easier to work with afterwards.
End of the day, what's the best depends a lot on how you want to use the results after retrieving them from the database.