Latest commit

History

History
159 lines (129 loc) · 6.98 KB

File metadata and controls

159 lines (129 loc) · 6.98 KB

Benchmarks

Benchmarks have been implemented with BenchmarkDotNet.

All CacheManager instances used in the benchmarks have only one cache handle configured, either the Dictionary, System.Runtime or Redis handle.

We are using the same configuration for all benchmarks and running two jobs each, one for x86 and one for x64. Regarding the different platforms, the conclusion is obviously that x64 is always faster than the x86 platform, but x64 consumes slightly more memory of course.

BenchmarkDotNet=v0.9.1.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3328117 ticks, Resolution=300.4702 ns
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
Type=PutWithRegionSingleBenchmark Mode=Throughput Platform=X64 Jit=RyuJit LaunchCount=1 WarmupCount=2 TargetCount=100 

Add

Adding one item per run

Redis will be a lot slower in this scenario because CacheManager waits for the response to be able to return the bool value if the key has been added or not. In general, it is good to see how fast the Dictionary handle is compared to the System.Runtime one. One thing you cannot see here is that also the memory footprint of the Dictionary handle is much lower.

MethodPlatformMedianStdDevScaled
DictionaryX641.7254 us0.0511 us1.00
DictionaryX861.9563 us0.0399 us1.00
RuntimeX644.9839 us0.0778 us2.89
RuntimeX867.0324 us0.2012 us3.59
RedisX6456.6671 us1.7202 us32.84
RedisX8658.0775 us0.9517 us29.69

Adding one item per run with using region

MethodPlatformMedianStdDevScaled
DictionaryX641.8094 us0.0386 us1.00
DictionaryX862.5029 us0.1179 us1.00
RuntimeX646.6934 us0.1275 us3.70
RuntimeX869.2334 us0.1637 us3.69
RedisX6458.5355 us1.7054 us32.35
RedisX8661.0272 us1.4178 us24.38

Put

Put 1 item per run Redis is as fast as the other handles in this scenario because CacheManager uses fire and forget for those operations. For Put it doesn't matter to know if the item has been added or updated...

MethodPlatformMedianStdDevScaled
DictionaryX641.6802 us0.0235 us1.00
DictionaryX861.9445 us0.0341 us1.00
RuntimeX644.4431 us0.0651 us2.64
RuntimeX866.5231 us0.1063 us3.35
RedisX642.6869 us0.0934 us1.60
RedisX863.5490 us0.0848 us1.83

Put 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX641.7401 us0.0365 us1.00
DictionaryX862.4589 us0.1022 us1.00
RuntimeX646.1772 us0.3683 us3.55
RuntimeX869.9298 us0.5574 us4.04
RedisX643.0807 us0.0906 us1.77
RedisX863.6385 us0.2123 us1.48

Get

Get 1 item per run With Get operations we can clearly see how much faster an in-memory cache is, compared to the distributed variant. That's why it makes so much sense to use CacheManager with a first and secondary cache layer.

MethodPlatformMedianStdDevScaled
DictionaryX64169.8708 ns4.6186 ns1.00
DictionaryX86540.0879 ns8.9363 ns1.00
RuntimeX64310.4025 ns8.2928 ns1.83
RuntimeX861,030.5911 ns8.0532 ns1.91
RedisX6456,917.3537 ns2,035.4765 ns335.06
RedisX8659,519.5459 ns1,527.9613 ns110.20

Get 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX64234.9676 ns7.9172 ns1.00
DictionaryX86600.0429 ns13.7090 ns1.00
RuntimeX64497.4116 ns15.6998 ns2.12
RuntimeX861,191.6452 ns19.8484 ns1.99
RedisX6457,257.9868 ns1,705.0148 ns243.68
RedisX8661,789.3944 ns1,775.6064 ns102.97

Serializer comparison

For this, I only used the bare serializer without the cache layer overhead (e.g. Redis) which could yield wrong results. Each single performance run does 1000 iterations of serializing and deserializing the same object. Object structure was the following:

{
"L" : 1671986962,
"S" : "1625c0a0-86ce-4fd5-9047-cf2fb1d145b2",
"SList" : ["98a62a89-f3e9-49d7-93ad-a4295b21c1a1", "47a86f42-64b0-4e6d-9f18-ecb20abff2a3", "7de26dfc-57a5-4f16-b421-8999b73c9afb", "e29a8f8a-feb8-4f3f-9825-78c067215339", "5b2e1923-8a76-4f39-9366-4700c7d0d408", "febea78f-ca5e-49d6-99c9-18738e4fb36f", "7c87b429-e931-4f1a-a59a-433504c87a1c", "bf288ff7-e6c0-4df1-bfcf-677ff31cdf45", "9b7fcd6c-45ee-4584-98b6-b30d32e52f72", "2729610c-d6ce-4960-b83b-b5fd4230cc7e"],
"OList" : [{
"Id" : 1210151618,
"Val" : "6d2871c9-c5f8-44b1-bad9-4eba68683510"
}, {
"Id" : 1171177179,
"Val" : "6b12cd3f-2726-4bf9-a25c-35533de3910c"
}, {
"Id" : 1676910093,
"Val" : "66f52534-92f3-4ef4-b555-48a993a9df7a"
}, {
"Id" : 977965209,
"Val" : "80a20081-a2a5-4dcc-8d07-162f697588b4"
}, {
"Id" : 2075961031,
"Val" : "35f8710a-64e5-481d-9f18-899c65abd675"
}, {
"Id" : 328057441,
"Val" : "d17277e2-ca25-42b1-a4b4-efc00deef358"
}, {
"Id" : 2046696720,
"Val" : "4fa32d5e-f770-4d44-a55b-f6479633839c"
}, {
"Id" : 422544189,
"Val" : "de39c21e-8cb3-4f5c-bf5c-a3d228bc4c25"
}, {
"Id" : 1887998603,
"Val" : "22b00459-7820-46a6-8514-10e901810bbd"
}, {
"Id" : 852015288,
"Val" : "09cc3bd8-da23-42cb-b700-02ec461beb3f"
}
]
}

The values are randomly generated the object has one list of strings (Guids) and a list of child objects with an integer and string. Pretty simple but large enough to analyze the performance.

Results:

MethodPlatformMedianStdDevScaledScaled-SD
BinarySerializerX6462.4158 ms1.8668 ms2.200.07
JsonSerializerX6428.5521 ms0.4633 ms1.000.00
JsonGzSerializerX64102.5552 ms4.8584 ms3.600.18
ProtoBufSerializerX6411.1276 ms0.1252 ms0.390.01

As expected the protobuf serialization outperforms everything else by a huge margin! The compression overhead of the JsonGz serializer seems to be pretty large and may have some potential for optimizations...

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

