DataFrames.jl 1.6 for JuliaCon 2023

By: Blog by Bogumił Kamiński

Re-posted from: https://bkamins.github.io/julialang/2023/07/21/df16.html

Introduction

Next week the whole Julia community is invited to attend the
JuliaCon 2023 conference.

As each year wanted to have a fresh DataFrames.jl release for
this event so a few days ago DataFrames.jl 1.6 was registered.

Today I want to briefly highlight selected features of this release
that are, in my opinion, going to be useful most often in users’ workflows.
If you are interested in a full list of changes please check NEWS.md.

The post was written under Julia 1.9.2 and DataFrames.jl 1.6.0.

More flexible negation selector

The Not selector is one of the little things that make users’ life a lot easier.

Here is a basic example of how it is used:

julia> using DataFrames

julia> df = DataFrame(a1=1, a2=2, a3=3, b1=4, b2=5, b3=6)
1×6 DataFrame
 Row │ a1     a2     a3     b1     b2     b3
     │ Int64  Int64  Int64  Int64  Int64  Int64
─────┼──────────────────────────────────────────
   1 │     1      2      3      4      5      6

julia> df[:, Not(:a2)]
1×5 DataFrame
 Row │ a1     a3     b1     b2     b3
     │ Int64  Int64  Int64  Int64  Int64
─────┼───────────────────────────────────
   1 │     1      3      4      5      6

julia> df[:, Not([:a2, :b1])]
1×4 DataFrame
 Row │ a1     a3     b2     b3
     │ Int64  Int64  Int64  Int64
─────┼────────────────────────────
   1 │     1      3      5      6

In the last example you might think that adding [...] brackets is redundant.
With DataFrames.jl release 1.6 it is not needed anymore:

julia> df[:, Not(:a2, :b1)]
1×4 DataFrame
 Row │ a1     a3     b2     b3
     │ Int64  Int64  Int64  Int64
─────┼────────────────────────────
   1 │     1      3      5      6

This flexibility extends to arbitrary column selectors:

julia> df[:, Not(r"a", r"2")]
1×2 DataFrame
 Row │ b1     b3
     │ Int64  Int64
─────┼──────────────
   1 │     4      6

In the example we dropped columns that contained substrings "a" or "2".

Allow column renaming when constructing a data frame

Often we have source data that has some predefined column names.
For example consider the following named tuple of vectors:

julia> nt = (a=1:2, b=3:4, c=5:6)
(a = 1:2, b = 3:4, c = 5:6)

We can easily create a DataFrame from it:

julia> DataFrame(nt)
2×3 DataFrame
 Row │ a      b      c
     │ Int64  Int64  Int64
─────┼─────────────────────
   1 │     1      3      5
   2 │     2      4      6

Note that column names in the created data frame were inherited from
the source table. In the past, if we wanted different column names we
would need to rename them:

julia> rename!(DataFrame(nt), ["x", "y", "z"])
2×3 DataFrame
 Row │ x      y      z
     │ Int64  Int64  Int64
─────┼─────────────────────
   1 │     1      3      5
   2 │     2      4      6

Since DataFrames.jl release 1.6 you can do data frame creation and column renaming in one step:

julia> DataFrame(nt, ["x", "y", "z"])
2×3 DataFrame
 Row │ x      y      z
     │ Int64  Int64  Int64
─────┼─────────────────────
   1 │     1      3      5
   2 │     2      4      6

In the example – the first argument is the source table, and the second argument are target column names.

Conclusions

I hope all users will enjoy the new release of DataFrames.jl.

If you plan to attend JuliaCon 2023 let me highlight several events
that will happen during the conference in which I am involved:

  • on Tuesday, July 25 there will be “Working with DataFrames.jl beyond CSV files” workshop.
  • on Wednesday, July 26, I plan to be present at the “book authors’ booth” in the conference
    where I can answer any questions you have regarding my “Julia for Data Analysis” book.
    I will have several print and electronic versions of the book to share with interested
    people.
  • on Thursday, July 27, There is a “Tools and techniques of working with tabular data” minisymposium.
    I will give a talk about the development status of DataFrames.jl.
  • on Friday, July 28, the “Statistics symposium” minisymposium; I will give a talk on doing
    basic econometrics with GLM.jl. Next the “Future of JuliaData ecosystem” Birds of Feather meetup will take place.

DataFrames.jl 1.6 for JuliaCon 2023

By: Blog by Bogumił Kamiński

Re-posted from: https://bkamins.github.io/julialang/2023/07/21/graphpuzzle.html

Introduction

Next week the whole Julia community is invited to attend the
JuliaCon 2023 conference.

As each year wanted to have a fresh DataFrames.jl release for
this event so a few days ago DataFrames.jl 1.6 was registered.

Today I want to briefly highlight selected features of this release
that are, in my opinion, going to be useful most often in users’ workflows.
If you are interested in a full list of changes please check NEWS.md.

The post was written under Julia 1.9.2 and DataFrames.jl 1.6.0.

More flexible negation selector

