Notes/Clarifications on Assignment #2 ------------------------------------- * I'm sorry I didn't call this out explicitly, but your discussion questions for the readings should be turned in separately on Monday evening, like last week, so that we have time to read and collate them before lecture. I'll be more careful about stating this in the future, but please assume that's the case in general. * In Question 3b (writing computeMy*Part()), your goal should be to return a range that can be iterated over trivially at the callsite. For example, as a task, I should be able to write something like: const myPart = computeMyBlockPart(1..n, numTasks, myTaskID); for i in myPart do ... Rather than having to do any further manipulation of myPart after the call to compute my portion of the workspace. Moreover (and this should go without saying), in order to be a legal distribution, the union of all 'myPart's across all tasks should (a) cover all of the original 1..n indices and (b) not cover any of them redundantly. * (added 1/21) Question 3: In doing the cross-language performance comparison, it's most important to use the same array element type (32- or 64-bit ints). Purists could also make sure they're using the same array index types, but this is less important for the sake of the performance comparison, and therefore not necessary for this assignment's sake. Anyone who wants to use int(32) indices (for curiosity, or to be a purist in their comparisons) should read my post on the 'HW2: int32 to int 64' topic on the discussion board for tips on declaring ranges and arrays using 32-bit indices. To re-emphasize: this is not necessary for the assignment. Chapel Tips that may be helpful for Assignment #2 ------------------------------------------------- * Queries on ranges that may be useful for this assignment are: r.low -- return the low bound of a range r.high -- return the high bound of a range r.stride -- return the stride of range r.alignment -- return the alignment of a range * (added 1/21) Here are some helpful debugging tips, when wrestling with types: x.type -- returns the type of expression 'x' typeToString(t) -- returns a string representation of type 't' r.idxType -- returns the index type of a range 'r' a.domain.idxType -- returns the index type of array 'a' a.eltType -- returns the element type of array 'a' * Math functions in Chapel are made available by default and are listed in Section 32.1.1 of the spec (available in your installation as $CHPL_HOME/doc/chapelLanguageSpec.pdf, or on the web at: http://chapel.cray.com/spec/spec-0.92.pdf). Generally, most math library routines available in C are available in Chapel as well, though sometimes with simpler names due to our support for function overloading (e.g., sin() works with both real(32) and real(64) rather than requiring sin() and sinf()). General Chapel Tips ------------------- * As with any new language, I recommend compiling your code often after small edits rather than writing a lot of Chapel code at once and then trying to get it to compile. Usually, this approach will help you identify and address bumps (whether in your understanding or in the implementation itself) as you hit them rather than sifting through a bunch of code to try and determine where things went wrong. * In addition to the lecture slides and the (somewhat overwhelming) language spec noted above, other ways to learn more about the language are: - Ask questions on the discussion board (or on email if they're not likely to be of general interest to the class). - Browse the programs in the $CHPL_HOME/examples/ directory -- and particularly the $CHPL_HOME/examples/primers subdirectory which contains a number of programs designed to explain features of the language through a mix of comments and code. - Check the language quick reference sheet which is available in $CHPL_HOME/doc/quickReference.pdf or on the web at: http://chapel.cray.com/spec/spec-0.92.pdf * As mentioned in class, if you are a vim or emacs user, there are syntax coloring modes available. See $CHPL_HOME/etc/README for details. I'm not a vim user myself, but understand it to be the more stable/mature of the two. As an emacs user, the mode is imperfect, but much better than nothing. * In general, I find that programming in Chapel is often easiest if I rely on the type inference capabilities in the language and have the compiler infer the types of variables, constants, arguments, and return types rather than declaring them myself (because they're an easy thing to get wrong). So often when you're wrestling with the compiler over some type mismatch or specification, removing the type declarations can be a way to make progress. The main downsides in practice are that the code can be less bulletproof and self-documenting for others; but for these assignments, that's not likely to be an issue (compared to if you were writing a library to be employed). * Trying to fix the 'align' operator confusion from lecture one more time (with the caveat that there's no need to necessarily use align for this assignment): - Think of ranges as being characterized by the four values above (low, high, stride, alignment) - A range describes all indices i such that low <= i <= high and i is equivalent to alignment, modulo |stride| (the absolute value of stride). If the stride value is positive, these integers will be enumerated from the lowest to the highest; if negative, from the highest to the lowest. - The expression i..j defines the range (low=i, high=j, stride=1, alignment=0). - The align operator essentially replaces the range's current alignment with a new alignment value. I think a big part of the confusion here stemmed from the fact that 'align' doesn't restrict the integers described by a range down to a smaller subset, but changes up the membership significantly. That's true. - The 'r by s' operator effectively multiplies the stride by 's' and sets the alignment to the first value that the range enumerates. There are some funny cases you can create, typically by chaining together multiple 'by' operators with differing signs and poorly aligned bounds (and the spec defines these cases far more precisely than I can here), but in practice I don't think you'll run into these (particularly in this class). - examples: 1..9 by 2 == 1, 3, 5, 7, 9 1..10 by 2 == 1, 3, 5, 7, 9 1..9 by -2 == 9, 7, 5, 3, 1 1..10 by -2 == 10, 8, 6, 4, 2 1..10 by 2 align 0 == 2, 4, 6, 8, 10 (ditto align 2, align 4, align -2) 1..10 by 2 align 1 == 1, 3, 5, 7, 9 (ditto align 3, align 5, align -1) etc. Note that another way to explore this algebra rather than trying to digest the spec is to use a little helper function like the following and pass it various range expressions: proc writeRangeIndices(r) { for i in r do writeln(i); writeln(); } writeRangeIndices(1..9 by 2); writeRangeIndices(1..10 by 2); etc.