History
159 lines (129 loc) · 6.98 KB

File metadata and controls

159 lines (129 loc) · 6.98 KB

Benchmarks

Benchmarks have been implemented with BenchmarkDotNet.

All CacheManager instances used in the benchmarks have only one cache handle configured, either the Dictionary, System.Runtime or Redis handle.

We are using the same configuration for all benchmarks and running two jobs each, one for x86 and one for x64. Regarding the different platforms, the conclusion is obviously that x64 is always faster than the x86 platform, but x64 consumes slightly more memory of course.

BenchmarkDotNet=v0.9.1.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3328117 ticks, Resolution=300.4702 ns
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
Type=PutWithRegionSingleBenchmark Mode=Throughput Platform=X64 Jit=RyuJit LaunchCount=1 WarmupCount=2 TargetCount=100 

Add

Adding one item per run

Redis will be a lot slower in this scenario because CacheManager waits for the response to be able to return the bool value if the key has been added or not. In general, it is good to see how fast the Dictionary handle is compared to the System.Runtime one. One thing you cannot see here is that also the memory footprint of the Dictionary handle is much lower.

MethodPlatformMedianStdDevScaled
DictionaryX641.7254 us0.0511 us1.00
DictionaryX861.9563 us0.0399 us1.00
RuntimeX644.9839 us0.0778 us2.89
RuntimeX867.0324 us0.2012 us3.59
RedisX6456.6671 us1.7202 us32.84
RedisX8658.0775 us0.9517 us29.69

Adding one item per run with using region

MethodPlatformMedianStdDevScaled
DictionaryX641.8094 us0.0386 us1.00
DictionaryX862.5029 us0.1179 us1.00
RuntimeX646.6934 us0.1275 us3.70
RuntimeX869.2334 us0.1637 us3.69
RedisX6458.5355 us1.7054 us32.35
RedisX8661.0272 us1.4178 us24.38

Put

Put 1 item per run Redis is as fast as the other handles in this scenario because CacheManager uses fire and forget for those operations. For Put it doesn't matter to know if the item has been added or updated...

MethodPlatformMedianStdDevScaled
DictionaryX641.6802 us0.0235 us1.00
DictionaryX861.9445 us0.0341 us1.00
RuntimeX644.4431 us0.0651 us2.64
RuntimeX866.5231 us0.1063 us3.35
RedisX642.6869 us0.0934 us1.60
RedisX863.5490 us0.0848 us1.83

Put 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX641.7401 us0.0365 us1.00
DictionaryX862.4589 us0.1022 us1.00
RuntimeX646.1772 us0.3683 us3.55
RuntimeX869.9298 us0.5574 us4.04
RedisX643.0807 us0.0906 us1.77
RedisX863.6385 us0.2123 us1.48

Get

Get 1 item per run With Get operations we can clearly see how much faster an in-memory cache is, compared to the distributed variant. That's why it makes so much sense to use CacheManager with a first and secondary cache layer.

MethodPlatformMedianStdDevScaled
DictionaryX64169.8708 ns4.6186 ns1.00
DictionaryX86540.0879 ns8.9363 ns1.00
RuntimeX64310.4025 ns8.2928 ns1.83
RuntimeX861,030.5911 ns8.0532 ns1.91
RedisX6456,917.3537 ns2,035.4765 ns335.06
RedisX8659,519.5459 ns1,527.9613 ns110.20

Get 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX64234.9676 ns7.9172 ns1.00
DictionaryX86600.0429 ns13.7090 ns1.00
RuntimeX64497.4116 ns15.6998 ns2.12
RuntimeX861,191.6452 ns19.8484 ns1.99
RedisX6457,257.9868 ns1,705.0148 ns243.68
RedisX8661,789.3944 ns1,775.6064 ns102.97

Serializer comparison

For this, I only used the bare serializer without the cache layer overhead (e.g. Redis) which could yield wrong results. Each single performance run does 1000 iterations of serializing and deserializing the same object. Object structure was the following:

{
"L" : 1671986962,
"S" : "1625c0a0-86ce-4fd5-9047-cf2fb1d145b2",
"SList" : ["98a62a89-f3e9-49d7-93ad-a4295b21c1a1", "47a86f42-64b0-4e6d-9f18-ecb20abff2a3", "7de26dfc-57a5-4f16-b421-8999b73c9afb", "e29a8f8a-feb8-4f3f-9825-78c067215339", "5b2e1923-8a76-4f39-9366-4700c7d0d408", "febea78f-ca5e-49d6-99c9-18738e4fb36f", "7c87b429-e931-4f1a-a59a-433504c87a1c", "bf288ff7-e6c0-4df1-bfcf-677ff31cdf45", "9b7fcd6c-45ee-4584-98b6-b30d32e52f72", "2729610c-d6ce-4960-b83b-b5fd4230cc7e"],
"OList" : [{
"Id" : 1210151618,
"Val" : "6d2871c9-c5f8-44b1-bad9-4eba68683510"
}, {
"Id" : 1171177179,
"Val" : "6b12cd3f-2726-4bf9-a25c-35533de3910c"
}, {
"Id" : 1676910093,
"Val" : "66f52534-92f3-4ef4-b555-48a993a9df7a"
}, {
"Id" : 977965209,
"Val" : "80a20081-a2a5-4dcc-8d07-162f697588b4"
}, {
"Id" : 2075961031,
"Val" : "35f8710a-64e5-481d-9f18-899c65abd675"
}, {
"Id" : 328057441,
"Val" : "d17277e2-ca25-42b1-a4b4-efc00deef358"
}, {
"Id" : 2046696720,
"Val" : "4fa32d5e-f770-4d44-a55b-f6479633839c"
}, {
"Id" : 422544189,
"Val" : "de39c21e-8cb3-4f5c-bf5c-a3d228bc4c25"
}, {
"Id" : 1887998603,
"Val" : "22b00459-7820-46a6-8514-10e901810bbd"
}, {
"Id" : 852015288,
"Val" : "09cc3bd8-da23-42cb-b700-02ec461beb3f"
}
]
}

The values are randomly generated the object has one list of strings (Guids) and a list of child objects with an integer and string. Pretty simple but large enough to analyze the performance.

Results:

MethodPlatformMedianStdDevScaledScaled-SD
BinarySerializerX6462.4158 ms1.8668 ms2.200.07
JsonSerializerX6428.5521 ms0.4633 ms1.000.00
JsonGzSerializerX64102.5552 ms4.8584 ms3.600.18
ProtoBufSerializerX6411.1276 ms0.1252 ms0.390.01

As expected the protobuf serialization outperforms everything else by a huge margin! The compression overhead of the JsonGz serializer seems to be pretty large and may have some potential for optimizations...

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
159 lines (129 loc) · 6.98 KB

