Refactoring variable assignment thru if let block with Enum - rust

I'm working with the code below, which works, but is clearly not a very clever or efficient way to write a value to res.
let mut res = "";
if let Video(n) = res_info { // res_info represents reference to &Settings type
if n.pixel_width > 1920{
res = "2160p";
}
else{
res = "1080p";
}
}
Printing res_info would yield the following:
Video(Video { pixel_width: 1920, pixel_height: 1080})
The following code seems to be close, however it's not assigning &str to res. I would much prefer a codeblock like this, in which res is only declared once.
let res = if let Video(n) = res_info {
if n.pixel_width > 1920 {
"2160p";
}
else{
"1080p";
}
};

As per the unit documentation
The semicolon ; can be used to discard the result of an expression at the end of a block, making the expression (and thus the block) evaluate to ()
Removing the semicolon should stop value from being discarded so the &str is resolved from the if blocks.
let res = if let Video(n) = res_info {
if n.pixel_width > 1920{
"2160p"
} else{
"1080p"
}
}else{
panic!("res_info is not a Video")
};
or with a match statement might be cleaner
let res = match res_info {
Video(n) if n.pixel_width > 1920 => "2160p",
Video(n) => "1080p",
_ => panic!("res_info is not a Video")
};

Related

Why is one of my variables returning undefined in a switch and case?

I'm trying to create a currency converter from USD to whatever other currency. I have simplified the code to the part I am having issues with.
const toCurrency = document.getElementById("toCurrency");
const returnValue = document.getElementById("returnValue");
let usdInput = document.getElementById("inputValue");
let submit = {
conversionSubmit: () => {
conversion.conversion()
}
}
let conversion = {
conversion: () => {
switch(toCurrency.value) {
case USD: returnValue.innerText = usdInput.value;
return usdInput.value;
}
}
};
The issue here is that my usdInput.value returns undefined when I run the function even though when I see what the variable is through the console, it is perfectly defined.

Why is the first function call is executed two times faster than all other sequential calls?