The Not selector is one of the little things that make users’ life a lot easier.

Here is a basic example of how it is used:

julia> using DataFrames

julia> df = DataFrame(a1=1, a2=2, a3=3, b1=4, b2=5, b3=6)
1×6 DataFrame
 Row │ a1     a2     a3     b1     b2     b3
     │ Int64  Int64  Int64  Int64  Int64  Int64
─────┼──────────────────────────────────────────
   1 │     1      2      3      4      5      6

julia> df[:, Not(:a2)]
1×5 DataFrame
 Row │ a1     a3     b1     b2     b3
     │ Int64  Int64  Int64  Int64  Int64
─────┼───────────────────────────────────
   1 │     1      3      4      5      6

julia> df[:, Not([:a2, :b1])]
1×4 DataFrame
 Row │ a1     a3     b2     b3
     │ Int64  Int64  Int64  Int64
─────┼────────────────────────────
   1 │     1      3      5      6

In the last example you might think that adding [...] brackets is redundant.
With DataFrames.jl release 1.6 it is not needed anymore:

julia> df[:, Not(:a2, :b1)]
1×4 DataFrame
 Row │ a1     a3     b2     b3
     │ Int64  Int64  Int64  Int64
─────┼────────────────────────────
   1 │     1      3      5      6

This flexibility extends to arbitrary column selectors:

julia> df[:, Not(r"a", r"2")]
1×2 DataFrame
 Row │ b1     b3
     │ Int64  Int64
─────┼──────────────
   1 │     4      6

In the example we dropped columns that contained substrings "a" or "2".

Allow column renaming when constructing a data frame

Often we have source data that has some predefined column names.
For example consider the following named tuple of vectors:

julia> nt = (a=1:2, b=3:4, c=5:6)
(a = 1:2, b = 3:4, c = 5:6)

We can easily create a DataFrame from it:

julia> DataFrame(nt)
2×3 DataFrame
 Row │ a      b      c
     │ Int64  Int64  Int64
─────┼─────────────────────
   1 │     1      3      5
   2 │     2      4      6

Note that column names in the created data frame were inherited from
the source table. In the past, if we wanted different column names we
would need to rename them:

julia> rename!(DataFrame(nt), ["x", "y", "z"])
2×3 DataFrame
 Row │ x      y      z
     │ Int64  Int64  Int64
─────┼─────────────────────
   1 │     1      3      5
   2 │     2      4      6

Since DataFrames.jl release 1.6 you can do data frame creation and column renaming in one step:

julia> DataFrame(nt, ["x", "y", "z"])
2×3 DataFrame
 Row │ x      y      z
     │ Int64  Int64  Int64
─────┼─────────────────────
   1 │     1      3      5
   2 │     2      4      6

In the example – the first argument is the source table, and the second argument are target column names.

Conclusions

I hope all users will enjoy the new release of DataFrames.jl.

If you plan to attend JuliaCon 2023 let me highlight several events
that will happen during the conference in which I am involved:

  • on Tuesday, July 25 there will be “Working with DataFrames.jl beyond CSV files” workshop.
  • on Wednesday, July 26, I plan to be present at the “book authors’ booth” in the conference
    where I can answer any questions you have regarding my “Julia for Data Analysis” book.
    I will have several print and electronic versions of the book to share with interested
    people.
  • on Thursday, July 27, There is a “Tools and techniques of working with tabular data” minisymposium.
    I will give a talk about the development status of DataFrames.jl.
  • on Friday, July 28, the “Statistics symposium” minisymposium; I will give a talk on doing
    basic econometrics with GLM.jl. Next the “Future of JuliaData ecosystem” Birds of Feather meetup will take place.

Profiling oneAPI.jl applications with VTune

By: Tim Besard

Re-posted from: https://juliagpu.org/post/2023-07-19-oneapi_profiling/index.html

Profiling GPU applications is hard, so this post shows how to use Intel's VTune Profiler to profile GPU applications written in Julia with oneAPI.jl.

Because of the asynchronous nature of GPU execution, profiling GPU applications with Julia's tried and tested tools like @profile or even @time can be misleading: They will only show the time spent on the CPU, and will likely report that your application is spending most of its time waiting for the GPU.

To get a better understanding of what is happening on the GPU, we need specialized tools. In this post, we'll show how to use Intel's VTune Profiler to profile GPU applications written in Julia using oneAPI.jl.

Set-up

Start by downloading and installing the Intel VTune Profiler. This does not require administrative permissions, and will install in your home folder under the intel directory. On Linux, binaries will appear in ~/intel/oneapi/vtune/latest/bin64. There are three that are particularly important:

  • vtune: a command-line tool to profile applications;

  • vtune-gui: a graphical user interface to profile applications, or to visualize the results of a command-line profiling session;

  • vtune-backend: a daemon that creates a web interface for VTune, which you can use to profile applications both locally and remotely.

Hello VTune!

Let's start with a simple example: A Julia program that computes the sum of two arrays (i.e., the vadd example from the oneAPI repository):