File metadata and controls

159 lines (129 loc) · 6.98 KB

Benchmarks

Benchmarks have been implemented with BenchmarkDotNet.

All CacheManager instances used in the benchmarks have only one cache handle configured, either the Dictionary, System.Runtime or Redis handle.

We are using the same configuration for all benchmarks and running two jobs each, one for x86 and one for x64. Regarding the different platforms, the conclusion is obviously that x64 is always faster than the x86 platform, but x64 consumes slightly more memory of course.

BenchmarkDotNet=v0.9.1.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3328117 ticks, Resolution=300.4702 ns
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
Type=PutWithRegionSingleBenchmark Mode=Throughput Platform=X64 Jit=RyuJit LaunchCount=1 WarmupCount=2 TargetCount=100 

Add

Adding one item per run

Redis will be a lot slower in this scenario because CacheManager waits for the response to be able to return the bool value if the key has been added or not. In general, it is good to see how fast the Dictionary handle is compared to the System.Runtime one. One thing you cannot see here is that also the memory footprint of the Dictionary handle is much lower.

MethodPlatformMedianStdDevScaled
DictionaryX641.7254 us0.0511 us1.00
DictionaryX861.9563 us0.0399 us1.00
RuntimeX644.9839 us0.0778 us2.89
RuntimeX867.0324 us0.2012 us3.59
RedisX6456.6671 us1.7202 us32.84
RedisX8658.0775 us0.9517 us29.69

Adding one item per run with using region

MethodPlatformMedianStdDevScaled
DictionaryX641.8094 us0.0386 us1.00
DictionaryX862.5029 us0.1179 us1.00
RuntimeX646.6934 us0.1275 us3.70
RuntimeX869.2334 us0.1637 us3.69
RedisX6458.5355 us1.7054 us32.35
RedisX8661.0272 us1.4178 us24.38

Put

Put 1 item per run Redis is as fast as the other handles in this scenario because CacheManager uses fire and forget for those operations. For Put it doesn't matter to know if the item has been added or updated...

MethodPlatformMedianStdDevScaled
DictionaryX641.6802 us0.0235 us1.00
DictionaryX861.9445 us0.0341 us1.00
RuntimeX644.4431 us0.0651 us2.64
RuntimeX866.5231 us0.1063 us3.35
RedisX642.6869 us0.0934 us1.60
RedisX863.5490 us0.0848 us1.83

Put 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX641.7401 us0.0365 us1.00
DictionaryX862.4589 us0.1022 us1.00
RuntimeX646.1772 us0.3683 us3.55
RuntimeX869.9298 us0.5574 us4.04
RedisX643.0807 us0.0906 us1.77
RedisX863.6385 us0.2123 us1.48

Get

Get 1 item per run With Get operations we can clearly see how much faster an in-memory cache is, compared to the distributed variant. That's why it makes so much sense to use CacheManager with a first and secondary cache layer.

MethodPlatformMedianStdDevScaled
DictionaryX64169.8708 ns4.6186 ns1.00
DictionaryX86540.0879 ns8.9363 ns1.00
RuntimeX64310.4025 ns8.2928 ns1.83
RuntimeX861,030.5911 ns8.0532 ns1.91
RedisX6456,917.3537 ns2,035.4765 ns335.06
RedisX8659,519.5459 ns1,527.9613 ns110.20

Get 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX64234.9676 ns7.9172 ns1.00
DictionaryX86600.0429 ns13.7090 ns1.00
RuntimeX64497.4116 ns15.6998 ns2.12
RuntimeX861,191.6452 ns19.8484 ns1.99
RedisX6457,257.9868 ns1,705.0148 ns243.68
RedisX8661,789.3944 ns1,775.6064 ns102.97

Serializer comparison

For this, I only used the bare serializer without the cache layer overhead (e.g. Redis) which could yield wrong results. Each single performance run does 1000 iterations of serializing and deserializing the same object. Object structure was the following:

{
"L" : 1671986962,
"S" : "1625c0a0-86ce-4fd5-9047-cf2fb1d145b2",
"SList" : ["98a62a89-f3e9-49d7-93ad-a4295b21c1a1", "47a86f42-64b0-4e6d-9f18-ecb20abff2a3", "7de26dfc-57a5-4f16-b421-8999b73c9afb", "e29a8f8a-feb8-4f3f-9825-78c067215339", "5b2e1923-8a76-4f39-9366-4700c7d0d408", "febea78f-ca5e-49d6-99c9-18738e4fb36f", "7c87b429-e931-4f1a-a59a-433504c87a1c", "bf288ff7-e6c0-4df1-bfcf-677ff31cdf45", "9b7fcd6c-45ee-4584-98b6-b30d32e52f72", "2729610c-d6ce-4960-b83b-b5fd4230cc7e"],
"OList" : [{
"Id" : 1210151618,
"Val" : "6d2871c9-c5f8-44b1-bad9-4eba68683510"
}, {
"Id" : 1171177179,
"Val" : "6b12cd3f-2726-4bf9-a25c-35533de3910c"
}, {
"Id" : 1676910093,
"Val" : "66f52534-92f3-4ef4-b555-48a993a9df7a"
}, {
"Id" : 977965209,
"Val" : "80a20081-a2a5-4dcc-8d07-162f697588b4"
}, {
"Id" : 2075961031,
"Val" : "35f8710a-64e5-481d-9f18-899c65abd675"
}, {
"Id" : 328057441,
"Val" : "d17277e2-ca25-42b1-a4b4-efc00deef358"
}, {
"Id" : 2046696720,
"Val" : "4fa32d5e-f770-4d44-a55b-f6479633839c"
}, {
"Id" : 422544189,
"Val" : "de39c21e-8cb3-4f5c-bf5c-a3d228bc4c25"
}, {
"Id" : 1887998603,
"Val" : "22b00459-7820-46a6-8514-10e901810bbd"
}, {
"Id" : 852015288,
"Val" : "09cc3bd8-da23-42cb-b700-02ec461beb3f"
}
]
}

The values are randomly generated the object has one list of strings (Guids) and a list of child objects with an integer and string. Pretty simple but large enough to analyze the performance.

Results:

MethodPlatformMedianStdDevScaledScaled-SD
BinarySerializerX6462.4158 ms1.8668 ms2.200.07
JsonSerializerX6428.5521 ms0.4633 ms1.000.00
JsonGzSerializerX64102.5552 ms4.8584 ms3.600.18
ProtoBufSerializerX6411.1276 ms0.1252 ms0.390.01

As expected the protobuf serialization outperforms everything else by a huge margin! The compression overhead of the JsonGz serializer seems to be pretty large and may have some potential for optimizations...

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
159 lines (129 loc) · 6.98 KB

