I have a Blazor Server App .Net7 with a single page with this code:
#page "/"
#using System.Diagnostics
<PageTitle>Counter</PageTitle>
<button #onclick=OnCalcClick>Count</button>
<div>Elapsed Time: #time ms</div>
<div>Elapsed Time Stopwatch: #timeHigh ms</div>
#code{
long result = 0;
double time = 0;
long timeHigh = 0;
void OnCalcClick() {
//Stopwatch s = new Stopwatch();
//s.Start();
var start = DateTime.UtcNow;
for (long i = 0; i < 1000000000; i++)
result ++;
//s.Stop();
var stop = DateTime.UtcNow;
//timeHigh = s.ElapsedMilliseconds;
time = (stop - start).TotalMicroseconds;
}
}
If I run the App in Release mode without debugging, I get the execution time about 250 ms.
If I comment using of DateTime and uncomment the Stopwatch, I get the execution time about 750 ms
If I uncomment everething, I get the same time with both measurement method and this time is about 750 ms.
I tried to do this multiple times, but results are the same.
So, it seems, Stopwatch makes the execution slower, but why?
Here's my test version of your code. I:
Dropped the loop by an order of 10.
Separated out each test
I can't see any major difference in execution times. Debug is slower, but that's expected. What am I missing from your testing?
#page "/"
#using System.Diagnostics
<PageTitle>Counter</PageTitle>
<div class="alert alert-info">
<div class="mb-2" >Elapsed Time: #time1 ms</div>
<button class="btn btn-sm btn-primary" #onclick=OnCalcClick>Count</button>
</div>
<div class="alert alert-info">
<div class="mb-2">Elapsed Time Stopwatch: #timeHigh2 ms</div>
<button class="btn btn-sm btn-primary" #onclick=OnCalcClickWithTimer>Count Timed</button>
</div>
<div class="alert alert-info">
<div>Elapsed Time: #time3 ms</div>
<div class="mb-2">Elapsed Time Stopwatch: #timeHigh3 ms</div>
<button class="btn btn-sm btn-primary" #onclick=OnCalcClickWithAll>Count Timed</button>
</div>
#code {
long result1 = 0;
double time1 = 0;
long result2 = 0;
double time2 = 0;
long timeHigh2 = 0;
long result3 = 0;
double time3 = 0;
long timeHigh3 = 0;
void OnCalcClick()
{
var start = DateTime.UtcNow;
for (long i = 0; i < 100000000; i++)
result1++;
var stop = DateTime.UtcNow;
time1 = (stop - start).TotalMilliseconds;
}
void OnCalcClickWithTimer()
{
Stopwatch s = new Stopwatch();
s.Start();
for (long i = 0; i < 100000000; i++)
result2++;
s.Stop();
timeHigh2 = s.ElapsedMilliseconds;
}
void OnCalcClickWithAll()
{
Stopwatch s = new Stopwatch();
s.Start();
var start = DateTime.UtcNow;
for (long i = 0; i < 100000000; i++)
result3++;
s.Stop();
var stop = DateTime.UtcNow;
timeHigh3 = s.ElapsedMilliseconds;
time3 = (stop - start).TotalMilliseconds;
}
}
Result in release mode:
And debug:
Related
I have only started js recently, so i hope this makes sense..
I have made a simple dynamic form which starts with a table of 4 rows. After the heading row, the remaining 3 rows have +/- symbols which are to add or delete rows as necessary.
The add row functionality is currently working, however, even after assigning the correct class to the new row, the event listener wont work for the new buttons (even thought i have re-assigned the number of buttons within that class).
After adding the row, i re-assign btnAddRows and when logged to the console it is increasing with each row added. I can't figure out why it wont get captured in the for loop?
Thanks
//select elements on DOM
let btnAddRows = document.querySelectorAll('.btnADD');
let myTable = document.querySelector('#SecProp');
let myTableRows = document.querySelector('.tableRows');
for (let i = 0; i < btnAddRows.length; i++) {
btnAddRows[i].addEventListener('click', function () {
console.log(btnAddRows.length);
btnAddRows = document.querySelectorAll('.btnADD');
const rowNum = Number(btnAddRows[i].id.slice(6));
// console.log(rowNum);
// if (rowNum === myTableRows.length - 1) {
// addTableRow(rowNum);
// } else {
addTableRow(rowNum);
// }
btnAddRows = document.querySelectorAll('.btnADD');
myTable = document.querySelector('#SecProp');
myTableRows = document.querySelector('.tableRows');
});
}
const addTableRow = function (rowNum) {
//insert row into table
const addRw = myTable.insertRow(rowNum + 1);
const newRow = myTable.rows[rowNum + 1];
newRow.className = 'tableRows';
console.log(myTable.rows[rowNum + 1], typeof myTable.rows[rowNum + 1]);
const cell1 = newRow.insertCell(0);
const cell2 = newRow.insertCell(1);
const cell3 = newRow.insertCell(2);
const cell4 = newRow.insertCell(3);
const cell5 = newRow.insertCell(4);
cell1.className = 'column1';
cell2.className = 'coordsColumn';
cell3.className = 'coordsColumn';
cell4.className = 'buttons';
cell5.className = 'buttons';
cell1.innerHTML = `<td> ${rowNum + 1}</td>`;
cell2.innerHTML = '<td ><input type = "text" name="" value = ""/><td>';
cell3.innerHTML = '<td ><input type = "text" name="" value = ""/><td>';
cell4.innerHTML = `<td ><button class="btnADD btn btn-success" id="btnADD${
rowNum + 1
}"> + </button>`;
cell5.innerHTML = `<td ><button id="btnDEL${
rowNum + 1
}" class="btnDEL btn btn-success"> -</button>`;
};
I am working on MVC 5. Using WEB API, fetch the data now it is time to apply the HTML PAGE design and CSS.
#foreach (var item in Model)
{
if (Model.First() == item)
{
///APPLY FIRST RECORD CSS:- Works FINE
}
else {
<div class="row">
<div class="col-sm-4">
</div>
</div>
}
}
In the else portion, every time it generates the new ROW for a SINGLE record. But I am interested to display record 2 3 4 in SECOND ROW. 5 6 7 Record in the THIRD ROW and so on.
If it is the first item, open a div and then put the items in it. Close the div when the number of columns is 3 or close the div if the item is at the end of the list.
The following algorithm does this for you
#{
int i = 0;
int columnCounter = 1;
bool newRow = false;
}
#foreach (var item in Model)
{
//add and open div row
if (i == 0 || newRow)
{
newRow = false;
#:<div class="row" style="border:2px solid red;">
}
<div class="col-md-4" style="padding:0;">
<div style="height:40px;background:#f6f6f6;width:100%;text-align:center;">
<span>Column #i</span>
</div>
</div>
//close div row if column count == 3 or reach of end list
if (columnCounter == 3 || i == Model.Count - 1)
{
newRow = true;
columnCounter = 1;
#:</div>
}
else
{
columnCounter = columnCounter + 1;
}
}
result:
I store 2 dates in my products table start_discounted and end_discounted this 2 columns are responsible of my discount price duration. Now when my product discount duration expires except it return back to normal price it stays on discounted price and my timer goes to minus.
Codes
my model scope
public function scopeValidDiscount($query){
return $query->where('start_discounted', '<=', Carbon::now()->toDateTimeString())
->where('end_discounted', '>=', Carbon::now()->toDateTimeString());
}
controller
public function product($slug){
$product = Product::where('slug', $slug)->firstOrFail();
$validdiscount = $product->ValidDiscount()->get();
//...
}
blade
#if($validdiscount)
show timer
#endif
PS: I need to avoid showing timer in 2 conditions, 1 discount dates are expired. 2 Product discount dates are null.
Any idea where is my mistake?
UPDATE
My timer in blade:
#if($validdiscount)
<div class="deals_timer row align-items-center">
<div class="col-md-6 deals_timer_title_container">
<div class="deals_timer_title">{{__('store.hurryup')}}</div>
<div class="deals_timer_subtitle">{{__('store.endsin')}}</div>
</div>
<div class="col-md-6 deals_timer_content ml-auto">
<div class="deals_timer_box clearfix" data-target-time="{{$end}}">
<div class="text-center deals_timer_unit">
<div id="deals_timer3_hr" class="deals_timer_hr"></div>
<span>{{__('store.hours')}}</span>
</div>
<div class="text-center deals_timer_unit">
<div id="deals_timer3_min" class="deals_timer_min"></div>
<span>{{__('store.mins')}}</span>
</div>
<div class="text-center deals_timer_unit">
<div id="deals_timer3_sec" class="deals_timer_sec"></div>
<span>{{__('store.secs')}}</span>
</div>
</div>
</div>
</div>
#endif
Please pay attention to data-target-time="{{$end}}" i think the issue might be here...
my timer script:
<script>
$(document).ready(function(){
if($('.deals_timer_box').length)
{
var timers = $('.deals_timer_box');
timers.each(function()
{
var timer = $(this);
var targetTime;
var target_date;
// Add a date to data-target-time of the .deals_timer_box
// Format: "Feb 17, 2018"
if(timer.data('target-time') !== "")
{
targetTime = timer.data('target-time');
target_date = new Date(targetTime).getTime();
}
else
{
var date = new Date();
date.setDate(date.getDate() + 2);
target_date = date.getTime();
}
// variables for time units
var days, hours, minutes, seconds;
var h = timer.find('.deals_timer_hr');
var m = timer.find('.deals_timer_min');
var s = timer.find('.deals_timer_sec');
setInterval(function ()
{
// find the amount of "seconds" between now and target
var current_date = new Date().getTime();
var seconds_left = (target_date - current_date) / 1000;
console.log(seconds_left);
// do some time calculations
days = parseInt(seconds_left / 86400);
seconds_left = seconds_left % 86400;
hours = parseInt(seconds_left / 3600);
hours = hours + days * 24;
seconds_left = seconds_left % 3600;
minutes = parseInt(seconds_left / 60);
seconds = parseInt(seconds_left % 60);
if(hours.toString().length < 2)
{
hours = "0" + hours;
}
if(minutes.toString().length < 2)
{
minutes = "0" + minutes;
}
if(seconds.toString().length < 2)
{
seconds = "0" + seconds;
}
// display results
h.text(hours);
m.text(minutes);
s.text(seconds);
}, 1000);
});
}
});
</script>
{{$end}}
This is what I have in my controller in order to get my expired date and use if in my JavaScript above:
$mytime = Carbon::now();
if(!empty($product->start_discounted && $product->end_discounted)){
$start = $product->start_discounted->format('M d, Y');
$end = $product->end_discounted->format('M d, Y');
}
then I also have my scope code:
$validdiscount = $product->ValidDiscount()->get();
now what I think is that my issue cause by: I get my expire date from
product directly $product->end_discounted while I try to avoid it while I'm using scope which
it does the same carbon thing in my model, not sure! (maybe i should get my $end by scope as well?
You're not using scopes correctly. Scopes are used on query builder, not on an already retrieved model.
For doing this, you just need a regular method:
public function validDiscount() {
return $this->start_discounted <= now()->toDateTimeString()
&& $this->end_discounted >= now()->toDateTimeString();
}
And then in your view, check it like this:
#if($product->validDiscount())
show timer
#endif
And remove the unneeded $validdiscount = $product->ValidDiscount()->get(); (where you're using a query scope on an instance of a model).
You can use javascript/jquery in front end to do it.
When you populate that page, call that js function to check that value like this $(document).ready(function(){ check_timer() })
Then trigger that method again when you timer has expired to update your view(hide promotional price, hide timer & showing actual price)
check_timer function can be like this
function check_timer(){
if(!{{ $validdiscount }}){
//hide your required div
}
}
When I looping through a nodelist obtained by querySelectorAll and add a new class for each one,it takes much less time(3ms) than which obtained by getElementsByClassName(100ms).Why?
var container = document.getElementById('box-container');
var button = document.getElementById('button');
for (var i = 0; i < 3000; i++) {
var div = document.createElement('div');
div.classList.add('box');
div.index = i;
container.appendChild(div);
}
button.addEventListener('click', function() {
var box1 = container.getElementsByClassName('box');
for (var i = 1;i < box1.length; i+=2){
box1[i].classList.toggle('gray');
};
var box2 = container.querySelectorAll('.box');
for (var i = 1;i < box2.length; i+=2){
box2[i-1].classList.toggle('gray');
};
});
The difference is the type of list you are running on.
box1 is a NodeList (a.k.a a live node list) which is updated when the DOM changes. box2 is an array, which is a non-live list - so changing the DOM doesn't affect it.
What happens when you iterate on box1 is that on every class toggle, the box1 list is updated, which causes the overhead.
Here's a test you can easily run:
var container = document.getElementById('box-container');
var button = document.getElementById('button');
for (var i = 0; i < 6000; i++) { // added 3000 more to challenge modern browsers...
var div = document.createElement('div');
div.classList.add('box');
div.index = i;
container.appendChild(div);
}
button.addEventListener('click', function () {
var box1 = container.getElementsByClassName('box');
for (var i = 1; i < box1.length; i += 2) {
box1[i].classList.toggle('gray');
}
var deadBox1 = [];
for (i = 0; i < box1.length; i++) {
deadBox1[i] = box1[i];
}
for (var i = 1; i < deadBox1.length; i += 2) {
deadBox1[i].classList.toggle('gray');
}
var box2 = container.querySelectorAll('.box');
for (i = 1; i < box2.length; i += 2) {
box2[i - 1].classList.toggle('gray');
}
});
Now run the chrome performance (or timeline) tool. You can see the diff here:
Have been looking into the performance issues with the foreach and template binding. In our single page app, we have nested foreach/templates. Below is the jsperf url, which gives the information on a plain array rendered without a foreach and the one with foreach; where the test with title "Expanded loop markup" is better than the "Nested foreach" binding.
Also observed that the corrsponding "foreach via template" tests for nested and expanded are much more time consuming than the ones without foreach via template.
jsperf url:
http://jsperf.com/knockout-nested-foreach-vs-expanded-markup/2
Would appreciate your help on the performance with knockout 3.1.0
The performance issue is present with the knockout 3.2 version as well.
Want to know how to decrease the load time with nested foreach and/or template bindings.
In line with what Hans outlined, if you are really looking to squeeze the most performance out of the client-side. A custom binding working directly with the collection, building HTML as a string and injecting it using something like element.innerHTML.
A simple example below:
ko.bindingHandlers.innerHtml = {
init: function (element, valueAccessor) {
var lst = ko.unwrap(valueAccessor());
if (lst) {
var html = '';
for (var i = 0; i < lst.length; i++) {
html += '<li>' + lst[i] + '</li>';
}
if (html)
element.innerHTML = html;
}
}
};
var vm = function(){
var self = this;
self.lst = ko.observableArray();
for (var i = 0; i < 100; i++) {
var ary = [];
for (var j = 0; j < 1000; j++)
{
ary.push(j)
}
self.lst.push({ num: i, numAry: ary});
}
};
ko.applyBindings(new vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: $data.lst">
<li>
<span data-bind="text: num"></span>
<ul data-bind="innerHtml: numAry"></ul>
</li>
</ul>