I have a custom JS iterator implementation and code for measuring performance of the latter implementation:
const ITERATION_END = Symbol('ITERATION_END');
const arrayIterator = (array) => {
let index = 0;
return {
hasValue: true,
next() {
if (index >= array.length) {
this.hasValue = false;
return ITERATION_END;
}
return array[index++];
},
};
};
const customIterator = (valueGetter) => {
return {
hasValue: true,
next() {
const nextValue = valueGetter();
if (nextValue === ITERATION_END) {
this.hasValue = false;
return ITERATION_END;
}
return nextValue;
},
};
};
const map = (iterator, selector) => customIterator(() => {
const value = iterator.next();
return value === ITERATION_END ? value : selector(value);
});
const filter = (iterator, predicate) => customIterator(() => {
if (!iterator.hasValue) {
return ITERATION_END;
}
let currentValue = iterator.next();
while (iterator.hasValue && currentValue !== ITERATION_END && !predicate(currentValue)) {
currentValue = iterator.next();
}
return currentValue;
});
const toArray = (iterator) => {
const array = [];
while (iterator.hasValue) {
const value = iterator.next();
if (value !== ITERATION_END) {
array.push(value);
}
}
return array;
};
const test = (fn, iterations) => {
const times = [];
for (let i = 0; i < iterations; i++) {
const start = performance.now();
fn();
times.push(performance.now() - start);
}
console.log(times);
console.log(times.reduce((sum, x) => sum + x, 0) / times.length);
}
const createData = () => Array.from({ length: 9000000 }, (_, i) => i + 1);
const testIterator = (data) => () => toArray(map(filter(arrayIterator(data), x => x % 2 === 0), x => x * 2))
test(testIterator(createData()), 10);
The output of the test function is very weird and unexpected - the first test run is constantly executed two times faster than all the other runs. One of the results, where the array contains all execution times and the number is the mean (I ran it on Node):
[
147.9088459983468,
396.3472499996424,
374.82447600364685,
367.74555300176144,
363.6300039961934,
362.44370299577713,
363.8418449983001,
390.86111199855804,
360.23125199973583,
358.4788999930024
]
348.6312940984964
Similar results can be observed using Deno runtime, however I could not reproduce this behaviour on other JS engines. What can be the reason behind it on the V8?
Environment:
Node v13.8.0, V8 v7.9.317.25-node.28,
Deno v1.3.3, V8 v8.6.334
(V8 developer here.) In short: it's inlining, or lack thereof, as decided by engine heuristics.
For an optimizing compiler, inlining a called function can have significant benefits (e.g.: avoids the call overhead, sometimes makes constant folding possible, or elimination of duplicate computations, sometimes even creates new opportunities for additional inlining), but comes at a cost: it makes the compilation itself slower, and it increases the risk of having to throw away the optimized code ("deoptimize") later due to some assumption that turns out not to hold. Inlining nothing would waste performance, inlining everything would waste performance, inlining exactly the right functions would require being able to predict the future behavior of the program, which is obviously impossible. So compilers use heuristics.
V8's optimizing compiler currently has a heuristic to inline functions only if it was always the same function that was called at a particular place. In this case, that's the case for the first iterations. Subsequent iterations then create new closures as callbacks, which from V8's point of view are new functions, so they don't get inlined. (V8 actually knows some advanced tricks that allow it to de-duplicate function instances coming from the same source in some cases and inline them anyway; but in this case those are not applicable [I'm not sure why]).
So in the first iteration, everything (including x => x % 2 === 0 and x => x * 2) gets inlined into toArray. From the second iteration onwards, that's no longer the case, and instead the generated code performs actual function calls.
That's probably fine; I would guess that in most real applications, the difference is barely measurable. (Reduced test cases tend to make such differences stand out more; but changing the design of a larger app based on observations made on a small test is often not the most impactful way to spend your time, and at worst can make things worse.)
Also, hand-optimizing code for engines/compilers is a difficult balance. I would generally recommend not to do that (because engines improve over time, and it really is their job to make your code fast); on the other hand, there clearly is more efficient code and less efficient code, and for maximum overall efficiency, everyone involved needs to do their part, i.e. you might as well make the engine's job simpler when you can.
If you do want to fine-tune performance of this, you can do so by separating code and data, thereby making sure that always the same functions get called. For example like this modified version of your code:
const ITERATION_END = Symbol('ITERATION_END');
class ArrayIterator {
constructor(array) {
this.array = array;
this.index = 0;
}
next() {
if (this.index >= this.array.length) return ITERATION_END;
return this.array[this.index++];
}
}
function arrayIterator(array) {
return new ArrayIterator(array);
}
class MapIterator {
constructor(source, modifier) {
this.source = source;
this.modifier = modifier;
}
next() {
const value = this.source.next();
return value === ITERATION_END ? value : this.modifier(value);
}
}
function map(iterator, selector) {
return new MapIterator(iterator, selector);
}
class FilterIterator {
constructor(source, predicate) {
this.source = source;
this.predicate = predicate;
}
next() {
let value = this.source.next();
while (value !== ITERATION_END && !this.predicate(value)) {
value = this.source.next();
}
return value;
}
}
function filter(iterator, predicate) {
return new FilterIterator(iterator, predicate);
}
function toArray(iterator) {
const array = [];
let value;
while ((value = iterator.next()) !== ITERATION_END) {
array.push(value);
}
return array;
}
function test(fn, iterations) {
for (let i = 0; i < iterations; i++) {
const start = performance.now();
fn();
console.log(performance.now() - start);
}
}
function createData() {
return Array.from({ length: 9000000 }, (_, i) => i + 1);
};
function even(x) { return x % 2 === 0; }
function double(x) { return x * 2; }
function testIterator(data) {
return function main() {
return toArray(map(filter(arrayIterator(data), even), double));
};
}
test(testIterator(createData()), 10);
Observe how there are no more dynamically created functions on the hot path, and the "public interface" (i.e. the way arrayIterator, map, filter, and toArray compose) is exactly the same as before, only under-the-hood details have changed. A benefit of giving all functions names is that you get more useful profiling output ;-)
Astute readers will notice that this modification only shifts the issue away: if you have several places in your code that call map and filter with different modifiers/predicates, then the inlineability issue will come up again. As I said above: microbenchmarks tend to be misleading, as real apps typically have different behavior...
(FWIW, this is pretty much the same effect as at Why is the execution time of this function call changing? .)
Just to add to this investigation, I compared the OP's original code with the predicate and selector functions declared as separate functions as suggested by jmrk to two other implementations. So, this code has three implementations:
OP's code with predicate and selector functions declared separately as named functions (not inline).
Using standard array.map() and .filter() (which you would think would be slower because of the extra creation of intermediate arrays)
Using a custom iteration that does both filtering and mapping in one iteration
The OP's attempt at saving time and making things faster is actually the slowest (on average). The custom iteration is the fastest.
I guess the lesson here is that it's not necessarily intuitive how you make things faster with the optimizing compiler so if you're tuning performance, you have to measure against the "typical" way of doing things (which may benefit from the most optimizations).
Also, note that in the method #3, the first two iterations are the slowest and then it gets faster - the opposite effect from the original code. Go figure.
The results are here:
[
99.90320014953613,
253.79690098762512,
271.3091011047363,
247.94990015029907,
247.457200050354,
261.9487009048462,
252.95090007781982,
250.8520998954773,
270.42809987068176,
249.340900182724
]
240.59370033740998
[
222.14270091056824,
220.48679995536804,
224.24630093574524,
237.07260012626648,
218.47070002555847,
218.1493010520935,
221.50559997558594,
223.3587999343872,
231.1618001461029,
243.55419993400574
]
226.01488029956818
[
147.81360006332397,
144.57479882240295,
73.13350009918213,
79.41700005531311,
77.38950109481812,
78.40880012512207,
112.31539988517761,
80.87990117073059,
76.7899010181427,
79.79679894447327
]
95.05192012786866
The code is here:
const { performance } = require('perf_hooks');
const ITERATION_END = Symbol('ITERATION_END');
const arrayIterator = (array) => {
let index = 0;
return {
hasValue: true,
next() {
if (index >= array.length) {
this.hasValue = false;
return ITERATION_END;
}
return array[index++];
},
};
};
const customIterator = (valueGetter) => {
return {
hasValue: true,
next() {
const nextValue = valueGetter();
if (nextValue === ITERATION_END) {
this.hasValue = false;
return ITERATION_END;
}
return nextValue;
},
};
};
const map = (iterator, selector) => customIterator(() => {
const value = iterator.next();
return value === ITERATION_END ? value : selector(value);
});
const filter = (iterator, predicate) => customIterator(() => {
if (!iterator.hasValue) {
return ITERATION_END;
}
let currentValue = iterator.next();
while (iterator.hasValue && currentValue !== ITERATION_END && !predicate(currentValue)) {
currentValue = iterator.next();
}
return currentValue;
});
const toArray = (iterator) => {
const array = [];
while (iterator.hasValue) {
const value = iterator.next();
if (value !== ITERATION_END) {
array.push(value);
}
}
return array;
};
const test = (fn, iterations) => {
const times = [];
let result;
for (let i = 0; i < iterations; i++) {
const start = performance.now();
result = fn();
times.push(performance.now() - start);
}
console.log(times);
console.log(times.reduce((sum, x) => sum + x, 0) / times.length);
return result;
}
const createData = () => Array.from({ length: 9000000 }, (_, i) => i + 1);
const cache = createData();
const comp1 = x => x % 2 === 0;
const comp2 = x => x * 2;
const testIterator = (data) => () => toArray(map(filter(arrayIterator(data), comp1), comp2))
// regular array filter and map
const testIterator2 = (data) => () => data.filter(comp1).map(comp2);
// combine filter and map in same operation
const testIterator3 = (data) => () => {
let result = [];
for (let value of data) {
if (comp1(value)) {
result.push(comp2(value));
}
}
return result;
}
const a = test(testIterator(cache), 10);
const b = test(testIterator2(cache), 10);
const c = test(testIterator3(cache), 10);
function compareArrays(a1, a2) {
if (a1.length !== a2.length) return false;
for (let [i, val] of a1.entries()) {
if (a2[i] !== val) return false;
}
return true;
}
console.log(a.length);
console.log(compareArrays(a, b));
console.log(compareArrays(a, c));

Reducing match indentation for deeply nested properties

I need to refer to a value deep within a structure which includes an Option nested in a struct property, nested in a Result.
My current (working) solution is:
let raw = &packet[16..];
match PacketHeaders::from_ip_slice(raw) {
Err(_value) => {
/* ignore */
},
Ok(value) => {
match value.ip {
Some(Version4(header)) => {
let key = format!("{}.{}.{}.{},{}.{}.{}.{}",
header.source[0], header.source[1], header.source[2], header.source[3],
header.destination[0], header.destination[1], header.destination[2], header.destination[3],
);
let Count {packets, bytes} = counts.entry(key).or_insert(Count {packets: 0, bytes: 0});
*packets += 1;
*bytes += packet.len();
if p > 1000 { /* exit after 1000 packets */
for (key, value) in counts {
println!("{},{},{}", key, value.packets, value.bytes);
}
return ();
}
p += 1;
}
_ => {
/* ignore */
}
}
}
}
(The problem with my current code is the excessive nesting and the two matches.)
All I want is PacketHeaders::from_ip_slice(ip) >> Ok >> ip >> Some >> Version4.
How can I get this, or ignore a failure nicely (NOT crash/exit) for each captured packet?
Pattern matching nests, and the pattern for struct looks like struct literals with the addition that .. means "and match all the rest, which I don't care about", similar to how the _ pattern means "match anything". Meaning you can do
match PacketHeaders::from_ip_slice(raw) {
Ok(PacketHeaders { ip: Version4(header), .. }) => {
/* handle */
}
_ => {
/* ignore `Err(_)` and other `Ok(_)` */
},
}
and if you're going to ignore all but one case, you can use if let:
if let Ok(PacketHeaders { ip: Version4(header), .. }) = PacketHeaders::from_ip_slice(raw) {
/* handle */
}
Code ended up being:
let raw = &packet[16..];
if let Ok(PacketHeaders { ip: Some(Version4(header)), link: _, vlan: _, transport: _, payload: _}) = PacketHeaders::from_ip_slice(raw) {
let key = format!("{}.{}.{}.{},{}.{}.{}.{}",
header.source[0], header.source[1], header.source[2], header.source[3],
header.destination[0], header.destination[1], header.destination[2], header.destination[3],
);
let Count {packets, bytes} = counts.entry(key).or_insert(Count {packets: 0, bytes: 0});
*packets += 1;
*bytes += packet.len();
}

Deeply nested data objects in multidimensional object

I have a multidimensional object and using Vue, I am trying to make the inner object reactive.
My object looks like this:
data() {
return {
myObject: {}
}
}
And the filled data looks like this:
myObject: {
1: { // (client)
0: "X", // (index) : (value)
1: "Y"
},
2: {
0: "A",
2: "B"
}
}
If I try using:
let value = "X";
let client = 1;
let index = 1;
let obj = {};
obj[client][index] = value;
this.myObject = Object.assign({}, this.myObject, obj);
It throws an error:
TypeError: Cannot set property '0' of undefined
And if I try below, it overwrites the initial values as it is initially setting the object to {}
let obj = {};
obj[index] = value;
let parentObj = {};
parentObj[client] = obj;
this.myObject = Object.assign({}, this.myObject, parentObj);
What is the proper way of adding the values to the multidimensional object?
In javascript, dim2Thing[1][1] = ... expressions require dim2Thing[1] to exist. This is why you get the error you mentioned. So you can do two expressions, which should work fine:
dim2Thing[1] = dim2Thing[1] || {}
dim2Thing[1][1] = otherThing
For the last block, you mention that it "overwrites the initial values"
I think what's actually happening here is just that Object.assign is not recursive. It only merges top-level keys. So if parentObj has a key that over-laps with this.myObj, then sub-keys will be lost.
Object.assign({ a: { b: 2} }, { a: { c: 3 } }) // returns { a: { c: 3 } }
This is what I interpret your code as trying to do - though I am unfamiliar with vue.js at this time, so I cannot assure it will have the desired result to your webpage:
let value = "X";
let client = 1;
let index = 1;
const newObj = Object.assign({}, this.myObject);
// if you have lodash _.set is handy
newObj[client] = newObj[client] || {}; // whatever was there, or a new object
newObj[client][index] = value
this.myObject = newObj
Just use an array, thats reactive by design.
If you need to get elements from the array in your template or anywhere just add a find method
// temp
late
<div v-for="(value, idx) in myArray">{{find(obj => obj.id === idx)}}</div>
methods: {
find (searchFunction) {
return this.myArray.find(searchFunction)
}
}

Where is the Rust intrinsic called "transmute" actually implemented?

I'm trying to find the implementation for the Rust intrinsics, particularly the "transumte" intrinsic that takes one argument.
I've seen the following code in cast.rs, but as you can see, it merely delegates to some other implementation of transmute.
#[inline]
pub unsafe fn transmute<L, G>(thing: L) -> G {
intrinsics::transmute(thing)
}
Where is the actual implementation for intrinsics, especially the transmute intrinsic?
cast::transmute delegates to intrinsics::transmute. There is a module called intrinsics (now in libcore), it contains extern bindings to several functions, including transmute. As you can see from the module documentation, implementation of intrinsics is said to be located in librustc/middle/trans/foreign.rs.
However, as far as I can see, actual implementation of intrinsics is present in librustc/middle/trans/intrinsic.rs. You can search for transmute, and you will find an arm in a really big match statement which looks like this:
"transmute" => {
let (in_type, out_type) = (*substs.substs.tps.get(0),
*substs.substs.tps.get(1));
let llintype = type_of::type_of(ccx, in_type);
let llouttype = type_of::type_of(ccx, out_type);
let in_type_size = machine::llbitsize_of_real(ccx, llintype);
let out_type_size = machine::llbitsize_of_real(ccx, llouttype);
if in_type_size != out_type_size {
let sp = match ccx.tcx.map.get(ref_id.unwrap()) {
ast_map::NodeExpr(e) => e.span,
_ => fail!("transmute has non-expr arg"),
};
ccx.sess().span_fatal(sp,
format!("transmute called on types with different sizes: \
{intype} ({insize, plural, =1{# bit} other{# bits}}) to \
{outtype} ({outsize, plural, =1{# bit} other{# bits}})",
intype = ty_to_str(ccx.tcx(), in_type),
insize = in_type_size as uint,
outtype = ty_to_str(ccx.tcx(), out_type),
outsize = out_type_size as uint));
}
if !return_type_is_void(ccx, out_type) {
let llsrcval = get_param(decl, first_real_arg);
if type_is_immediate(ccx, in_type) {
match fcx.llretptr.get() {
Some(llretptr) => {
Store(bcx, llsrcval, PointerCast(bcx, llretptr, llintype.ptr_to()));
RetVoid(bcx);
}
None => match (llintype.kind(), llouttype.kind()) {
(Pointer, other) | (other, Pointer) if other != Pointer => {
let tmp = Alloca(bcx, llouttype, "");
Store(bcx, llsrcval, PointerCast(bcx, tmp, llintype.ptr_to()));
Ret(bcx, Load(bcx, tmp));
}
(Array, _) | (_, Array) | (Struct, _) | (_, Struct) => {
let tmp = Alloca(bcx, llouttype, "");
Store(bcx, llsrcval, PointerCast(bcx, tmp, llintype.ptr_to()));
Ret(bcx, Load(bcx, tmp));
}
_ => {
let llbitcast = BitCast(bcx, llsrcval, llouttype);
Ret(bcx, llbitcast)
}
}
}
} else if type_is_immediate(ccx, out_type) {
let llsrcptr = PointerCast(bcx, llsrcval, llouttype.ptr_to());
let ll_load = Load(bcx, llsrcptr);
Ret(bcx, ll_load);
} else {
// NB: Do not use a Load and Store here. This causes massive
// code bloat when `transmute` is used on large structural
// types.
let lldestptr = fcx.llretptr.get().unwrap();
let lldestptr = PointerCast(bcx, lldestptr, Type::i8p(ccx));
let llsrcptr = PointerCast(bcx, llsrcval, Type::i8p(ccx));
let llsize = llsize_of(ccx, llintype);
call_memcpy(bcx, lldestptr, llsrcptr, llsize, 1);
RetVoid(bcx);
};
} else {
RetVoid(bcx);
}
}
This seems to be code which generates code which will be inserted instead of a call to transmute. I'm no compiler expert, so please someone correct me if I'm wrong.

Resources