File metadata and controls

159 lines (129 loc) · 6.98 KB

Benchmarks

Benchmarks have been implemented with BenchmarkDotNet.

All CacheManager instances used in the benchmarks have only one cache handle configured, either the Dictionary, System.Runtime or Redis handle.

We are using the same configuration for all benchmarks and running two jobs each, one for x86 and one for x64. Regarding the different platforms, the conclusion is obviously that x64 is always faster than the x86 platform, but x64 consumes slightly more memory of course.

BenchmarkDotNet=v0.9.1.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3328117 ticks, Resolution=300.4702 ns
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
Type=PutWithRegionSingleBenchmark Mode=Throughput Platform=X64 Jit=RyuJit LaunchCount=1 WarmupCount=2 TargetCount=100 

Add

Adding one item per run

Redis will be a lot slower in this scenario because CacheManager waits for the response to be able to return the bool value if the key has been added or not. In general, it is good to see how fast the Dictionary handle is compared to the System.Runtime one. One thing you cannot see here is that also the memory footprint of the Dictionary handle is much lower.

MethodPlatformMedianStdDevScaled
DictionaryX641.7254 us0.0511 us1.00
DictionaryX861.9563 us0.0399 us1.00
RuntimeX644.9839 us0.0778 us2.89
RuntimeX867.0324 us0.2012 us3.59
RedisX6456.6671 us1.7202 us32.84
RedisX8658.0775 us0.9517 us29.69

Adding one item per run with using region

MethodPlatformMedianStdDevScaled
DictionaryX641.8094 us0.0386 us1.00
DictionaryX862.5029 us0.1179 us1.00
RuntimeX646.6934 us0.1275 us3.70
RuntimeX869.2334 us0.1637 us3.69
RedisX6458.5355 us1.7054 us32.35
RedisX8661.0272 us1.4178 us24.38

Put

Put 1 item per run Redis is as fast as the other handles in this scenario because CacheManager uses fire and forget for those operations. For Put it doesn't matter to know if the item has been added or updated...

MethodPlatformMedianStdDevScaled
DictionaryX641.6802 us0.0235 us1.00
DictionaryX861.9445 us0.0341 us1.00
RuntimeX644.4431 us0.0651 us2.64
RuntimeX866.5231 us0.1063 us3.35
RedisX642.6869 us0.0934 us1.60
RedisX863.5490 us0.0848 us1.83

Put 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX641.7401 us0.0365 us1.00
DictionaryX862.4589 us0.1022 us1.00
RuntimeX646.1772 us0.3683 us3.55
RuntimeX869.9298 us0.5574 us4.04
RedisX643.0807 us0.0906 us1.77
RedisX863.6385 us0.2123 us1.48

Get

Get 1 item per run With Get operations we can clearly see how much faster an in-memory cache is, compared to the distributed variant. That's why it makes so much sense to use CacheManager with a first and secondary cache layer.

MethodPlatformMedianStdDevScaled
DictionaryX64169.8708 ns4.6186 ns1.00
DictionaryX86540.0879 ns8.9363 ns1.00
RuntimeX64310.4025 ns8.2928 ns1.83
RuntimeX861,030.5911 ns8.0532 ns1.91
RedisX6456,917.3537 ns2,035.4765 ns335.06
RedisX8659,519.5459 ns1,527.9613 ns110.20

Get 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX64234.9676 ns7.9172 ns1.00
DictionaryX86600.0429 ns13.7090 ns1.00
RuntimeX64497.4116 ns15.6998 ns2.12
RuntimeX861,191.6452 ns19.8484 ns1.99
RedisX6457,257.9868 ns1,705.0148 ns243.68
RedisX8661,789.3944 ns1,775.6064 ns102.97

Serializer comparison

For this, I only used the bare serializer without the cache layer overhead (e.g. Redis) which could yield wrong results. Each single performance run does 1000 iterations of serializing and deserializing the same object. Object structure was the following:

{
"L" : 1671986962,
"S" : "1625c0a0-86ce-4fd5-9047-cf2fb1d145b2",
"SList" : ["98a62a89-f3e9-49d7-93ad-a4295b21c1a1", "47a86f42-64b0-4e6d-9f18-ecb20abff2a3", "7de26dfc-57a5-4f16-b421-8999b73c9afb", "e29a8f8a-feb8-4f3f-9825-78c067215339", "5b2e1923-8a76-4f39-9366-4700c7d0d408", "febea78f-ca5e-49d6-99c9-18738e4fb36f", "7c87b429-e931-4f1a-a59a-433504c87a1c", "bf288ff7-e6c0-4df1-bfcf-677ff31cdf45", "9b7fcd6c-45ee-4584-98b6-b30d32e52f72", "2729610c-d6ce-4960-b83b-b5fd4230cc7e"],
"OList" : [{
"Id" : 1210151618,
"Val" : "6d2871c9-c5f8-44b1-bad9-4eba68683510"
}, {
"Id" : 1171177179,
"Val" : "6b12cd3f-2726-4bf9-a25c-35533de3910c"
}, {
"Id" : 1676910093,
"Val" : "66f52534-92f3-4ef4-b555-48a993a9df7a"
}, {
"Id" : 977965209,
"Val" : "80a20081-a2a5-4dcc-8d07-162f697588b4"
}, {
"Id" : 2075961031,
"Val" : "35f8710a-64e5-481d-9f18-899c65abd675"
}, {
"Id" : 328057441,
"Val" : "d17277e2-ca25-42b1-a4b4-efc00deef358"
}, {
"Id" : 2046696720,
"Val" : "4fa32d5e-f770-4d44-a55b-f6479633839c"
}, {
"Id" : 422544189,
"Val" : "de39c21e-8cb3-4f5c-bf5c-a3d228bc4c25"
}, {
"Id" : 1887998603,
"Val" : "22b00459-7820-46a6-8514-10e901810bbd"
}, {
"Id" : 852015288,
"Val" : "09cc3bd8-da23-42cb-b700-02ec461beb3f"
}
]
}

The values are randomly generated the object has one list of strings (Guids) and a list of child objects with an integer and string. Pretty simple but large enough to analyze the performance.

Results:

MethodPlatformMedianStdDevScaledScaled-SD
BinarySerializerX6462.4158 ms1.8668 ms2.200.07
JsonSerializerX6428.5521 ms0.4633 ms1.000.00
JsonGzSerializerX64102.5552 ms4.8584 ms3.600.18
ProtoBufSerializerX6411.1276 ms0.1252 ms0.390.01

As expected the protobuf serialization outperforms everything else by a huge margin! The compression overhead of the JsonGz serializer seems to be pretty large and may have some potential for optimizations...

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

