THE SQL Server Blog Spot on the Web
Welcome to SQLblog.com - The SQL Server blog spot on the web Sign in | Join | Help
in Search

Joe Chang

Why Logical IO is a poor performance metric

Many years ago, I attended a conference where a very highly respected Oracle performance expert advocated the use of logical IO count as a performance metric. The argument was that a logical IO is a fairly substantial operation relative to others. I suppose that detailed logical IO was already collected supported this. So why would logical IO be advocated over CPU? On the Windows side, many people have probably noticed that Profiler frequently shows 0 CPU for low cost queries.

(The resolution of Profiler in SQL Server 2000 was some approximate integer multiple of 10 ms for the single processor kernel and 15.625 ms for multi-processor kernel. It does turn out that averaging many calls yields a reasonably accurate CPU measure, possibly to the range of 1 msec.)

I am not sure what the situation is for Oracle, which runs on many platforms, UNIX, Windows and other OS. It cannot be easy collecting performance counters whose meaning is uniform across all the different platforms.

 

Anyways, the argument was convincing enough that I thought it warranted investigation on the SQL Server side. By comparing logical IO and average CPU (measured in full saturation load tests where SQL Server is driven to near 100% CPU), it was quickly apparent that Logical IO and true cost had no meaningful relation.

 

Consider a table for which the indexes have depth 4, so that there is a root level, 2 intermediate levels and the leaf level. The index seek for a single index key (that is, no key lookup) would generate 4 logical IO (LIO). Now consider if a key lookup is required. If the table has a clustered index also of depth 4, each key lookup generates 4 LIO. If the table were a heap, each key lookup generates 1 LIO. In actuality, the key lookup to a heap is about 20-30% less expensive than a key lookup to a clustered index, not anywhere close to the 4:1 LIO ratio. Now consider a loop join between two tables. The index seek to outer source generates 4-5 LIO, 4 for the index depth, and possibly one or more IO for additional rows that do not fit in the same leaf level page. Each index seek to the inner source generates 4 LIO as the index is traversed. For a loop join involving 100 rows in a one-to-one join, (no key lookups) there are 404 or so LIO. Now consider a hash or merge join for the same number of rows (for which there is an explicit SARG on each source). There are 4-5 LIO to each table, for a total of 8-10 LIO. In actuality, the cost between the 3 join types at this row count is not far different (the hash join has a higher startup cost than a loop join, but lower cost per incremental row), but the LIO ratio is 404:10!

 

The SQL Server Cost Based Optimizer itself clearly does not use LIO for its cost estimation. Just compare an index seek for tables and indexes with different index depth. The cost is the same, which is approximately true. After assembling a very deep collection of SQL Server performance measurements, I went back to the Oracle expert to ask about the LIO argument. He said that in the intervening time, a huge amount of evidence on the Oracle side also indicated LIO is a poor metric and this had been abandoned.

 

The SQL Server 2005 DMV dm_exec_query_stats keep milli-sec resolution CPU stats, and micro-sec in SQL Server 2008. I strongly suggest people abandon LIO as a performance metric. Use CPU (total_worker_time) for non-parallel execution plans. For parallel plans, the DMV reports 1000. So I suppose one must revert to Profiler traces for parallel execution plans. (If any one know how to get parallel plan CPU from DMV, please advise.)

 

Note also, the other post indicating physical IO has a completely different cost structure than logical IO.


Published Wednesday, September 10, 2008 1:08 PM by jchang

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Mario A Chavez said:

Joe;

I don't know what was the exactly advise from the Oracle "expert", but reducing LIO is meant that a LIO can cause PIO, not all the time but it's possible, this situation can have or not noticeable CPU usage.

Even if you have all data in DB buffers cache and all your operations are pure LIO, if you have a big LIO number everytime you run your query, this big number will be reflected on how quick your operation can return the data asked and how quick can you leave alone the db buffers latch for other requests.

So the advise to tune LIO on your query is not really meant to reduce your CPU usage, but to reduce the resources - if it's possible - that your queries use and be a good database citizen.

Mario

September 10, 2008 2:19 PM
 

jchang said:

What you are saying is that you use LIO to assess the guilty party, its some resource which may not be related to CPU. What I said is LIO is not a meaningful measure of any guilt: PIO or memory or CPU, which is most of the resources that have meaning.