using oneAPIfunction kernel(a, b, c)
    i = get_global_id()
    @inbounds c[i] = a[i] + b[i]
    return
endfunction vadd(a, b)
    d_a = oneArray(a)
    d_b = oneArray(b)
    d_c = similar(d_a)    @oneapi items=size(d_c) kernel(d_a, d_b, d_c)
    Array(d_c)
endfunction main(N=256)
    a = round.(rand(Float32, N) * 100)
    b = round.(rand(Float32, N) * 100)
    c = vadd(a, b)
end
main()

We've tweaked this example to make it more suited for profiling: We've enclosed the main application in a function so that it gets compiled, and we've increased the array sizes to make the GPU work harder.

There are several ways to profile this application. We'll start by demonstrating the command-line interface:

$ vtune -collect gpu-offload julia vadd.jlvtune: Collection started.
vtune: Collection stopped.vtune: Using result path `/home/tim/Julia/pkg/oneAPI/r000gh'
    GPU Time: 0.002s
EU Array Stalled/Idle: 100.0% of Elapsed time with GPU busy
 | The percentage of time when the EUs were stalled or idle is high, which has a
 | negative impact on compute-bound applications.
FPU Utilization: 0.0% of Elapsed time with GPU busy
...

This will run the application, and collect a number of GPU-related metrics. A summary is shown in the terminal, and a more detailed report will be written to a directory in the current working directory. You can open that report with the graphical user interface, possibly even on a different machine:

$ vtune-gui r000gh

Instrumenting the application

The trace we just collected includes the time spent compiling our application, making it difficult to analyze what is happening. To refine the trace, we can instrument our application with Intel's Instrumentation and Tracing Technology (ITT) APIs:

  • only start the profiler when we're running code of interest;

  • add markers to the trace to indicate what is happening.

We can interface with the ITT APIs using the IntelITT.jl package. Let's update our example:

using oneAPI, IntelITT# same as beforefunction main(N=256)
    a = round.(rand(Float32, N) * 100)
    b = round.(rand(Float32, N) * 100)
    c = IntelITT.@task "vadd" oneAPI.@sync vadd(a, b)
end# warm-up
main()# actual profile
IntelITT.@collect main()

Here, the IntelITT.@collect macro will start and stop the collection, so we should launch VTune with the -start-paused option:

$ vtune -collect gpu-offload -start-paused julia vadd.jl

In the GUI, we can now clearly see a nicely packed stream of API calls, grouped under the vadd task we added. Note that because API calls are asynchronous, i.e. they return immediately before the GPU has executed them, I grouped them under a oneAPI.@sync call so that the task not only captures the time spent on the CPU, but also the time spent on the GPU. This may not be wanted for your application.

VTune timeline

Kernel details

The timeline view is great for getting an application-level overview of what is happening, but once you've isolated a kernel that doesn't perform as expected, you may want to switch from the GPU Offload to the GPU Compute Hotspots analysis. Here, you get a more detailed view of what's happening during execution on the GPU, including the memory bandwidth and execution properties:

$ vtune -collect gpu-hotspots -start-paused julia vadd.jl

VTune timeline

Many of these analysis can be configured to collect more or less data, at the cost of more or less overhead.

Working remotely

In many cases, your local system will not have a GPU, and you will want to profile an application running on a remote system. As shown above, you can use the vtune CLI to create a trace and open that locally using vtune-gui, however there is an easier way: The vtune-backend daemon.

Start by launching the VTune back-end on the remote system:

$ vtune-backend --enable-server-profiling --web-port 8443 --log-to-console

If your remote system is directly reachable, you want to add --allow-remote-access --base-url "https://remoteServer:8443". However, most people will need to set-up an SSH tunnel:

$ ssh -L 8443:localhost:8443 remoteServer

You can now access the VTune GUI at https://localhost:8443/. Note that the first time you connect, you will need to do so using the one-time URL that is shown in the terminal where you launched the vtune-backend daemon.

The web interface that vtune-backend provides is identical to the GUI from vtune-gui: Start by creating a new project, and configuring an analysis: Select the local VTune profile server, enter the path to the Julia executable along with arguments and a working directory, and select the GPU Offload analysis type:

VTune WebUI

To start the analysis, click the big blue play button. If you use IntelITT.@collect to restrict the trace to the code of interest, use the second button with the pause symbol.

Give it a try!

Hopefully, this guide has shed some light on how to accurately profile oneAPI.jl applications using Intel's VTune Profiler. It turns out that one package could significantly benefit from some rigorous profiling: oneAPI.jl! Until now, development has focussed on correctness and usability, leaving considerable room for performance enhancements.

If you have access to an Intel GPU and want to gain experience profiling GPU applications with VTune, we encourage you to get involved! A good starting point would be analyzing some of oneAPI.jl's array operations like mapreduce or broadcast to identify potential bottlenecks. For more information or any queries, feel free to open an issue on GitHub, or join the discussion on Slack or Discourse. Your help could make a significant difference!