History
159 lines (129 loc) · 6.98 KB

File metadata and controls

159 lines (129 loc) · 6.98 KB

Benchmarks

Benchmarks have been implemented with BenchmarkDotNet.

All CacheManager instances used in the benchmarks have only one cache handle configured, either the Dictionary, System.Runtime or Redis handle.

We are using the same configuration for all benchmarks and running two jobs each, one for x86 and one for x64. Regarding the different platforms, the conclusion is obviously that x64 is always faster than the x86 platform, but x64 consumes slightly more memory of course.

BenchmarkDotNet=v0.9.1.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3328117 ticks, Resolution=300.4702 ns
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
Type=PutWithRegionSingleBenchmark Mode=Throughput Platform=X64 Jit=RyuJit LaunchCount=1 WarmupCount=2 TargetCount=100 

Add

Adding one item per run

Redis will be a lot slower in this scenario because CacheManager waits for the response to be able to return the bool value if the key has been added or not. In general, it is good to see how fast the Dictionary handle is compared to the System.Runtime one. One thing you cannot see here is that also the memory footprint of the Dictionary handle is much lower.

MethodPlatformMedianStdDevScaled
DictionaryX641.7254 us0.0511 us1.00
DictionaryX861.9563 us0.0399 us1.00
RuntimeX644.9839 us0.0778 us2.89
RuntimeX867.0324 us0.2012 us3.59
RedisX6456.6671 us1.7202 us32.84
RedisX8658.0775 us0.9517 us29.69

Adding one item per run with using region

MethodPlatformMedianStdDevScaled
DictionaryX641.8094 us0.0386 us1.00
DictionaryX862.5029 us0.1179 us1.00
RuntimeX646.6934 us0.1275 us3.70
RuntimeX869.2334 us0.1637 us3.69
RedisX6458.5355 us1.7054 us32.35
RedisX8661.0272 us1.4178 us24.38

Put

Put 1 item per run Redis is as fast as the other handles in this scenario because CacheManager uses fire and forget for those operations. For Put it doesn't matter to know if the item has been added or updated...

MethodPlatformMedianStdDevScaled
DictionaryX641.6802 us0.0235 us1.00
DictionaryX861.9445 us0.0341 us1.00
RuntimeX644.4431 us0.0651 us2.64
RuntimeX866.5231 us0.1063 us3.35
RedisX642.6869 us0.0934 us1.60
RedisX863.5490 us0.0848 us1.83

Put 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX641.7401 us0.0365 us1.00
DictionaryX862.4589 us0.1022 us1.00
RuntimeX646.1772 us0.3683 us3.55
RuntimeX869.9298 us0.5574 us4.04
RedisX643.0807 us0.0906 us1.77
RedisX863.6385 us0.2123 us1.48

Get

Get 1 item per run With Get operations we can clearly see how much faster an in-memory cache is, compared to the distributed variant. That's why it makes so much sense to use CacheManager with a first and secondary cache layer.

MethodPlatformMedianStdDevScaled
DictionaryX64169.8708 ns4.6186 ns1.00
DictionaryX86540.0879 ns8.9363 ns1.00
RuntimeX64310.4025 ns8.2928 ns1.83
RuntimeX861,030.5911 ns8.0532 ns1.91
RedisX6456,917.3537 ns2,035.4765 ns335.06
RedisX8659,519.5459 ns1,527.9613 ns110.20

Get 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX64234.9676 ns7.9172 ns1.00
DictionaryX86600.0429 ns13.7090 ns1.00
RuntimeX64497.4116 ns15.6998 ns2.12
RuntimeX861,191.6452 ns19.8484 ns1.99
RedisX6457,257.9868 ns1,705.0148 ns243.68
RedisX8661,789.3944 ns1,775.6064 ns102.97

Serializer comparison

For this, I only used the bare serializer without the cache layer overhead (e.g. Redis) which could yield wrong results. Each single performance run does 1000 iterations of serializing and deserializing the same object. Object structure was the following:

{
"L" : 1671986962,
"S" : "1625c0a0-86ce-4fd5-9047-cf2fb1d145b2",
"SList" : ["98a62a89-f3e9-49d7-93ad-a4295b21c1a1", "47a86f42-64b0-4e6d-9f18-ecb20abff2a3", "7de26dfc-57a5-4f16-b421-8999b73c9afb", "e29a8f8a-feb8-4f3f-9825-78c067215339", "5b2e1923-8a76-4f39-9366-4700c7d0d408", "febea78f-ca5e-49d6-99c9-18738e4fb36f", "7c87b429-e931-4f1a-a59a-433504c87a1c", "bf288ff7-e6c0-4df1-bfcf-677ff31cdf45", "9b7fcd6c-45ee-4584-98b6-b30d32e52f72", "2729610c-d6ce-4960-b83b-b5fd4230cc7e"],
"OList" : [{
"Id" : 1210151618,
"Val" : "6d2871c9-c5f8-44b1-bad9-4eba68683510"
}, {
"Id" : 1171177179,
"Val" : "6b12cd3f-2726-4bf9-a25c-35533de3910c"
}, {
"Id" : 1676910093,
"Val" : "66f52534-92f3-4ef4-b555-48a993a9df7a"
}, {
"Id" : 977965209,
"Val" : "80a20081-a2a5-4dcc-8d07-162f697588b4"
}, {
"Id" : 2075961031,
"Val" : "35f8710a-64e5-481d-9f18-899c65abd675"
}, {
"Id" : 328057441,
"Val" : "d17277e2-ca25-42b1-a4b4-efc00deef358"
}, {
"Id" : 2046696720,
"Val" : "4fa32d5e-f770-4d44-a55b-f6479633839c"
}, {
"Id" : 422544189,
"Val" : "de39c21e-8cb3-4f5c-bf5c-a3d228bc4c25"
}, {
"Id" : 1887998603,
"Val" : "22b00459-7820-46a6-8514-10e901810bbd"
}, {
"Id" : 852015288,
"Val" : "09cc3bd8-da23-42cb-b700-02ec461beb3f"
}
]
}

The values are randomly generated the object has one list of strings (Guids) and a list of child objects with an integer and string. Pretty simple but large enough to analyze the performance.

Results:

MethodPlatformMedianStdDevScaledScaled-SD
BinarySerializerX6462.4158 ms1.8668 ms2.200.07
JsonSerializerX6428.5521 ms0.4633 ms1.000.00
JsonGzSerializerX64102.5552 ms4.8584 ms3.600.18
ProtoBufSerializerX6411.1276 ms0.1252 ms0.390.01

As expected the protobuf serialization outperforms everything else by a huge margin! The compression overhead of the JsonGz serializer seems to be pretty large and may have some potential for optimizations...

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
159 lines (129 loc) · 6.98 KB