Consider 2 queries, one doe 10000 key lookups to a clustered index, another does 10000 to a heap. The first generates 40K LIO, the second 10K. Both have the same likelihood of generating PIO, both generate the same memory usage, the second does have 20-30% lower CPU, and yet LIO counting assessed 4X the guilt to the first!

If you are after PIO, then go after PIO not LIO. Even if you are after memory, after fixing the big CPU consumers, its very likely you will have fixed the memory consumers as well.

The main resource to watch is CPU, if a query finishes quickly with minimum CPU, thats being a good citizen, LIO has no bearing. (Duration is a good counter to watch after CPU has been brought down.) There are examples of good citizenship, but again LIO count has no bearing.

September 10, 2008 6:20 PM
 

Uri Dimant said:

Joe

In general,if query used less logical IOs is better and faster than using more,thus tuning query towards lower number of logical IOs in most cases produces query that is faster and uses less resources. But  you are absolutely  right, that reading the same data may end up showing the different results as you showed us above.

In OLTP applications people have trying avoid using a heap so I would say your example is a little bit artificial. I think it depends on what kind of queries you issue on the server whether it is large queries (larger amount of LIO) or queries that executed very very frequently.

September 10, 2008 11:36 PM
 

Michael Zilberstein said:

Uri,

> if query used less logical IOs is better and faster than using more

That's not always the case - depends on IO type (sequential or random) for example. Classical case of the opposite: scan vs seek + lookup. Optimizer usually prefers scan over seek + lookup when > 1-1.5% of the table should be returned. In such a case scan would consume more IO but the overall duration will be lower.

September 11, 2008 2:29 AM
 

jchang said:

Sometimes people just want to be difficult. The situation is: there is a reliable metric (CPU) and an unreliable metric (LIO) available. Why is it people to gravitate to the unreliable metric? In most cases? yes, but if you tune to CPU, it is all cases! So why work with the metric that is not reliable? If query averages 10000 LIO, nobody has any idea what that means, because we do not know the source of the LIO. If CPU is 100ms, I can guarantee you that the upper bound on your system is 10 calls/sec per core and if you have an 8 core, the upper bound 80/sec (more likely 20-30% lower than 80). If you tune a query reducing it 10 LIO, you don't know how much a gain you made. If you tune it down to 10ms, you made 10X gain. If you are doing tuning, it probably means you are changing the execution plan. When you change the execution plan, LIO from one plan has no relation to LIO in a different plan!

If you what another example. Suppose a query involves a loop join for 10000 rows (outer source) and the index depth to the inner source table is 4, This means your query is about 40K LIO. Suppose the inner source table is 30K pages in size (240MB) and you forced a hash join. Then LIO will be around 30K. Did you improve it? by LIO yes, by CPU no! The loop join for 10K rows probably cost around 50 cpu-ms. The hash with scan (mostly from the scan) will cost more.

September 11, 2008 2:49 AM
 

Michael Zilberstein said:

Joe,

What you're talking about is an ideal world. When I come to the client that has severe performance problems, usually I don't have enough time to set Profiler and analyze results afterwards with cup of coffee. If I have to detect most problematic queries instantly, going on logical IO from dm_exec_query_stats is the first choice. When frequently running query shows tens or hundreds thousands logical reads on average, it indicates one of the 2 options: scan on heavy table or extremely heavy seek + lookup. Both should be taken care of. Third option is that it is heavy report "by design" but from my experience this is unlikely option.

By the way, scan + hash join can cost more CPU than seek + lookup + nested loops join but still will be first choice because Optimizer will estimate that this way overall duration will be lower. So measuring by CPU only can be misleading, sometimes more CPU is a better choice.

September 11, 2008 3:47 AM
 

Uri Dimant said:

Misha

I t depends on data access as I pointed in my post (how SQL Server Optimizer counts SEEKs and TABLE Scans)

Joe

>If you are doing tuning, it probably means you are changing the >execution plan. When you change the execution plan, LIO from one >plan has no relation to LIO in a different plan!

Yes ,before I add/remove the index (for example)I think how it can be affected in terms of readig data/index pages.In most cases (again)if query made less LIOs it producess better.

