Quantcast
Channel: Why is using a table variable more than twice as fast as a #temp table in this specific case? - Database Administrators Stack Exchange
Viewing all articles
Browse latest Browse all 3

Answer by Erik Darling for Why is using a table variable more than twice as fast as a #temp table in this specific case?

$
0
0

Disco Inferno

Since this is an older question, I decided to revisit the issue on newer versions of SQL Server to see if the same performance profile still exists, or if the characteristics have changed at all.

Specifically, the addition of in-memory system tables for SQL Server 2019 seems a worthwhile occasion to re-test.

I'm using a slightly different test harness, since I ran into this issue while working on something else.

Testing, testing

Using the 2013 version of Stack Overflow, I have this index and these two procedures:

Index:

CREATE INDEX ix_whatever     ON dbo.Posts(OwnerUserId) INCLUDE(Score);GO

Temp table:

    CREATE OR ALTER PROCEDURE dbo.TempTableTest(@Id INT)    AS    BEGIN    SET NOCOUNT ON;        CREATE TABLE #t(i INT NOT NULL);        DECLARE @i INT;        INSERT #t ( i )        SELECT p.Score        FROM dbo.Posts AS p        WHERE p.OwnerUserId = @Id;        SELECT @i = AVG(t.i)        FROM #t AS t;    END;    GO 

Table variable:

    CREATE OR ALTER PROCEDURE dbo.TableVariableTest(@Id INT)    AS    BEGIN    SET NOCOUNT ON;        DECLARE @t TABLE (i INT NOT NULL);        DECLARE @i INT;        INSERT @t ( i )        SELECT p.Score        FROM dbo.Posts AS p        WHERE p.OwnerUserId = @Id;        SELECT @i = AVG(t.i)        FROM @t AS t;    END;    GO 

To prevent any potential ASYNC_NETWORK_IO waits, I'm using wrapper procedures.

CREATE PROCEDURE #TT ASSET NOCOUNT ON;    DECLARE @i INT = 1;    DECLARE @StartDate DATETIME2(7) = SYSDATETIME();    WHILE @i <= 50000        BEGIN            EXEC dbo.TempTableTest @Id = @i;            SET @i += 1;        END;    SELECT DATEDIFF(MILLISECOND, @StartDate, SYSDATETIME()) AS [ElapsedTimeMilliseconds];GOCREATE PROCEDURE #TV ASSET NOCOUNT ON;    DECLARE @i INT = 1;    DECLARE @StartDate DATETIME2(7) = SYSDATETIME();    WHILE @i <= 50000        BEGIN            EXEC dbo.TableVariableTest @Id = @i;            SET @i += 1;        END;    SELECT DATEDIFF(MILLISECOND, @StartDate, SYSDATETIME()) AS [ElapsedTimeMilliseconds];GO

SQL Server 2017

Since 2014 and 2016 are basically RELICS at this point, I'm starting my testing with 2017. Also, for brevity, I'm jumping right to profiling the code with Perfview. In real life, I looked at waits, latches, spinlocks, crazy trace flags, and other stuff.

Profiling the code is the only thing that revealed anything of interest.

Time difference:

  • Temp Table: 17891 ms
  • Table Variable: 5891 ms

Still a very clear difference, eh? But what's SQL Server hitting now?

NUTS

Looking at the top two increases in the diffed samples, we see sqlmin and sqlsqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucket are the two biggest offenders.

NUTS

Judging by the names in the call stacks, cleaning up and internally renaming temp tables seems to be the biggest time sucks in the temp table call vs. the table variable call.

Even though table variables are internally backed by temp tables, this doesn't seem to be an issue.

SET STATISTICS IO ON;DECLARE @t TABLE(id INT);SELECT * FROM @t AS t;

Table '#B98CE339'. Scan count 1

Looking through the call stacks for the table variable test doesn't show either of the main offenders at all:

NUTS

SQL Server 2019 (Vanilla)

Alright, so this is still an issue in SQL Server 2017, is anything different in 2019 out of the box?

First, to show there's nothing up my sleeve:

SELECT c.name,       c.value_in_use,       c.descriptionFROM sys.configurations AS cWHERE c.name = 'tempdb metadata memory-optimized';

NUTS

Time difference:

  • Temp table: 15765 ms
  • Table Variable: 7250 ms

Both procedures were different. The temp table call was a couple seconds faster, and the table variable call was about 1.5 seconds slower. The table variable slow down may be partially explained by table variable deferred compilation, a new optimizer choice in 2019.

Looking at the diff in Perfview, it has changed a bit -- sqlmin is no longer there -- but sqllang!TCacheStore<CacheClockAlgorithm>::GetNextUserDataInHashBucket is.

NUTS

SQL Server 2019 (In-Memory Tempdb system tables)

What about this new in memory system table thing? Hm? Sup with that?

Let's turn it on!

EXEC sys.sp_configure @configname = 'advanced',                       @configvalue = 1  RECONFIGURE;EXEC sys.sp_configure @configname = 'tempdb metadata memory-optimized',                       @configvalue = 1 RECONFIGURE;

Note that this requires a SQL Server restart to kick in, so pardon me while I reboot SQL on this lovely Friday afternoon.

Now things look different:

SELECT c.name,       c.value_in_use,       c.descriptionFROM sys.configurations AS cWHERE c.name = 'tempdb metadata memory-optimized';SELECT *,        OBJECT_NAME(object_id) AS object_name,        @@VERSION AS sql_server_versionFROM tempdb.sys.memory_optimized_tables_internal_attributes;

NUTS

Time difference:

  • Temp table: 11638 ms
  • Table variable: 7403 ms

The temp tables did about 4 seconds better! That's something.

I like something.

This time, the Perfview diff isn't very interesting. Side by side, it's interesting to note how close the times are across the board:

NUTS

One interesting point in the diff are the calls to hkengine!, which may seem obvious since hekaton-ish features are now in use.

NUTS

As far as the top two items in the diff, I can't make much of ntoskrnl!?:

NUTS

Or sqltses!CSqlSortManager_80::GetSortKey, but they're here for Smrtr Ppl™ to look at:

NUTS

Note that there is an undocumented and definitely not safe for production so please don't use it startup trace flag you can use to have additional temp table system objects (sysrowsets, sysallocunits, and sysseobjvalues) included in the in-memory feature, but it didn't make a noticeable difference in execution times in this case.

Roundup

Even in newer versions of SQL server, high frequency calls to table variables are much faster than high frequency calls to temp tables.

Though it's tempting to blame compilations, recompilations, auto stats, latches, spinlocks, caching, or other issues, the issue is clearly still around managing temp table cleanup.

It's a closer call in SQL Server 2019 with in-memory system tables enabled, but table variables still perform better when call frequency is high.

Of course, as a vaping sage once mused: "use table variables when plan choice isn't an issue".


Viewing all articles
Browse latest Browse all 3


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>