File metadata and controls

159 lines (129 loc) · 6.98 KB

Benchmarks

Benchmarks have been implemented with BenchmarkDotNet.

All CacheManager instances used in the benchmarks have only one cache handle configured, either the Dictionary, System.Runtime or Redis handle.

We are using the same configuration for all benchmarks and running two jobs each, one for x86 and one for x64. Regarding the different platforms, the conclusion is obviously that x64 is always faster than the x86 platform, but x64 consumes slightly more memory of course.

BenchmarkDotNet=v0.9.1.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3328117 ticks, Resolution=300.4702 ns
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
Type=PutWithRegionSingleBenchmark Mode=Throughput Platform=X64 Jit=RyuJit LaunchCount=1 WarmupCount=2 TargetCount=100 

Add

Adding one item per run

Redis will be a lot slower in this scenario because CacheManager waits for the response to be able to return the bool value if the key has been added or not. In general, it is good to see how fast the Dictionary handle is compared to the System.Runtime one. One thing you cannot see here is that also the memory footprint of the Dictionary handle is much lower.

MethodPlatformMedianStdDevScaled
DictionaryX641.7254 us0.0511 us1.00
DictionaryX861.9563 us0.0399 us1.00
RuntimeX644.9839 us0.0778 us2.89
RuntimeX867.0324 us0.2012 us3.59
RedisX6456.6671 us1.7202 us32.84
RedisX8658.0775 us0.9517 us29.69

Adding one item per run with using region

MethodPlatformMedianStdDevScaled
DictionaryX641.8094 us0.0386 us1.00
DictionaryX862.5029 us0.1179 us1.00
RuntimeX646.6934 us0.1275 us3.70
RuntimeX869.2334 us0.1637 us3.69
RedisX6458.5355 us1.7054 us32.35
RedisX8661.0272 us1.4178 us24.38

Put

Put 1 item per run Redis is as fast as the other handles in this scenario because CacheManager uses fire and forget for those operations. For Put it doesn't matter to know if the item has been added or updated...

MethodPlatformMedianStdDevScaled
DictionaryX641.6802 us0.0235 us1.00
DictionaryX861.9445 us0.0341 us1.00
RuntimeX644.4431 us0.0651 us2.64
RuntimeX866.5231 us0.1063 us3.35
RedisX642.6869 us0.0934 us1.60
RedisX863.5490 us0.0848 us1.83

Put 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX641.7401 us0.0365 us1.00
DictionaryX862.4589 us0.1022 us1.00
RuntimeX646.1772 us0.3683 us3.55
RuntimeX869.9298 us0.5574 us4.04
RedisX643.0807 us0.0906 us1.77
RedisX863.6385 us0.2123 us1.48

Get

Get 1 item per run With Get operations we can clearly see how much faster an in-memory cache is, compared to the distributed variant. That's why it makes so much sense to use CacheManager with a first and secondary cache layer.

MethodPlatformMedianStdDevScaled
DictionaryX64169.8708 ns4.6186 ns1.00
DictionaryX86540.0879 ns8.9363 ns1.00
RuntimeX64310.4025 ns8.2928 ns1.83
RuntimeX861,030.5911 ns8.0532 ns1.91
RedisX6456,917.3537 ns2,035.4765 ns335.06
RedisX8659,519.5459 ns1,527.9613 ns110.20

Get 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX64234.9676 ns7.9172 ns1.00
DictionaryX86600.0429 ns13.7090 ns1.00
RuntimeX64497.4116 ns15.6998 ns2.12
RuntimeX861,191.6452 ns19.8484 ns1.99
RedisX6457,257.9868 ns1,705.0148 ns243.68
RedisX8661,789.3944 ns1,775.6064 ns102.97

Serializer comparison

For this, I only used the bare serializer without the cache layer overhead (e.g. Redis) which could yield wrong results. Each single performance run does 1000 iterations of serializing and deserializing the same object. Object structure was the following:

{
"L" : 1671986962,
"S" : "1625c0a0-86ce-4fd5-9047-cf2fb1d145b2",
"SList" : ["98a62a89-f3e9-49d7-93ad-a4295b21c1a1", "47a86f42-64b0-4e6d-9f18-ecb20abff2a3", "7de26dfc-57a5-4f16-b421-8999b73c9afb", "e29a8f8a-feb8-4f3f-9825-78c067215339", "5b2e1923-8a76-4f39-9366-4700c7d0d408", "febea78f-ca5e-49d6-99c9-18738e4fb36f", "7c87b429-e931-4f1a-a59a-433504c87a1c", "bf288ff7-e6c0-4df1-bfcf-677ff31cdf45", "9b7fcd6c-45ee-4584-98b6-b30d32e52f72", "2729610c-d6ce-4960-b83b-b5fd4230cc7e"],
"OList" : [{
"Id" : 1210151618,
"Val" : "6d2871c9-c5f8-44b1-bad9-4eba68683510"
}, {
"Id" : 1171177179,
"Val" : "6b12cd3f-2726-4bf9-a25c-35533de3910c"
}, {
"Id" : 1676910093,
"Val" : "66f52534-92f3-4ef4-b555-48a993a9df7a"
}, {
"Id" : 977965209,
"Val" : "80a20081-a2a5-4dcc-8d07-162f697588b4"
}, {
"Id" : 2075961031,
"Val" : "35f8710a-64e5-481d-9f18-899c65abd675"
}, {
"Id" : 328057441,
"Val" : "d17277e2-ca25-42b1-a4b4-efc00deef358"
}, {
"Id" : 2046696720,
"Val" : "4fa32d5e-f770-4d44-a55b-f6479633839c"
}, {
"Id" : 422544189,
"Val" : "de39c21e-8cb3-4f5c-bf5c-a3d228bc4c25"
}, {
"Id" : 1887998603,
"Val" : "22b00459-7820-46a6-8514-10e901810bbd"
}, {
"Id" : 852015288,
"Val" : "09cc3bd8-da23-42cb-b700-02ec461beb3f"
}
]
}

The values are randomly generated the object has one list of strings (Guids) and a list of child objects with an integer and string. Pretty simple but large enough to analyze the performance.

Results:

MethodPlatformMedianStdDevScaledScaled-SD
BinarySerializerX6462.4158 ms1.8668 ms2.200.07
JsonSerializerX6428.5521 ms0.4633 ms1.000.00
JsonGzSerializerX64102.5552 ms4.8584 ms3.600.18
ProtoBufSerializerX6411.1276 ms0.1252 ms0.390.01

As expected the protobuf serialization outperforms everything else by a huge margin! The compression overhead of the JsonGz serializer seems to be pretty large and may have some potential for optimizations...

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

History
159 lines (129 loc) · 6.98 KB