September 11, 2008 5:23 AM
 

Alexander Kuznetsov said:

Joe,

Clearly CPU usage strongly depends on isolation level:

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/07/12/cpu-overhead-for-higher-isolation-levels.aspx

Have you researched if the optimizer's choice depends on isolation level?

September 11, 2008 9:53 AM
 

jchang said:

what idea world! if you can go to sys.dm_exec_query_stats for LIO, why is it so hard to aggregate total_worker_time??? So why the stubborn persistent clinging to the less reliable indicator?

I will talk about hash vs loop join in a later post. Per my earlier post, the 1-1.5% idea is another peeve. The SQL optimizer does not use an x% rule, so we should not tell people that it does. The simplified version of the rule that the optimizer uses is: each incremental sequential IO page cost 0.00074074 sec or 1350/sec which works out to 10.5MB/sec. Each "random" IO in a key lookup costs 0.003125 sec or 320 IOPS, with a method for estimating how many IOs are required per row. For a large number of rows, its usually 90+%, so essentially the index seek + key lookup versus scan decision is made based on the 1350 : 320 ratio (4.2:1), which the rate of scan pages per sec versus key lookups per sec.

September 11, 2008 10:02 AM
 

jchang said:

alex, for some reason i thought it was linchi's post that talked about this. Very good post anyways. Several years ago, I published performance versus lock level, NOLOCK, default, PAGLOCK and TABLOCK. My NOLOCK to default results are more or less in the range of your dirty versus RC. As far i know, the optimizer does not consider isolation level. The purpose of the optimizer is to determine the best table access strategy. It is not meant to reflect other costs that do not affect the index/scan/join order choice.

Back to your post. What I found was lock level assesses a specific cost per row, irrespective of the operation. In your query, the plan is a clustered index (range) seek?, which has relatively low cost per additional row, hence the lock/isolation level has a large impact. in a more expensive operation, key lookup, loop join, the lock level has slightly less relative impact (but the time differentials will be about the same)

September 11, 2008 10:29 AM
 

Michael Zilberstein said:

> if you can go to sys.dm_exec_query_stats for LIO, why is it so hard to aggregate total_worker_time?

Because poorly written heavy queries tend to run in parallel! So total_worker_time is usable only on single-core paleolithic artifacts and on instances where for some reason maxdop is limited to one on instance or query level.

September 11, 2008 11:06 AM
 

jchang said:

ok, i missed that you mentioned it in your post. so we are stuck as far a dm_exec_query_stats goes. I am ok with using high LIO or elapsed time with low cpu to identify parallel plans. In SQL 2008, I noticed that several parallel plans reported 1000 for worker time. Not sure we can rely on this. I do not think you should ignore worker time. Use high LIO or duration together with low worker time to identify parallel plans, but use worker time to rank non-parallel plans. it should not be ignored

September 11, 2008 11:54 AM
 

Adam Machanic said:

No reason to play guessing games to identify parallel plans...

select *

from sys.dm_exec_query_stats c

cross apply sys.dm_exec_query_plan(c.plan_handle) p

where

p.query_plan.exist

(

'declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan";

/ShowPlanXML/BatchSequence/Batch/Statements//RelOp/@PhysicalOp[. = "Parallelism"]'

) = 1

September 11, 2008 12:55 PM
 

jchang said:

my concern is that this is not cheap query to run (as is) if there are very many query plans, i have seen > 100K

perhaps a nested query first finding the very high LIO as Michael proposed, but with unusually low cpu, then just check those,

in SQL 2008, worker time = 1000 might indicate a parallel plan suspect, I will try a couple of queries, unless Adam gets to it first

September 11, 2008 1:33 PM
 

Adam Machanic said:

I don't have access at the moment to a SQL 2008 instance that can do parallel queries, and I don't want to reconfigure my VMs to fake it since I'm doing some work.  But I'm looking at a 2005 instance and worker times are all over the place for the plans returned by that query.

By the way, you're certainly correct that it's not a cheap query; the instance I ran it on has over 67,000 plans in the cache at the moment, and I accidentally left the query running and walked away for a while.  I came back 42 minutes later and found the thing still spinning, having returned only 54 rows... Granted, this is a somewhat underpowered server, but still.  Use with caution!

