It’s because you’re binding to a function expression here:
<td>{{calcRowTotal($index, row)}}</td>
What that does it force that function to be reevaluated on every item, on every digest. What you’ll want to do to prevent that is pre-calculate that value and put it in your array to begin with.
One way to do that is to set up a watch on your array:
$scope.$watch('timesheetRows', function(rows) {
for(var i = 0; i < value.length; i++) {
var row = rows[i];
row.rowTotal = $scope.calcRowTotal(row, i);
}
}, true);
Then all you have to do is bind to that new value:
<td>{{row.rowTotal}}</td>