File metadata and controls

159 lines (129 loc) · 6.98 KB

Benchmarks

Benchmarks have been implemented with BenchmarkDotNet.

All CacheManager instances used in the benchmarks have only one cache handle configured, either the Dictionary, System.Runtime or Redis handle.

We are using the same configuration for all benchmarks and running two jobs each, one for x86 and one for x64. Regarding the different platforms, the conclusion is obviously that x64 is always faster than the x86 platform, but x64 consumes slightly more memory of course.

BenchmarkDotNet=v0.9.1.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3328117 ticks, Resolution=300.4702 ns
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
Type=PutWithRegionSingleBenchmark Mode=Throughput Platform=X64 Jit=RyuJit LaunchCount=1 WarmupCount=2 TargetCount=100 

Add

Adding one item per run

Redis will be a lot slower in this scenario because CacheManager waits for the response to be able to return the bool value if the key has been added or not. In general, it is good to see how fast the Dictionary handle is compared to the System.Runtime one. One thing you cannot see here is that also the memory footprint of the Dictionary handle is much lower.

MethodPlatformMedianStdDevScaled
DictionaryX641.7254 us0.0511 us1.00
DictionaryX861.9563 us0.0399 us1.00
RuntimeX644.9839 us0.0778 us2.89
RuntimeX867.0324 us0.2012 us3.59
RedisX6456.6671 us1.7202 us32.84
RedisX8658.0775 us0.9517 us29.69

Adding one item per run with using region

MethodPlatformMedianStdDevScaled
DictionaryX641.8094 us0.0386 us1.00
DictionaryX862.5029 us0.1179 us1.00
RuntimeX646.6934 us0.1275 us3.70
RuntimeX869.2334 us0.1637 us3.69
RedisX6458.5355 us1.7054 us32.35
RedisX8661.0272 us1.4178 us24.38

Put

Put 1 item per run Redis is as fast as the other handles in this scenario because CacheManager uses fire and forget for those operations. For Put it doesn't matter to know if the item has been added or updated...

MethodPlatformMedianStdDevScaled
DictionaryX641.6802 us0.0235 us1.00
DictionaryX861.9445 us0.0341 us1.00
RuntimeX644.4431 us0.0651 us2.64
RuntimeX866.5231 us0.1063 us3.35
RedisX642.6869 us0.0934 us1.60
RedisX863.5490 us0.0848 us1.83

Put 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX641.7401 us0.0365 us1.00
DictionaryX862.4589 us0.1022 us1.00
RuntimeX646.1772 us0.3683 us3.55
RuntimeX869.9298 us0.5574 us4.04
RedisX643.0807 us0.0906 us1.77
RedisX863.6385 us0.2123 us1.48

Get

Get 1 item per run With Get operations we can clearly see how much faster an in-memory cache is, compared to the distributed variant. That's why it makes so much sense to use CacheManager with a first and secondary cache layer.

MethodPlatformMedianStdDevScaled
DictionaryX64169.8708 ns4.6186 ns1.00
DictionaryX86540.0879 ns8.9363 ns1.00
RuntimeX64310.4025 ns8.2928 ns1.83
RuntimeX861,030.5911 ns8.0532 ns1.91
RedisX6456,917.3537 ns2,035.4765 ns335.06
RedisX8659,519.5459 ns1,527.9613 ns110.20

Get 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX64234.9676 ns7.9172 ns1.00
DictionaryX86600.0429 ns13.7090 ns1.00
RuntimeX64497.4116 ns15.6998 ns2.12
RuntimeX861,191.6452 ns19.8484 ns1.99
RedisX6457,257.9868 ns1,705.0148 ns243.68
RedisX8661,789.3944 ns1,775.6064 ns102.97

Serializer comparison

For this, I only used the bare serializer without the cache layer overhead (e.g. Redis) which could yield wrong results. Each single performance run does 1000 iterations of serializing and deserializing the same object. Object structure was the following:

{
"L" : 1671986962,
"S" : "1625c0a0-86ce-4fd5-9047-cf2fb1d145b2",
"SList" : ["98a62a89-f3e9-49d7-93ad-a4295b21c1a1", "47a86f42-64b0-4e6d-9f18-ecb20abff2a3", "7de26dfc-57a5-4f16-b421-8999b73c9afb", "e29a8f8a-feb8-4f3f-9825-78c067215339", "5b2e1923-8a76-4f39-9366-4700c7d0d408", "febea78f-ca5e-49d6-99c9-18738e4fb36f", "7c87b429-e931-4f1a-a59a-433504c87a1c", "bf288ff7-e6c0-4df1-bfcf-677ff31cdf45", "9b7fcd6c-45ee-4584-98b6-b30d32e52f72", "2729610c-d6ce-4960-b83b-b5fd4230cc7e"],
"OList" : [{
"Id" : 1210151618,
"Val" : "6d2871c9-c5f8-44b1-bad9-4eba68683510"
}, {
"Id" : 1171177179,
"Val" : "6b12cd3f-2726-4bf9-a25c-35533de3910c"
}, {
"Id" : 1676910093,
"Val" : "66f52534-92f3-4ef4-b555-48a993a9df7a"
}, {
"Id" : 977965209,
"Val" : "80a20081-a2a5-4dcc-8d07-162f697588b4"
}, {
"Id" : 2075961031,
"Val" : "35f8710a-64e5-481d-9f18-899c65abd675"
}, {
"Id" : 328057441,
"Val" : "d17277e2-ca25-42b1-a4b4-efc00deef358"
}, {
"Id" : 2046696720,
"Val" : "4fa32d5e-f770-4d44-a55b-f6479633839c"
}, {
"Id" : 422544189,
"Val" : "de39c21e-8cb3-4f5c-bf5c-a3d228bc4c25"
}, {
"Id" : 1887998603,
"Val" : "22b00459-7820-46a6-8514-10e901810bbd"
}, {
"Id" : 852015288,
"Val" : "09cc3bd8-da23-42cb-b700-02ec461beb3f"
}
]
}

The values are randomly generated the object has one list of strings (Guids) and a list of child objects with an integer and string. Pretty simple but large enough to analyze the performance.

Results:

MethodPlatformMedianStdDevScaledScaled-SD
BinarySerializerX6462.4158 ms1.8668 ms2.200.07
JsonSerializerX6428.5521 ms0.4633 ms1.000.00
JsonGzSerializerX64102.5552 ms4.8584 ms3.600.18
ProtoBufSerializerX6411.1276 ms0.1252 ms0.390.01

As expected the protobuf serialization outperforms everything else by a huge margin! The compression overhead of the JsonGz serializer seems to be pretty large and may have some potential for optimizations...

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

History
159 lines (129 loc) · 6.98 KB