September 11, 2008 1:42 PM
 

Anon said:

What rubbish... Considering how cheap CPUs are...

September 15, 2008 10:07 PM
 

jchang said:

The fact the performance topics are immensely popular at conferences, in papers, and blogs indicates CPU power is not cheap enough to consider performance analysis rubbish

It is really great how powerful systems are today, a small app really does not need to worry about performance. Good design is still important if you want correct functionality and coding transparency. If you do not want to worry about CPU, small apps is a good field to practice.

September 16, 2008 5:21 PM
 

Adam Machanic said:

Sure, "Anon", CPUs are plenty cheap and solve all of our problems automatically.  Tell that to my recent customer that dropped half a million dollars into a gigantic server and discovered that its application still wouldn't perform well even running with all of that hardware.  How do you think that I fixed the problem?  Simple: I used all of this rubbish and analyzed why the performance was so bad.

September 16, 2008 10:07 PM
 

Logical I/Os - Not a useful performance metric? said:

September 23, 2008 11:18 AM
 

DonRWatters said:

Intersting.  At the last Pacific Northwest SQL Server Users Group, Kalen talked about reviewing Logical I/O of long running queries as a measure for determining if using an Index Seek or just doing a Table Scan was better for overall performance.  She showed cases where a Table Scan was better than an Index Seek, and the only way to see it was by looking at Logical I/O.  By cutting the Logical I/O by using a Table Scan, the performance was better.  I'm sure you're not advocating NOT looking at Logical I/O anymore, but just in case someone misses the message, it still HAS to be considered as part of the performance equation...just not the whole thing.

September 24, 2008 8:09 PM
 

jchang said:

How can logical IO must be any part of the performance equation if no one, NO ONE, can say what it means? Consider the pre-euro days, you are traveling throughout europe, every time you buy something, you increment a counter for each of the local currency you spent. 5L in england, 9 francs in france, 8 marks in Germany, 1000 lire in Italy. At the end of your trip, what does your counter tell you? how meaningful is it? unless you know the actual execution plan and rows that made up your LIO, you don't know what it means? And if you did have the components, you don't need LIO. If LIO was so meaningful, why doesn't the SQL Optimizer use it? it doesn't, because it has no real consistent quantitative value.

On index seek + key lookup versus table scan, for better performance, you need to specify CPU or duration. In any case LIO doesn't tell you anything. If you are talking about when the execution plan switches from IS+KL to Scan, it occurs around 1350:320 ratio (4.2 to 1, or 10.5MB/sec to 320 IOPS) used by the optimizer. If your table data size is 8GB, ie, 1M leaf level pages, then around 250K or so rows, the execution plan changes from IS+KL to Scan. If your clustered index key depth just happens to be 4, then the switch just happens to ocurr around the same LIO count(order of magnitude). But if the key depth was 3 or 5, then you are way off. See my other posts on the formulas used by the SQL optimizer. If you are talking about CPU, with all data in memory, then 1 row in a key lookup might cost about the same as 1 page of a table scan depending on the rows per page, which is drasticly different from what the optimizer model. If you are going to disk, and looking at duration, then it all depends on your actual random IOP to sequential transfer rate is. For a single 15K SAS drive, assuming tightly packed data, you might get 400-800 IOPS (note this is not a true random IOP) in the key lookup (with scatter gather activated) or you could get 80-120MB/sec in the table scan, depending exactly how your data is laid out and how the SQL is written. Again this is far different than the optimizer model in the other direction than in-memory.

Yes I know many people have written about LIO as if it were a key system resource, but know one could actually say what. The truth is it is sued because it is easy to collect, which does not say it is useful. Like people who cling to guns and religion, the scientific dba needs to let go of LIO, and the db buggyman is not lurking in the closet at night.

September 25, 2008 6:15 PM

Leave a Comment

(required) 
(optional)
(required) 
Submit

About jchang

Database architecture, SQL Server engine, Performance modeling and prediction, Reverse engineering the SQL Server 2000, 2005 and 2008 Cost Based Optimizer, Microprocessors, Storage performance, NUMA system performance characteristics, mucking with the data distribution statistics histogram
Powered by Community Server (Commercial Edition), by Telligent Systems
  Privacy Statement