File metadata and controls

159 lines (129 loc) · 6.98 KB

Benchmarks

Benchmarks have been implemented with BenchmarkDotNet.

All CacheManager instances used in the benchmarks have only one cache handle configured, either the Dictionary, System.Runtime or Redis handle.

We are using the same configuration for all benchmarks and running two jobs each, one for x86 and one for x64. Regarding the different platforms, the conclusion is obviously that x64 is always faster than the x86 platform, but x64 consumes slightly more memory of course.

BenchmarkDotNet=v0.9.1.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3328117 ticks, Resolution=300.4702 ns
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
Type=PutWithRegionSingleBenchmark Mode=Throughput Platform=X64 Jit=RyuJit LaunchCount=1 WarmupCount=2 TargetCount=100 

Add

Adding one item per run

Redis will be a lot slower in this scenario because CacheManager waits for the response to be able to return the bool value if the key has been added or not. In general, it is good to see how fast the Dictionary handle is compared to the System.Runtime one. One thing you cannot see here is that also the memory footprint of the Dictionary handle is much lower.

MethodPlatformMedianStdDevScaled
DictionaryX641.7254 us0.0511 us1.00
DictionaryX861.9563 us0.0399 us1.00
RuntimeX644.9839 us0.0778 us2.89
RuntimeX867.0324 us0.2012 us3.59
RedisX6456.6671 us1.7202 us32.84
RedisX8658.0775 us0.9517 us29.69

Adding one item per run with using region

MethodPlatformMedianStdDevScaled
DictionaryX641.8094 us0.0386 us1.00
DictionaryX862.5029 us0.1179 us1.00
RuntimeX646.6934 us0.1275 us3.70
RuntimeX869.2334 us0.1637 us3.69
RedisX6458.5355 us1.7054 us32.35
RedisX8661.0272 us1.4178 us24.38

Put

Put 1 item per run Redis is as fast as the other handles in this scenario because CacheManager uses fire and forget for those operations. For Put it doesn't matter to know if the item has been added or updated...

MethodPlatformMedianStdDevScaled
DictionaryX641.6802 us0.0235 us1.00
DictionaryX861.9445 us0.0341 us1.00
RuntimeX644.4431 us0.0651 us2.64
RuntimeX866.5231 us0.1063 us3.35
RedisX642.6869 us0.0934 us1.60
RedisX863.5490 us0.0848 us1.83

Put 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX641.7401 us0.0365 us1.00
DictionaryX862.4589 us0.1022 us1.00
RuntimeX646.1772 us0.3683 us3.55
RuntimeX869.9298 us0.5574 us4.04
RedisX643.0807 us0.0906 us1.77
RedisX863.6385 us0.2123 us1.48

Get

Get 1 item per run With Get operations we can clearly see how much faster an in-memory cache is, compared to the distributed variant. That's why it makes so much sense to use CacheManager with a first and secondary cache layer.

MethodPlatformMedianStdDevScaled
DictionaryX64169.8708 ns4.6186 ns1.00
DictionaryX86540.0879 ns8.9363 ns1.00
RuntimeX64310.4025 ns8.2928 ns1.83
RuntimeX861,030.5911 ns8.0532 ns1.91
RedisX6456,917.3537 ns2,035.4765 ns335.06
RedisX8659,519.5459 ns1,527.9613 ns110.20

Get 1 item per run with region

MethodPlatformMedianStdDevScaled
DictionaryX64234.9676 ns7.9172 ns1.00
DictionaryX86600.0429 ns13.7090 ns1.00
RuntimeX64497.4116 ns15.6998 ns2.12
RuntimeX861,191.6452 ns19.8484 ns1.99
RedisX6457,257.9868 ns1,705.0148 ns243.68
RedisX8661,789.3944 ns1,775.6064 ns102.97

Serializer comparison

For this, I only used the bare serializer without the cache layer overhead (e.g. Redis) which could yield wrong results. Each single performance run does 1000 iterations of serializing and deserializing the same object. Object structure was the following:

{
"L" : 1671986962,
"S" : "1625c0a0-86ce-4fd5-9047-cf2fb1d145b2",
"SList" : ["98a62a89-f3e9-49d7-93ad-a4295b21c1a1", "47a86f42-64b0-4e6d-9f18-ecb20abff2a3", "7de26dfc-57a5-4f16-b421-8999b73c9afb", "e29a8f8a-feb8-4f3f-9825-78c067215339", "5b2e1923-8a76-4f39-9366-4700c7d0d408", "febea78f-ca5e-49d6-99c9-18738e4fb36f", "7c87b429-e931-4f1a-a59a-433504c87a1c", "bf288ff7-e6c0-4df1-bfcf-677ff31cdf45", "9b7fcd6c-45ee-4584-98b6-b30d32e52f72", "2729610c-d6ce-4960-b83b-b5fd4230cc7e"],
"OList" : [{
"Id" : 1210151618,
"Val" : "6d2871c9-c5f8-44b1-bad9-4eba68683510"
}, {
"Id" : 1171177179,
"Val" : "6b12cd3f-2726-4bf9-a25c-35533de3910c"
}, {
"Id" : 1676910093,
"Val" : "66f52534-92f3-4ef4-b555-48a993a9df7a"
}, {
"Id" : 977965209,
"Val" : "80a20081-a2a5-4dcc-8d07-162f697588b4"
}, {
"Id" : 2075961031,
"Val" : "35f8710a-64e5-481d-9f18-899c65abd675"
}, {
"Id" : 328057441,
"Val" : "d17277e2-ca25-42b1-a4b4-efc00deef358"
}, {
"Id" : 2046696720,
"Val" : "4fa32d5e-f770-4d44-a55b-f6479633839c"
}, {
"Id" : 422544189,
"Val" : "de39c21e-8cb3-4f5c-bf5c-a3d228bc4c25"
}, {
"Id" : 1887998603,
"Val" : "22b00459-7820-46a6-8514-10e901810bbd"
}, {
"Id" : 852015288,
"Val" : "09cc3bd8-da23-42cb-b700-02ec461beb3f"
}
]
}

The values are randomly generated the object has one list of strings (Guids) and a list of child objects with an integer and string. Pretty simple but large enough to analyze the performance.

Results:

MethodPlatformMedianStdDevScaledScaled-SD
BinarySerializerX6462.4158 ms1.8668 ms2.200.07
JsonSerializerX6428.5521 ms0.4633 ms1.000.00
JsonGzSerializerX64102.5552 ms4.8584 ms3.600.18
ProtoBufSerializerX6411.1276 ms0.1252 ms0.390.01

As expected the protobuf serialization outperforms everything else by a huge margin! The compression overhead of the JsonGz serializer seems to be pretty large and may have some potential for optimizations...